diff --git a/kinit-admin/src/api/vadmin/auth/user.ts b/kinit-admin/src/api/vadmin/auth/user.ts index ea67af9..3972a2d 100644 --- a/kinit-admin/src/api/vadmin/auth/user.ts +++ b/kinit-admin/src/api/vadmin/auth/user.ts @@ -51,3 +51,7 @@ export const postImportUserApi = (data: any): Promise => { export const postUsersInitPasswordSendSMSApi = (data: any): Promise => { return request.post({ url: `/vadmin/auth/users/init/password/send/sms/`, data }) } + +export const postUsersInitPasswordSendEmailApi = (data: any): Promise => { + return request.post({ url: `/vadmin/auth/users/init/password/send/email/`, data }) +} diff --git a/kinit-admin/src/hooks/web/useValidator.ts b/kinit-admin/src/hooks/web/useValidator.ts index a0d36c3..cda9b21 100644 --- a/kinit-admin/src/hooks/web/useValidator.ts +++ b/kinit-admin/src/hooks/web/useValidator.ts @@ -54,11 +54,21 @@ export const useValidator = () => { } } + const isEmail = (rule: any, val: any, callback: Callback) => { + // 判断是否为邮箱地址 + if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) { + callback() + } else { + callback(new Error('请填写正确的邮箱地址')) + } + } + return { required, lengthRange, notSpace, notSpecialCharacters, - isEqual + isEqual, + isEmail } } diff --git a/kinit-admin/src/views/vadmin/auth/user/components/Write.vue b/kinit-admin/src/views/vadmin/auth/user/components/Write.vue index 6d8695b..07f6b92 100644 --- a/kinit-admin/src/views/vadmin/auth/user/components/Write.vue +++ b/kinit-admin/src/views/vadmin/auth/user/components/Write.vue @@ -6,7 +6,7 @@ import { useValidator } from '@/hooks/web/useValidator' import { schema } from './user.data' import { getRoleOptionsApi } from '@/api/vadmin/auth/role' -const { required } = useValidator() +const { required, isEmail } = useValidator() const props = defineProps({ currentRow: { @@ -20,7 +20,8 @@ const rules = reactive({ is_active: [required()], is_staff: [required()], role_ids: [required()], - telephone: [required()] + telephone: [required()], + email: [required(), { validator: isEmail, trigger: 'blur' }] }) const { register, methods, elFormRef } = useForm({ diff --git a/kinit-admin/src/views/vadmin/auth/user/components/user.data.ts b/kinit-admin/src/views/vadmin/auth/user/components/user.data.ts index 7d29caf..ed9e508 100644 --- a/kinit-admin/src/views/vadmin/auth/user/components/user.data.ts +++ b/kinit-admin/src/views/vadmin/auth/user/components/user.data.ts @@ -27,6 +27,12 @@ export const columns = reactive([ show: true, disabled: true }, + { + field: 'email', + label: '邮箱', + show: true, + disabled: true + }, { field: 'gender', label: '性别', @@ -76,6 +82,19 @@ export const schema = reactive([ } } }, + { + field: 'nickname', + label: '用户昵称', + colProps: { + span: 12 + }, + component: 'Input', + componentProps: { + style: { + width: '100%' + } + } + }, { field: 'telephone', label: '手机号码', @@ -90,8 +109,8 @@ export const schema = reactive([ } }, { - field: 'nickname', - label: '用户昵称', + field: 'email', + label: '邮箱', colProps: { span: 12 }, diff --git a/kinit-admin/src/views/vadmin/auth/user/index.vue b/kinit-admin/src/views/vadmin/auth/user/index.vue index 60195a9..f6db192 100644 --- a/kinit-admin/src/views/vadmin/auth/user/index.vue +++ b/kinit-admin/src/views/vadmin/auth/user/index.vue @@ -20,7 +20,8 @@ import { columns, searchSchema } from './components/user.data' import { ref, unref, watch, nextTick } from 'vue' import Write from './components/Write.vue' import Import from './components/Import.vue' -import Password from './components/Password.vue' +import PasswordSendSMS from './components/PasswordSendSMS.vue' +import PasswordSendEmail from './components/PasswordSendEmail.vue' import { Dialog } from '@/components/Dialog' import { ElButton, @@ -180,7 +181,8 @@ const importList = () => { } const passwordDialogVisible = ref(false) -const passwordDialogTitle = ref('重置密码并发送短信') +let passwordDialogType = 'sms' +let passwordDialogTitle = ref('重置密码并发送短信') const selections = ref([] as any[]) // 批量发送密码至短信 @@ -189,6 +191,21 @@ const sendPasswordToSMS = async () => { selections.value = await getSelections() if (selections.value.length > 0) { passwordDialogVisible.value = true + passwordDialogType = 'sms' + passwordDialogTitle.value = '重置密码并发送短信' + } else { + return ElMessage.warning('请先选择数据') + } +} + +// 批量发送密码至邮件 +const sendPasswordToEmail = async () => { + const { getSelections } = methods + selections.value = await getSelections() + if (selections.value.length > 0) { + passwordDialogVisible.value = true + passwordDialogType = 'email' + passwordDialogTitle.value = '重置密码并发送邮件' } else { return ElMessage.warning('请先选择数据') } @@ -205,6 +222,8 @@ const handleCommand = (command: string) => { } else if (command === 'c') { sendPasswordToSMS() } else if (command === 'd') { + sendPasswordToEmail() + } else if (command === 'e') { delDatas(null, true) } } @@ -233,6 +252,9 @@ const handleCommand = (command: string) => { 重置密码通知短信 + + 重置密码通知邮件 + 批量删除 @@ -255,7 +277,10 @@ const handleCommand = (command: string) => { 重置密码通知短信 - 重置密码通知邮件 + 批量删除 @@ -343,10 +368,20 @@ const handleCommand = (command: string) => { - + + + diff --git a/kinit-admin/src/views/vadmin/system/settings/index.vue b/kinit-admin/src/views/vadmin/system/settings/index.vue index d90ae3b..12ace49 100644 --- a/kinit-admin/src/views/vadmin/system/settings/index.vue +++ b/kinit-admin/src/views/vadmin/system/settings/index.vue @@ -12,6 +12,7 @@ import Baidu from './baidu.vue' import Privacy from './privacy.vue' import Agreement from './agreement.vue' import WXClient from './wxServer.vue' +import Email from './email.vue' import { ContentWrap } from '@/components/ContentWrap' import { getSystemSettingsTabsApi } from '@/api/vadmin/system/settings' @@ -37,6 +38,7 @@ getList() + diff --git a/kinit-api/application/settings.py b/kinit-api/application/settings.py index 973cac4..f05ce3d 100644 --- a/kinit-api/application/settings.py +++ b/kinit-api/application/settings.py @@ -11,7 +11,7 @@ from fastapi.security import OAuth2PasswordBearer """ 系统版本 """ -VERSION = "1.7.1" +VERSION = "1.7.2" """安全警告: 不要在生产中打开调试运行!""" DEBUG = True diff --git a/kinit-api/apps/vadmin/auth/crud.py b/kinit-api/apps/vadmin/auth/crud.py index e4b68ab..801296c 100644 --- a/kinit-api/apps/vadmin/auth/crud.py +++ b/kinit-api/apps/vadmin/auth/crud.py @@ -21,6 +21,7 @@ from utils.file.aliyun_oss import AliyunOSS, BucketConf from utils.aliyun_sms import AliyunSMS from utils.excel.import_manage import ImportManage, FieldType from utils.excel.write_xlsx import WriteXlsx +from utils.send_email import EmailSender from .params import UserParams from utils.tools import test_password from . import models, schemas @@ -205,16 +206,16 @@ class UserDal(DalBase): "error_url": im.generate_error_url() } - async def init_password_send_sms(self, ids: List[int], rd: Redis): + async def init_password(self, ids: List[int]): """ - 初始化所选用户密码并发送通知短信 + 初始化所选用户密码 将用户密码改为系统默认密码,并将初始化密码状态改为false """ users = await self.get_datas(limit=0, id=("in", ids), v_return_objs=True) result = [] for user in users: # 重置密码 - data = {"id": user.id, "telephone": user.telephone, "name": user.name} + data = {"id": user.id, "telephone": user.telephone, "name": user.name, "email": user.email} password = user.telephone[5:12] if settings.DEFAULT_PASSWORD == "0" else settings.DEFAULT_PASSWORD user.password = self.model.get_password_hash(password) user.is_reset_password = False @@ -223,6 +224,14 @@ class UserDal(DalBase): data["password"] = password result.append(data) await self.db.flush() + return result + + async def init_password_send_sms(self, ids: List[int], rd: Redis): + """ + 初始化所选用户密码并发送通知短信 + 将用户密码改为系统默认密码,并将初始化密码状态改为false + """ + result = await self.init_password(ids) for user in result: if not user["reset_password_status"]: user["send_sms_status"] = False @@ -239,6 +248,35 @@ class UserDal(DalBase): user["send_sms_msg"] = e.msg return result + async def init_password_send_email(self, ids: List[int], rd: Redis): + """ + 初始化所选用户密码并发送通知邮件 + 将用户密码改为系统默认密码,并将初始化密码状态改为false + """ + result = await self.init_password(ids) + for user in result: + if not user["reset_password_status"]: + user["send_sms_status"] = False + user["send_sms_msg"] = "重置密码失败" + continue + password: str = user.pop("password") + email: str = user.get("email", None) + if email: + subject = "密码已重置" + body = f"您好,您的密码已经重置为{password},请及时登录并修改密码。" + es = EmailSender(rd) + try: + send_result = await es.send_email([email], subject, body) + user["send_sms_status"] = send_result + user["send_sms_msg"] = "" if send_result else "发送失败,请联系管理员" + except CustomException as e: + user["send_sms_status"] = False + user["send_sms_msg"] = e.msg + else: + user["send_sms_status"] = False + user["send_sms_msg"] = "未获取到邮箱地址" + return result + async def update_current_avatar(self, user: models.VadminUser, file: UploadFile): """ 更新当前用户头像 diff --git a/kinit-api/apps/vadmin/auth/models/user.py b/kinit-api/apps/vadmin/auth/models/user.py index 28034c4..9eebdd4 100644 --- a/kinit-api/apps/vadmin/auth/models/user.py +++ b/kinit-api/apps/vadmin/auth/models/user.py @@ -23,6 +23,7 @@ class VadminUser(BaseModel): avatar = Column(String(500), nullable=True, comment='头像') telephone = Column(String(11), nullable=False, index=True, comment="手机号", unique=False) + email = Column(String(50), nullable=True, comment="邮箱地址") name = Column(String(50), index=True, nullable=False, comment="姓名") nickname = Column(String(50), nullable=True, comment="昵称") password = Column(String(255), nullable=True, comment="密码") diff --git a/kinit-api/apps/vadmin/auth/params/user.py b/kinit-api/apps/vadmin/auth/params/user.py index 7481aa6..b622978 100644 --- a/kinit-api/apps/vadmin/auth/params/user.py +++ b/kinit-api/apps/vadmin/auth/params/user.py @@ -22,6 +22,7 @@ class UserParams(QueryParams): self, name: str = None, telephone: str = None, + email: str = None, is_active: bool | str = None, is_staff: bool | str = None, params: Paging = Depends() @@ -29,6 +30,7 @@ class UserParams(QueryParams): super().__init__(params) self.name = ("like", name) self.telephone = ("like", telephone) + self.email = ("like", email) self.is_active = is_active self.is_staff = is_staff diff --git a/kinit-api/apps/vadmin/auth/schemas/user.py b/kinit-api/apps/vadmin/auth/schemas/user.py index 07cc9c8..dd61d43 100644 --- a/kinit-api/apps/vadmin/auth/schemas/user.py +++ b/kinit-api/apps/vadmin/auth/schemas/user.py @@ -11,13 +11,14 @@ from typing import List, Optional from pydantic import BaseModel, root_validator -from core.data_types import Telephone, DatetimeStr +from core.data_types import Telephone, DatetimeStr, Email from .role import RoleSimpleOut class User(BaseModel): name: Optional[str] = None telephone: Telephone + email: Optional[Email] = None nickname: Optional[str] = None avatar: Optional[str] = None is_active: Optional[bool] = True @@ -40,6 +41,7 @@ class UserUpdateBaseInfo(BaseModel): """ name: str telephone: Telephone + email: Optional[Email] = None nickname: Optional[str] = None gender: Optional[str] = "0" @@ -50,6 +52,7 @@ class UserUpdate(User): """ name: Optional[str] = None telephone: Telephone + email: Optional[Email] = None nickname: Optional[str] = None avatar: Optional[str] = None is_active: Optional[bool] = True diff --git a/kinit-api/apps/vadmin/auth/views.py b/kinit-api/apps/vadmin/auth/views.py index ee4065f..f8dc020 100644 --- a/kinit-api/apps/vadmin/auth/views.py +++ b/kinit-api/apps/vadmin/auth/views.py @@ -119,6 +119,15 @@ async def post_users_init_password( return SuccessResponse(await crud.UserDal(auth.db).init_password_send_sms(ids.ids, request.app.state.redis)) +@app.post("/users/init/password/send/email/", summary="初始化所选用户密码并发送通知邮件") +async def post_users_init_password_send_email( + request: Request, + ids: IdList = Depends(), + auth: Auth = Depends(FullAdminAuth(permissions=["auth.user.reset"])) +): + return SuccessResponse(await crud.UserDal(auth.db).init_password_send_email(ids.ids, request.app.state.redis)) + + @app.put("/users/wx/server/openid/", summary="更新当前用户服务端微信平台openid") async def put_user_wx_server_openid(request: Request, code: str, auth: Auth = Depends(AllUserAuth())): result = await crud.UserDal(auth.db).update_wx_server_openid(code, auth.user, request.app.state.redis) diff --git a/kinit-api/core/crud.py b/kinit-api/core/crud.py index 77017c1..eb3ca76 100644 --- a/kinit-api/core/crud.py +++ b/kinit-api/core/crud.py @@ -19,14 +19,14 @@ # https://www.osgeo.cn/sqlalchemy/orm/loading_relationships.html?highlight=selectinload#sqlalchemy.orm.joinedload import datetime -from typing import List +from typing import List, Set from fastapi import HTTPException from fastapi.encoders import jsonable_encoder from sqlalchemy import func, delete, update, or_ from sqlalchemy.future import select from sqlalchemy.ext.asyncio import AsyncSession from starlette import status -from core.logger import logger +from core.exception import CustomException from sqlalchemy.sql.selectable import Select from typing import Any @@ -47,6 +47,7 @@ class DalBase: data_id: int = None, v_options: list = None, v_join_query: dict = None, + v_or: List[tuple] = None, v_order: str = None, v_return_none: bool = False, v_schema: Any = None, @@ -58,6 +59,7 @@ class DalBase: :param data_id: 数据 ID :param v_options: 指示应使用select在预加载中加载给定的属性。 :param v_join_query: 外键字段查询,内连接 + :param v_or: 或逻辑查询 :param v_order: 排序,默认正序,为 desc 是倒叙 :param v_return_none: 是否返回空 None,否认 抛出异常,默认抛出异常 :param v_schema: 指定使用的序列化对象 @@ -66,7 +68,7 @@ class DalBase: sql = select(self.model).where(self.model.is_delete == False) if data_id: sql = sql.where(self.model.id == data_id) - sql = self.add_filter_condition(sql, v_join_query, v_options, **kwargs) + sql = self.add_filter_condition(sql, v_options, v_join_query, v_or, **kwargs) if v_order and (v_order in self.ORDER_FIELD): sql = sql.order_by(self.model.create_datetime.desc()) queryset = await self.db.execute(sql) @@ -83,8 +85,9 @@ class DalBase: self, page: int = 1, limit: int = 10, - v_join_query: dict = None, v_options: list = None, + v_join_query: dict = None, + v_or: List[tuple] = None, v_order: str = None, v_order_field: str = None, v_return_objs: bool = False, @@ -96,18 +99,19 @@ class DalBase: 获取数据列表 :param page: 页码 :param limit: 当前页数据量 - :param v_join_query: 外键字段查询 :param v_options: 指示应使用select在预加载中加载给定的属性。 - :param v_schema: 指定使用的序列化对象 + :param v_join_query: 外键字段查询 + :param v_or: 或逻辑查询 :param v_order: 排序,默认正序,为 desc 是倒叙 :param v_order_field: 排序字段 :param v_return_objs: 是否返回对象 :param v_start_sql: 初始 sql + :param v_schema: 指定使用的序列化对象 :param kwargs: 查询参数 """ if not isinstance(v_start_sql, Select): v_start_sql = select(self.model).where(self.model.is_delete == False) - sql = self.add_filter_condition(v_start_sql, v_join_query, v_options, **kwargs) + sql = self.add_filter_condition(v_start_sql, v_options, v_join_query, v_or, **kwargs) if v_order_field and (v_order in self.ORDER_FIELD): sql = sql.order_by(getattr(self.model, v_order_field).desc(), self.model.id.desc()) elif v_order_field: @@ -121,15 +125,17 @@ class DalBase: return queryset.scalars().unique().all() return [await self.out_dict(i, v_schema=v_schema) for i in queryset.scalars().unique().all()] - async def get_count(self, v_join_query: dict = None, v_options: list = None, **kwargs): + async def get_count(self, v_options: list = None, v_join_query: dict = None, v_or: List[tuple] = None, **kwargs): """ 获取数据总数 - :param v_join_query: 外键字段查询 + :param v_options: 指示应使用select在预加载中加载给定的属性。 + :param v_join_query: 外键字段查询 + :param v_or: 或逻辑查询 :param kwargs: 查询参数 """ sql = select(func.count(self.model.id).label('total')).where(self.model.is_delete == False) - sql = self.add_filter_condition(sql, v_join_query, v_options, **kwargs) + sql = self.add_filter_condition(sql, v_options, v_join_query, v_or, **kwargs) queryset = await self.db.execute(sql) return queryset.one()['total'] @@ -189,72 +195,118 @@ class DalBase: else: await self.db.execute(delete(self.model).where(self.model.id.in_(ids))) - def add_filter_condition(self, sql: select, v_join_query: dict = None, v_options: list = None, **kwargs) -> select: + def add_filter_condition( + self, + sql: select, + v_options: list = None, + v_join_query: dict = None, + v_or: List[tuple] = None, + **kwargs + ) -> select: """ 添加过滤条件,以及内连接过滤条件 :param sql: - :param v_join_query: 外键字段查询,内连接 :param v_options: 指示应使用select在预加载中加载给定的属性。 + :param v_join_query: 外键字段查询,内连接 + :param v_or: 或逻辑 :param kwargs: 关键词参数 """ - if v_join_query and self.key_models: + v_join: Set[str] = set() + v_join_left: Set[str] = set() + if v_join_query: for key, value in v_join_query.items(): foreign_key = self.key_models.get(key) - if foreign_key and foreign_key.get("model"): - # 当外键模型在查询模型中存在多个外键时,则需要添加onclause属性 - sql = sql.join(foreign_key.get("model"), onclause=foreign_key.get("onclause", None)) - for v_key, v_value in value.items(): - if v_value is not None and v_value != "": - v_attr = getattr(foreign_key.get("model"), v_key, None) - sql = self.filter_condition(sql, v_attr, v_value) - else: - logger.error(f"外键查询报错:{key}模型不存在,无法进行下一步查询。") - elif v_join_query and not self.key_models: - logger.error(f"外键查询报错:key_models 外键模型无配置项,无法进行下一步查询。") - for field in kwargs: - value = kwargs.get(field) - if value is not None and value != "": - attr = getattr(self.model, field, None) - sql = self.filter_condition(sql, attr, value) + conditions = [] + self.__dict_filter(conditions, foreign_key.get("model"), **value) + if conditions: + sql = sql.where(*conditions) + v_join.add(key) + if v_or: + sql = self.__or_filter(sql, v_or, v_join_left, v_join) + for item in v_join: + foreign_key = self.key_models.get(item) + # 当外键模型在查询模型中存在多个外键时,则需要添加onclause属性 + sql = sql.join(foreign_key.get("model"), onclause=foreign_key.get("onclause")) + for item in v_join_left: + foreign_key = self.key_models.get(item) + # 当外键模型在查询模型中存在多个外键时,则需要添加onclause属性 + sql = sql.outerjoin(foreign_key.get("model"), onclause=foreign_key.get("onclause")) + conditions = [] + self.__dict_filter(conditions, self.model, **kwargs) + if conditions: + sql = sql.where(*conditions) if v_options: sql = sql.options(*[load for load in v_options]) return sql - @classmethod - def filter_condition(cls, sql: Any, attr: Any, value: Any): + def __or_filter(self, sql: select, v_or: List[tuple], v_join_left: Set[str], v_join: Set[str]): """ - 过滤条件 + 或逻辑操作 + :param sql: + :param v_or: 或逻辑 + :param v_join_left: 左连接 + :param v_join: 内连接 """ - if not attr: - return sql - if isinstance(value, tuple): - if len(value) == 1: - if value[0] == "None": - sql = sql.where(attr.is_(None)) - elif value[0] == "not None": - sql = sql.where(attr.isnot(None)) - elif len(value) == 2 and value[1] is not None: - if value[0] == "date": - # 根据日期查询, 关键函数是:func.time_format和func.date_format - sql = sql.where(func.date_format(attr, "%Y-%m-%d") == value[1]) - elif value[0] == "like": - sql = sql.where(attr.like(f"%{value[1]}%")) - elif value[0] == "or": - sql = sql.where(or_(i for i in value[1])) - elif value[0] == "in": - sql = sql.where(attr.in_(value[1])) - elif value[0] == "between" and len(value[1]) == 2: - sql = sql.where(attr.between(value[1][0], value[1][1])) - elif value[0] == "month": - sql = sql.where(func.date_format(attr, "%Y-%m") == value[1]) - elif value[0] == "!=": - sql = sql.where(attr != value[1]) - elif value[0] == ">": - sql = sql.where(attr > value[1]) - else: - sql = sql.where(attr == value) + or_list = [] + for item in v_or: + if len(item) == 2: + model = self.model + condition = {item[0]: item[1]} + self.__dict_filter(or_list, model, **condition) + elif len(item) == 4 and item[0] == "fk": + model = self.key_models.get(item[1]).get("model") + condition = {item[2]: item[3]} + conditions = [] + self.__dict_filter(conditions, model, **condition) + if conditions: + or_list = or_list + conditions + v_join_left.add(item[1]) + if item[1] in v_join: + v_join.remove(item[1]) + else: + raise CustomException(msg="v_or 获取查询属性失败,语法错误!") + if or_list: + sql = sql.where(or_(i for i in or_list)) return sql + def __dict_filter(self, conditions: list, model, **kwargs): + """ + 字典过滤 + :param model: + :param kwargs: + """ + for field, value in kwargs.items(): + if value is not None and value != "": + attr = getattr(model, field) + if isinstance(value, tuple): + if len(value) == 1: + if value[0] == "None": + conditions.append(attr.is_(None)) + elif value[0] == "not None": + conditions.append(attr.isnot(None)) + else: + raise CustomException("SQL查询语法错误") + elif len(value) == 2 and value[1] not in [None, [], ""]: + if value[0] == "date": + # 根据日期查询, 关键函数是:func.time_format和func.date_format + conditions.append(func.date_format(attr, "%Y-%m-%d") == value[1]) + elif value[0] == "like": + conditions.append(attr.like(f"%{value[1]}%")) + elif value[0] == "in": + conditions.append(attr.in_(value[1])) + elif value[0] == "between" and len(value[1]) == 2: + conditions.append(attr.between(value[1][0], value[1][1])) + elif value[0] == "month": + conditions.append(func.date_format(attr, "%Y-%m") == value[1]) + elif value[0] == "!=": + conditions.append(attr != value[1]) + elif value[0] == ">": + conditions.append(attr > value[1]) + else: + raise CustomException("SQL查询语法错误") + else: + conditions.append(attr == value) + async def flush(self, obj: Any = None): """ 刷新到数据库 diff --git a/kinit-api/core/data_types.py b/kinit-api/core/data_types.py index e67a0df..edd12e1 100644 --- a/kinit-api/core/data_types.py +++ b/kinit-api/core/data_types.py @@ -37,6 +37,17 @@ class Telephone(str): return vali_telephone(v) +class Email(str): + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + return vali_email(v) + + class DateStr(str): @classmethod diff --git a/kinit-api/core/exception.py b/kinit-api/core/exception.py index 72bfa05..773a6cf 100644 --- a/kinit-api/core/exception.py +++ b/kinit-api/core/exception.py @@ -16,7 +16,8 @@ from core.logger import logger class CustomException(Exception): - def __init__(self, msg: str, code: int, status_code: int = status.HTTP_200_OK): + + def __init__(self, msg: str, code: int = status.HTTP_400_BAD_REQUEST, status_code: int = status.HTTP_200_OK): self.msg = msg self.code = code self.status_code = status_code @@ -33,7 +34,7 @@ def register_exception(app: FastAPI): 自定义异常 """ return JSONResponse( - status_code=200, + status_code=exc.status_code, content={"message": exc.msg, "code": exc.code}, ) diff --git a/kinit-api/core/logger.py b/kinit-api/core/logger.py index bf0645c..4e0d9df 100644 --- a/kinit-api/core/logger.py +++ b/kinit-api/core/logger.py @@ -24,5 +24,6 @@ error = logger.add(log_path_error, rotation="00:00", retention="3 days", enqueue if __name__ == '__main__': print(BASE_DIR) - logger.info("hell") - logger.error("hell") + # logger.info("1") + retry: int = 1 + logger.error("未从Redis中获取到配置信息,正在重新更新配置信息,重试次数:{}。".format(retry)) diff --git a/kinit-api/core/validator.py b/kinit-api/core/validator.py index d04e35e..59c1f49 100644 --- a/kinit-api/core/validator.py +++ b/kinit-api/core/validator.py @@ -22,13 +22,30 @@ def vali_telephone(value: str) -> str: if not value or len(value) != 11 or not value.isdigit(): raise ValueError("请输入正确手机号") - REGEX_TELEPHONE = r'^1(3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8[0-9]|9[0-9])\d{8}$' + regex = r'^1(3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8[0-9]|9[0-9])\d{8}$' - if not re.match(REGEX_TELEPHONE, value): + if not re.match(regex, value): raise ValueError("请输入正确手机号") return value +def vali_email(value: str) -> str: + """ + 邮箱地址验证器 + :param value: 邮箱 + :return: 邮箱 + """ + if not value: + raise ValueError("请输入邮箱地址") + + regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' + + if not re.match(regex, value): + raise ValueError("请输入正确邮箱地址") + + return value + + diff --git a/kinit-api/scripts/initialize/data/init.xlsx b/kinit-api/scripts/initialize/data/init.xlsx index 0a95a96..db9cbca 100644 Binary files a/kinit-api/scripts/initialize/data/init.xlsx and b/kinit-api/scripts/initialize/data/init.xlsx differ diff --git a/kinit-api/utils/cache.py b/kinit-api/utils/cache.py index 5b4fec7..01a443f 100644 --- a/kinit-api/utils/cache.py +++ b/kinit-api/utils/cache.py @@ -42,13 +42,13 @@ class Cache: """ 获取系统配置 :params tab_name: 配置表标签名称 - :params retry: 重试次数 + :params retry_num: 重试次数 """ result = await self.rd.get(tab_name) if not result and retry > 0: - logger.error(f"未从Redis中获取到{tab_name}配置信息,正在重新更新配置信息,重试次数:{retry}") + logger.error(f"未从Redis中获取到{tab_name}配置信息,正在重新更新配置信息,重试次数:{retry}。") await self.cache_tab_names([tab_name]) - await self.get_tab_name(tab_name, retry - 1) + return await self.get_tab_name(tab_name, retry - 1) elif not result and retry == 0: raise CustomException(f"获取{tab_name}配置信息失败,请联系管理员!", code=status.HTTP_ERROR) else: diff --git a/kinit-api/utils/wx/oauth.py b/kinit-api/utils/wx/oauth.py index 19e309e..7f503c1 100644 --- a/kinit-api/utils/wx/oauth.py +++ b/kinit-api/utils/wx/oauth.py @@ -6,7 +6,6 @@ # @IDE : PyCharm # @desc : 简要说明 -import json import requests from core.logger import logger from utils.cache import Cache