|
3 | 3 | import os |
4 | 4 | import requests |
5 | 5 | import time |
6 | | -import warnings |
7 | 6 | from datetime import datetime |
8 | 7 | from fastapi import Request |
9 | 8 | from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse |
10 | 9 | from functools import lru_cache, wraps |
11 | | -from typing import Literal, Optional, Union |
| 10 | +from typing import Literal, Optional |
12 | 11 | from user_agents import parse |
13 | | -from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel |
14 | | -from module_admin.service.log_service import LoginLogService, OperationLogService |
15 | | -from module_admin.service.login_service import LoginService |
16 | 12 | from config.enums import BusinessType |
17 | 13 | from config.env import AppConfig |
18 | 14 | from exceptions.exception import LoginException, ServiceException, ServiceWarning |
| 15 | +from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel |
| 16 | +from module_admin.service.log_service import LoginLogService, OperationLogService |
| 17 | +from module_admin.service.login_service import LoginService |
19 | 18 | from utils.log_util import logger |
20 | 19 | from utils.response_util import ResponseUtil |
21 | 20 |
|
@@ -201,188 +200,6 @@ async def wrapper(*args, **kwargs): |
201 | 200 | return wrapper |
202 | 201 |
|
203 | 202 |
|
204 | | -def log_decorator( |
205 | | - title: str, |
206 | | - business_type: Union[Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], BusinessType], |
207 | | - log_type: Optional[Literal['login', 'operation']] = 'operation', |
208 | | -): |
209 | | - """ |
210 | | - 日志装饰器 |
211 | | -
|
212 | | - :param title: 当前日志装饰器装饰的模块标题 |
213 | | - :param business_type: 业务类型(0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据) |
214 | | - :param log_type: 日志类型(login表示登录日志,operation表示为操作日志) |
215 | | - :return: |
216 | | - """ |
217 | | - warnings.simplefilter('always', category=DeprecationWarning) |
218 | | - if isinstance(business_type, BusinessType): |
219 | | - business_type = business_type.value |
220 | | - warnings.warn( |
221 | | - '未来版本将会移除@log_decorator装饰器,请使用@Log装饰器', |
222 | | - category=DeprecationWarning, |
223 | | - stacklevel=2, |
224 | | - ) |
225 | | - |
226 | | - def decorator(func): |
227 | | - @wraps(func) |
228 | | - async def wrapper(*args, **kwargs): |
229 | | - start_time = time.time() |
230 | | - # 获取被装饰函数的文件路径 |
231 | | - file_path = inspect.getfile(func) |
232 | | - # 获取项目根路径 |
233 | | - project_root = os.getcwd() |
234 | | - # 处理文件路径,去除项目根路径部分 |
235 | | - relative_path = os.path.relpath(file_path, start=project_root)[0:-2].replace('\\', '.') |
236 | | - # 获取当前被装饰函数所在路径 |
237 | | - func_path = f'{relative_path}{func.__name__}()' |
238 | | - # 获取上下文信息 |
239 | | - request: Request = kwargs.get('request') |
240 | | - token = request.headers.get('Authorization') |
241 | | - query_db = kwargs.get('query_db') |
242 | | - request_method = request.method |
243 | | - operator_type = 0 |
244 | | - user_agent = request.headers.get('User-Agent') |
245 | | - if 'Windows' in user_agent or 'Macintosh' in user_agent or 'Linux' in user_agent: |
246 | | - operator_type = 1 |
247 | | - if 'Mobile' in user_agent or 'Android' in user_agent or 'iPhone' in user_agent: |
248 | | - operator_type = 2 |
249 | | - # 获取请求的url |
250 | | - oper_url = request.url.path |
251 | | - # 获取请求的ip及ip归属区域 |
252 | | - oper_ip = request.headers.get('X-Forwarded-For') |
253 | | - oper_location = '内网IP' |
254 | | - if AppConfig.app_ip_location_query: |
255 | | - oper_location = get_ip_location(oper_ip) |
256 | | - # 根据不同的请求类型使用不同的方法获取请求参数 |
257 | | - content_type = request.headers.get('Content-Type') |
258 | | - if content_type and ( |
259 | | - 'multipart/form-data' in content_type or 'application/x-www-form-urlencoded' in content_type |
260 | | - ): |
261 | | - payload = await request.form() |
262 | | - oper_param = '\n'.join([f'{key}: {value}' for key, value in payload.items()]) |
263 | | - else: |
264 | | - payload = await request.body() |
265 | | - # 通过 request.path_params 直接访问路径参数 |
266 | | - path_params = request.path_params |
267 | | - oper_param = {} |
268 | | - if payload: |
269 | | - oper_param.update(json.loads(str(payload, 'utf-8'))) |
270 | | - if path_params: |
271 | | - oper_param.update(path_params) |
272 | | - oper_param = json.dumps(oper_param, ensure_ascii=False) |
273 | | - # 日志表请求参数字段长度最大为2000,因此在此处判断长度 |
274 | | - if len(oper_param) > 2000: |
275 | | - oper_param = '请求参数过长' |
276 | | - |
277 | | - # 获取操作时间 |
278 | | - oper_time = datetime.now() |
279 | | - # 此处在登录之前向原始函数传递一些登录信息,用于监测在线用户的相关信息 |
280 | | - login_log = {} |
281 | | - if log_type == 'login': |
282 | | - user_agent_info = parse(user_agent) |
283 | | - browser = f'{user_agent_info.browser.family}' |
284 | | - system_os = f'{user_agent_info.os.family}' |
285 | | - if user_agent_info.browser.version != (): |
286 | | - browser += f' {user_agent_info.browser.version[0]}' |
287 | | - if user_agent_info.os.version != (): |
288 | | - system_os += f' {user_agent_info.os.version[0]}' |
289 | | - login_log = dict( |
290 | | - ipaddr=oper_ip, |
291 | | - loginLocation=oper_location, |
292 | | - browser=browser, |
293 | | - os=system_os, |
294 | | - loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'), |
295 | | - ) |
296 | | - kwargs['form_data'].login_info = login_log |
297 | | - try: |
298 | | - # 调用原始函数 |
299 | | - result = await func(*args, **kwargs) |
300 | | - except (LoginException, ServiceWarning) as e: |
301 | | - logger.warning(e.message) |
302 | | - result = ResponseUtil.failure(data=e.data, msg=e.message) |
303 | | - except ServiceException as e: |
304 | | - logger.error(e.message) |
305 | | - result = ResponseUtil.error(data=e.data, msg=e.message) |
306 | | - except Exception as e: |
307 | | - logger.exception(e) |
308 | | - result = ResponseUtil.error(msg=str(e)) |
309 | | - # 获取请求耗时 |
310 | | - cost_time = float(time.time() - start_time) * 100 |
311 | | - # 判断请求是否来自api文档 |
312 | | - request_from_swagger = ( |
313 | | - request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False |
314 | | - ) |
315 | | - request_from_redoc = ( |
316 | | - request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False |
317 | | - ) |
318 | | - # 根据响应结果的类型使用不同的方法获取响应结果参数 |
319 | | - if ( |
320 | | - isinstance(result, JSONResponse) |
321 | | - or isinstance(result, ORJSONResponse) |
322 | | - or isinstance(result, UJSONResponse) |
323 | | - ): |
324 | | - result_dict = json.loads(str(result.body, 'utf-8')) |
325 | | - else: |
326 | | - if request_from_swagger or request_from_redoc: |
327 | | - result_dict = {} |
328 | | - else: |
329 | | - if result.status_code == 200: |
330 | | - result_dict = {'code': result.status_code, 'message': '获取成功'} |
331 | | - else: |
332 | | - result_dict = {'code': result.status_code, 'message': '获取失败'} |
333 | | - json_result = json.dumps(result_dict, ensure_ascii=False) |
334 | | - # 根据响应结果获取响应状态及异常信息 |
335 | | - status = 1 |
336 | | - error_msg = '' |
337 | | - if result_dict.get('code') == 200: |
338 | | - status = 0 |
339 | | - else: |
340 | | - error_msg = result_dict.get('msg') |
341 | | - # 根据日志类型向对应的日志表插入数据 |
342 | | - if log_type == 'login': |
343 | | - # 登录请求来自于api文档时不记录登录日志,其余情况则记录 |
344 | | - if request_from_swagger or request_from_redoc: |
345 | | - pass |
346 | | - else: |
347 | | - user = kwargs.get('form_data') |
348 | | - user_name = user.username |
349 | | - login_log['loginTime'] = oper_time |
350 | | - login_log['userName'] = user_name |
351 | | - login_log['status'] = str(status) |
352 | | - login_log['msg'] = result_dict.get('msg') |
353 | | - |
354 | | - await LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log)) |
355 | | - else: |
356 | | - current_user = await LoginService.get_current_user(request, token, query_db) |
357 | | - oper_name = current_user.user.user_name |
358 | | - dept_name = current_user.user.dept.dept_name if current_user.user.dept else None |
359 | | - operation_log = OperLogModel( |
360 | | - title=title, |
361 | | - businessType=business_type, |
362 | | - method=func_path, |
363 | | - requestMethod=request_method, |
364 | | - operatorType=operator_type, |
365 | | - operName=oper_name, |
366 | | - deptName=dept_name, |
367 | | - operUrl=oper_url, |
368 | | - operIp=oper_ip, |
369 | | - operLocation=oper_location, |
370 | | - operParam=oper_param, |
371 | | - jsonResult=json_result, |
372 | | - status=status, |
373 | | - errorMsg=error_msg, |
374 | | - operTime=oper_time, |
375 | | - costTime=int(cost_time), |
376 | | - ) |
377 | | - await OperationLogService.add_operation_log_services(query_db, operation_log) |
378 | | - |
379 | | - return result |
380 | | - |
381 | | - return wrapper |
382 | | - |
383 | | - return decorator |
384 | | - |
385 | | - |
386 | 203 | @lru_cache() |
387 | 204 | def get_ip_location(oper_ip: str): |
388 | 205 | """ |
|
0 commit comments