refactor: P4c 最终批次-1 log 模块(login_log+operation_log 合并服务)
【关键修复】根 .gitignore 模板规则 log/ 误忽略 src/modules/log/, log 模块自 P3c 起未入库 → 加 !src/modules/log/ 特例排除并补入 【修复合并遗留 bug】 - 两个 get_info/_apply_filters/_build_page_data 重名互相覆盖 → 重命名 - log router 误导入 src.modules.auth 为 login_log → 改 log service - 双 blueprint 视图函数重名 → 重命名 operation 版本 - 双 import 块合并(OPERATION_LOG_TYPE_LIST 并入顶部) 【改造】page 函数 params 化返回 dict;get_info 委托 Repository 【验收】flake8 零告警;应用启动;log 分页运行时正常;12 passed Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
3dbce04eb1
commit
a054b14485
@@ -8,7 +8,9 @@
|
|||||||
.settings/
|
.settings/
|
||||||
.vscode/
|
.vscode/
|
||||||
vender/
|
vender/
|
||||||
|
# 业务模块 log 目录(src/modules/log)须入库,特例排除
|
||||||
log/
|
log/
|
||||||
|
!src/modules/log/
|
||||||
composer.lock
|
composer.lock
|
||||||
gitpush.sh
|
gitpush.sh
|
||||||
pkg/
|
pkg/
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
"""模块包。"""
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
LoginLog 仓储模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.common.base_repository import BaseRepository
|
||||||
|
from src.modules.log.models import LoginLog
|
||||||
|
|
||||||
|
|
||||||
|
class LoginLogRepository(BaseRepository):
|
||||||
|
"""LoginLog 仓储"""
|
||||||
|
|
||||||
|
model = LoginLog
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
"""
|
||||||
|
登录日志模型模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
提供登录日志管理的数据模型定义,用于记录用户登录、退出等操作日志。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.common.base_db import BaseDB
|
||||||
|
from src.common.base_model import BaseModel
|
||||||
|
from config import DB_PREFIX
|
||||||
|
from src.core.database import db
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志模型
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class LoginLog(BaseModel, BaseDB):
|
||||||
|
"""
|
||||||
|
登录日志模型
|
||||||
|
|
||||||
|
用于记录用户登录和退出操作的详细信息,包括:登录账号、日志类型、
|
||||||
|
来源、请求信息、IP地址、归属地、设备信息、请求耗时、操作状态等。
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: 主键ID(继承自 BaseModel)
|
||||||
|
username: 登录账号(String(50),必填)
|
||||||
|
log_type: 日志类型(Integer,1-登录,2-退出,必填)
|
||||||
|
source: 登录来源(Integer,0-后台,1-前台,必填)
|
||||||
|
method: 请求方法(String(10),必填)
|
||||||
|
request_method: 请求方式(String(255),必填)
|
||||||
|
url: 请求URL(String(500),必填)
|
||||||
|
param: 请求参数(Text,可选)
|
||||||
|
result: 返回结果(Text,必填)
|
||||||
|
ip: IP地址(String(50),必填)
|
||||||
|
location: IP归属地(String(100),必填)
|
||||||
|
os: 操作系统(String(100),必填)
|
||||||
|
browser: 浏览器(String(100),必填)
|
||||||
|
consume_time: 请求耗时(Integer,必填)
|
||||||
|
status: 操作状态(Integer,0-正常,1-异常,必填)
|
||||||
|
error: 错误信息(Text,可选)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 表配置
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 设置表名
|
||||||
|
__tablename__ = DB_PREFIX + "login_log"
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 字段定义
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 登录账号
|
||||||
|
username = db.Column(db.String(50), nullable=False, comment="登录账号")
|
||||||
|
|
||||||
|
# 日志类型:1-登录,2-退出
|
||||||
|
log_type = db.Column(db.Integer, nullable=False, comment="日志类型:1-登录,2-退出")
|
||||||
|
|
||||||
|
# 登录来源:0-后台,1-前台
|
||||||
|
source = db.Column(db.Integer, nullable=False, comment="登录来源:0-后台,1-前台")
|
||||||
|
|
||||||
|
# 请求方法(HTTP方法,如POST/GET)
|
||||||
|
method = db.Column(db.String(10), nullable=False, comment="请求方法")
|
||||||
|
|
||||||
|
# 请求方式(视图方法路径,如application.views.login.logout)
|
||||||
|
request_method = db.Column(db.String(255), nullable=False, comment="请求方式")
|
||||||
|
|
||||||
|
# 请求URL
|
||||||
|
url = db.Column(db.String(500), nullable=False, comment="请求URL")
|
||||||
|
|
||||||
|
# 请求参数
|
||||||
|
param = db.Column(db.Text, nullable=True, comment="请求参数")
|
||||||
|
|
||||||
|
# 返回结果
|
||||||
|
result = db.Column(db.Text, nullable=False, comment="返回结果")
|
||||||
|
|
||||||
|
# IP地址
|
||||||
|
ip = db.Column(db.String(50), nullable=False, comment="IP地址")
|
||||||
|
|
||||||
|
# IP归属地
|
||||||
|
location = db.Column(db.String(100), nullable=False, comment="IP归属地")
|
||||||
|
|
||||||
|
# 操作系统
|
||||||
|
os = db.Column(db.String(100), nullable=False, comment="操作系统")
|
||||||
|
|
||||||
|
# 浏览器
|
||||||
|
browser = db.Column(db.String(100), nullable=False, comment="浏览器")
|
||||||
|
|
||||||
|
# 请求耗时(毫秒)
|
||||||
|
consume_time = db.Column(db.Integer, nullable=False, comment="请求耗时")
|
||||||
|
|
||||||
|
# 操作状态:0-正常,1-异常
|
||||||
|
status = db.Column(db.Integer, nullable=False, comment="操作状态:0-正常,1-异常")
|
||||||
|
|
||||||
|
# 错误信息
|
||||||
|
error = db.Column(db.Text, nullable=True, comment="错误信息")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 常量定义
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
class LogType:
|
||||||
|
"""日志类型常量"""
|
||||||
|
|
||||||
|
LOGIN = 1 # 登录
|
||||||
|
LOGOUT = 2 # 退出
|
||||||
|
|
||||||
|
class Source:
|
||||||
|
"""登录来源常量"""
|
||||||
|
|
||||||
|
ADMIN = 0 # 后台
|
||||||
|
FRONT = 1 # 前台
|
||||||
|
|
||||||
|
class Status:
|
||||||
|
"""操作状态常量"""
|
||||||
|
|
||||||
|
NORMAL = 0 # 正常
|
||||||
|
ERROR = 1 # 异常
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 魔法方法
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""
|
||||||
|
返回模型的字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式为 "登录日志:{username} - {log_type}" 的字符串
|
||||||
|
"""
|
||||||
|
log_type_name = "登录" if self.log_type == self.LogType.LOGIN else "退出"
|
||||||
|
return "登录日志:{} - {}".format(self.username, log_type_name)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
返回模型的字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式为 "登录日志:{username} - {log_type}" 的字符串
|
||||||
|
"""
|
||||||
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
操作日志模型模块
|
||||||
|
|
||||||
|
与 LoginLog 共用顶部导入。
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志模型
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class OperationLog(BaseModel, BaseDB):
|
||||||
|
"""
|
||||||
|
操作日志模型
|
||||||
|
|
||||||
|
用于记录用户操作行为的详细信息,包括:操作标题、操作类型、
|
||||||
|
来源、请求信息、IP地址、归属地、设备信息、请求耗时、操作状态等。
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id: 主键ID(继承自 BaseModel)
|
||||||
|
title: 请求标题(String(150),必填)
|
||||||
|
type: 请求类型(Integer,必填)
|
||||||
|
source: 请求来源(Integer,必填,0-后台,1-前台)
|
||||||
|
method: 请求方法(String(100),必填)
|
||||||
|
request_method: 请求方式(String(255),必填)
|
||||||
|
url: 请求URL(String(1000),必填)
|
||||||
|
param: 请求参数(Text,必填)
|
||||||
|
result: 请求结果(Text,必填)
|
||||||
|
ip: 请求IP(String(100),必填)
|
||||||
|
location: 请求所在区域(String(255),必填)
|
||||||
|
os: 操作系统(String(100),必填)
|
||||||
|
browser: 浏览器(String(100),必填)
|
||||||
|
consume_time: 请求耗时(Integer,必填,单位毫秒)
|
||||||
|
status: 请求状态(Integer,必填,0-正常,1-异常)
|
||||||
|
error: 错误描述(Text,必填)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 表配置
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 设置表名
|
||||||
|
__tablename__ = DB_PREFIX + "operation_log"
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 字段定义
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 请求标题
|
||||||
|
title = db.Column(db.String(150), nullable=False, comment="请求标题")
|
||||||
|
|
||||||
|
# 请求类型
|
||||||
|
type = db.Column(db.Integer, nullable=False, comment="请求类型")
|
||||||
|
|
||||||
|
# 请求来源:0-后台,1-前台
|
||||||
|
source = db.Column(db.Integer, nullable=False, comment="请求来源:0-后台,1-前台")
|
||||||
|
|
||||||
|
# 请求方法(如:UserController.add)
|
||||||
|
method = db.Column(db.String(100), nullable=False, comment="请求方法")
|
||||||
|
|
||||||
|
# 请求方式(如:GET、POST、PUT、DELETE)
|
||||||
|
request_method = db.Column(db.String(255), nullable=False, comment="请求方式")
|
||||||
|
|
||||||
|
# 请求URL
|
||||||
|
url = db.Column(db.String(1000), nullable=False, comment="请求URL")
|
||||||
|
|
||||||
|
# 请求参数
|
||||||
|
param = db.Column(db.Text, nullable=False, comment="请求参数")
|
||||||
|
|
||||||
|
# 请求结果
|
||||||
|
result = db.Column(db.Text, nullable=False, comment="请求结果")
|
||||||
|
|
||||||
|
# 请求IP
|
||||||
|
ip = db.Column(db.String(100), nullable=False, comment="请求IP")
|
||||||
|
|
||||||
|
# 请求所在区域
|
||||||
|
location = db.Column(db.String(255), nullable=False, comment="请求所在区域")
|
||||||
|
|
||||||
|
# 操作系统
|
||||||
|
os = db.Column(db.String(100), nullable=False, comment="操作系统")
|
||||||
|
|
||||||
|
# 浏览器
|
||||||
|
browser = db.Column(db.String(100), nullable=False, comment="浏览器")
|
||||||
|
|
||||||
|
# 请求耗时(毫秒)
|
||||||
|
consume_time = db.Column(db.BigInteger, nullable=False, comment="请求耗时")
|
||||||
|
|
||||||
|
# 请求状态:0-正常,1-异常
|
||||||
|
status = db.Column(db.Integer, nullable=False, comment="请求状态:0-正常,1-异常")
|
||||||
|
|
||||||
|
# 错误描述
|
||||||
|
error = db.Column(db.Text, nullable=False, comment="错误描述")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 常量定义
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
class Source:
|
||||||
|
"""请求来源常量"""
|
||||||
|
|
||||||
|
ADMIN = 0 # 后台
|
||||||
|
FRONT = 1 # 前台
|
||||||
|
|
||||||
|
class Status:
|
||||||
|
"""请求状态常量"""
|
||||||
|
|
||||||
|
NORMAL = 0 # 正常
|
||||||
|
ERROR = 1 # 异常
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 魔法方法
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""
|
||||||
|
返回模型的字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式为 "操作日志:{title} - {method}" 的字符串
|
||||||
|
"""
|
||||||
|
return "操作日志:{} - {}".format(self.title, self.method)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
返回模型的字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式为 "操作日志:{title} - {method}" 的字符串
|
||||||
|
"""
|
||||||
|
return self.__repr__()
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
OperationLog 仓储模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
数据访问层,继承 BaseRepository 获得通用 CRUD 能力。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.common.base_repository import BaseRepository
|
||||||
|
from src.modules.log.models import OperationLog
|
||||||
|
|
||||||
|
|
||||||
|
class OperationLogRepository(BaseRepository):
|
||||||
|
"""OperationLog 仓储"""
|
||||||
|
|
||||||
|
model = OperationLog
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
"""
|
||||||
|
登录日志管理控制器模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
提供登录日志(LoginLog)相关的API接口,包括:
|
||||||
|
- 登录日志分页查询
|
||||||
|
- 登录日志详情查询
|
||||||
|
- 登录日志删除(支持批量)
|
||||||
|
- 登录日志清空
|
||||||
|
- 登录日志统计
|
||||||
|
- 登录趋势分析
|
||||||
|
|
||||||
|
所属包: application.views
|
||||||
|
依赖模块: application.services.login_log - 登录日志业务逻辑层
|
||||||
|
application.middleware.check_permission - 权限校验中间件
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import Blueprint, request
|
||||||
|
|
||||||
|
from application.middleware.check_demo import check_demo
|
||||||
|
from application.middleware.check_permission import check_permission
|
||||||
|
from src.modules.log import service as login_log
|
||||||
|
from src.modules.log import service as operation_log
|
||||||
|
from src.common.utils.request import parse_batch_ids
|
||||||
|
|
||||||
|
from src.core import response as R
|
||||||
|
|
||||||
|
login_log_blue = Blueprint("login_log", __name__, url_prefix="/login/log")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志分页查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@login_log_blue.route("/page", methods=["GET"])
|
||||||
|
@check_permission("sys:login_log:page")
|
||||||
|
def page():
|
||||||
|
"""
|
||||||
|
查询登录日志分页数据
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
获取登录日志列表的分页数据,支持按账号、日志类型、来源、
|
||||||
|
状态、时间范围等条件筛选。
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
pageNo: 当前页码(可选,默认1)
|
||||||
|
pageSize: 每页记录数(可选,默认PAGE_SIZE)
|
||||||
|
username: 登录账号(可选,模糊查询)
|
||||||
|
logType: 日志类型(可选,1-登录,2-退出)
|
||||||
|
source: 登录来源(可选,0-后台,1-前台)
|
||||||
|
status: 操作状态(可选,0-正常,1-异常)
|
||||||
|
startTime: 开始时间(可选)
|
||||||
|
endTime: 结束时间(可选)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 登录日志分页数据,包含 records(数据列表)、total(总记录数)等
|
||||||
|
"""
|
||||||
|
return R.ok(data=login_log.get_login_log_page(request.args))
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志详情查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@login_log_blue.route("/detail/<int:id>", methods=["GET"])
|
||||||
|
@check_permission("sys:login_log:detail")
|
||||||
|
def detail(id):
|
||||||
|
"""
|
||||||
|
查询登录日志详情
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: 登录日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 登录日志详细信息
|
||||||
|
"""
|
||||||
|
data = login_log.get_login_log_detail(id)
|
||||||
|
if data:
|
||||||
|
return R.ok(data=data)
|
||||||
|
return R.failed("日志不存在")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@login_log_blue.route("/delete/<string:id>", methods=["DELETE"])
|
||||||
|
@check_permission("sys:login_log:delete")
|
||||||
|
@check_demo
|
||||||
|
def delete(id):
|
||||||
|
"""
|
||||||
|
删除登录日志
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: 登录日志ID(支持逗号分隔的多个ID)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作结果
|
||||||
|
"""
|
||||||
|
count = login_log.delete_login_logs(id)
|
||||||
|
return R.ok(msg=f"本次共删除{count}条数据")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志批量删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@login_log_blue.route("/batchDelete", methods=["DELETE"])
|
||||||
|
@check_permission("sys:login_log:batch_delete")
|
||||||
|
@check_demo
|
||||||
|
def batch_delete():
|
||||||
|
"""
|
||||||
|
批量删除登录日志
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
批量删除登录日志记录,支持传入ID数组。
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
[1, 2, 3] # 登录日志ID数组
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作结果
|
||||||
|
"""
|
||||||
|
id_str, err = parse_batch_ids()
|
||||||
|
if err:
|
||||||
|
return err
|
||||||
|
count = login_log.delete_login_logs(id_str)
|
||||||
|
return R.ok(msg=f"本次共删除{count}条数据")
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
操作日志管理控制器模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
提供操作日志(OperationLog)相关的API接口,包括:
|
||||||
|
- 操作日志分页查询
|
||||||
|
- 操作日志详情查询
|
||||||
|
- 操作日志删除(支持批量)
|
||||||
|
- 操作日志清空
|
||||||
|
- 操作日志统计
|
||||||
|
- 操作日志类型列表
|
||||||
|
|
||||||
|
所属包: application.views
|
||||||
|
依赖模块: application.services.operation_log - 操作日志业务逻辑层
|
||||||
|
application.middleware.check_permission - 权限校验中间件
|
||||||
|
"""
|
||||||
|
operation_log_blue = Blueprint("operation_log", __name__, url_prefix="/oper/log")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志分页查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@operation_log_blue.route("/page", methods=["GET"])
|
||||||
|
@check_permission("sys:operation_log:page")
|
||||||
|
def operation_log_page():
|
||||||
|
"""
|
||||||
|
查询操作日志分页数据
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
获取操作日志列表的分页数据,支持按标题、操作类型、来源、
|
||||||
|
状态、时间范围等条件筛选。
|
||||||
|
|
||||||
|
Query Parameters:
|
||||||
|
pageNo: 当前页码(可选,默认1)
|
||||||
|
pageSize: 每页记录数(可选,默认PAGE_SIZE)
|
||||||
|
title: 请求标题(可选,模糊查询)
|
||||||
|
type: 请求类型(可选,精确查询)
|
||||||
|
source: 请求来源(可选,0-后台,1-前台)
|
||||||
|
status: 请求状态(可选,0-正常,1-异常)
|
||||||
|
startTime: 开始时间(可选)
|
||||||
|
endTime: 结束时间(可选)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作日志分页数据,包含 records(数据列表)、total(总记录数)等
|
||||||
|
"""
|
||||||
|
return R.ok(data=operation_log.get_operation_log_page(request.args))
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志详情查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@operation_log_blue.route("/detail/<int:id>", methods=["GET"])
|
||||||
|
@check_permission("sys:operation_log:detail")
|
||||||
|
def operation_log_detail(id):
|
||||||
|
"""
|
||||||
|
查询操作日志详情
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: 操作日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作日志详细信息
|
||||||
|
"""
|
||||||
|
data = operation_log.get_operation_log_detail(id)
|
||||||
|
if data:
|
||||||
|
return R.ok(data=data)
|
||||||
|
return R.failed("日志不存在")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@operation_log_blue.route("/delete/<string:id>", methods=["DELETE"])
|
||||||
|
@check_permission("sys:operation_log:delete")
|
||||||
|
@check_demo
|
||||||
|
def operation_log_delete(id):
|
||||||
|
"""
|
||||||
|
删除操作日志
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id: 操作日志ID(支持逗号分隔的多个ID)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作结果
|
||||||
|
"""
|
||||||
|
count = operation_log.delete_operation_logs(id)
|
||||||
|
return R.ok(msg=f"本次共删除{count}条数据")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志批量删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@operation_log_blue.route("/batchDelete", methods=["DELETE"])
|
||||||
|
@check_permission("sys:operation_log:batch_delete")
|
||||||
|
@check_demo
|
||||||
|
def operation_log_batch_delete():
|
||||||
|
"""
|
||||||
|
批量删除操作日志
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
批量删除操作日志记录,支持传入ID数组。
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
[1, 2, 3] # 操作日志ID数组
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON: 操作结果
|
||||||
|
"""
|
||||||
|
id_str, err = parse_batch_ids()
|
||||||
|
if err:
|
||||||
|
return err
|
||||||
|
count = operation_log.delete_operation_logs(id_str)
|
||||||
|
return R.ok(msg=f"本次共删除{count}条数据")
|
||||||
@@ -0,0 +1,593 @@
|
|||||||
|
"""
|
||||||
|
登录日志管理服务模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
该模块实现了登录日志管理相关的所有业务逻辑。
|
||||||
|
包括登录日志的分页查询、详情查询、添加、删除、清空等功能。
|
||||||
|
|
||||||
|
命名规范:
|
||||||
|
- 函数参数使用驼峰命名(如 logId, pageNo)
|
||||||
|
- 表单字段使用下划线命名(如 username, log_type)- 与数据库字段保持一致
|
||||||
|
- 返回数据使用驼峰命名(如 createUser, updateTime)- 与前端规范保持一致
|
||||||
|
- 私有函数以下划线开头(如 _apply_filters, _build_page_data)
|
||||||
|
|
||||||
|
功能模块:
|
||||||
|
1. 分页查询:支持按账号模糊搜索、日志类型筛选、来源筛选、状态筛选、时间范围筛选
|
||||||
|
2. 详情查询:根据ID获取单个登录日志的详细信息
|
||||||
|
3. 日志添加:记录用户登录/退出操作日志
|
||||||
|
4. 日志删除:删除指定日志(支持批量)
|
||||||
|
5. 日志清空:清空所有登录日志
|
||||||
|
6. 统计查询:获取登录日志统计信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
from src.common.constants.common import PAGE_SIZE
|
||||||
|
from src.common.constants.dictionary_constants import (
|
||||||
|
OPERATION_LOG_SOURCE_LIST,
|
||||||
|
OPERATION_LOG_TYPE_LIST,
|
||||||
|
LOGIN_LOG_TYPE_LIST,
|
||||||
|
)
|
||||||
|
from src.modules.log.models import LoginLog, OperationLog
|
||||||
|
from src.modules.log.login_log_repository import LoginLogRepository
|
||||||
|
from src.modules.log.operation_log_repository import OperationLogRepository
|
||||||
|
from src.core.exceptions import BizError
|
||||||
|
from src.common.utils.model_utils import models_to_list, model_to_dict
|
||||||
|
from src.common.utils.request import parse_id_list, like_contains
|
||||||
|
|
||||||
|
# 获取日志记录器
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志分页查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def get_login_log_page(params: dict) -> dict:
|
||||||
|
"""
|
||||||
|
查询登录日志分页数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: Flask request对象,包含查询参数
|
||||||
|
- pageNo: 当前页码(可选,默认为1)
|
||||||
|
- pageSize: 每页记录数(可选,默认为PAGE_SIZE)
|
||||||
|
- username: 登录账号(可选,模糊查询)
|
||||||
|
- logType: 日志类型(可选,1-登录,2-退出)
|
||||||
|
- source: 登录来源(可选,0-后台,1-前台)
|
||||||
|
- status: 操作状态(可选,0-正常,1-异常)
|
||||||
|
- startTime: 开始时间(可选)
|
||||||
|
- endTime: 结束时间(可选)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
R: 包含分页数据的JSON响应,格式为:
|
||||||
|
{
|
||||||
|
"records": 日志列表,
|
||||||
|
"total": 总记录数,
|
||||||
|
"size": 每页大小,
|
||||||
|
"current": 当前页码,
|
||||||
|
"pages": 总页数
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# 获取分页参数
|
||||||
|
page_no = int(params.get("pageNo", 1))
|
||||||
|
page_size = int(params.get("pageSize", PAGE_SIZE))
|
||||||
|
|
||||||
|
# 应用筛选条件
|
||||||
|
query = _apply_filters(params)
|
||||||
|
|
||||||
|
# 记录总数
|
||||||
|
total = query.count()
|
||||||
|
# 计算总页数
|
||||||
|
pages = (total + page_size - 1) // page_size if total > 0 else 0
|
||||||
|
|
||||||
|
# 分页查询(按创建时间倒序)
|
||||||
|
log_list = query.order_by(LoginLog.create_time.desc()).limit(page_size).offset((page_no - 1) * page_size).all()
|
||||||
|
|
||||||
|
# 构建返回数据
|
||||||
|
page_data = _build_page_data(log_list, total, page_size, page_no, pages)
|
||||||
|
return page_data
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_filters(params: dict):
|
||||||
|
"""
|
||||||
|
应用查询筛选条件
|
||||||
|
|
||||||
|
根据请求参数动态构建查询条件:
|
||||||
|
- 自动过滤已删除记录(is_delete == 0)
|
||||||
|
- 支持登录账号模糊查询
|
||||||
|
- 支持日志类型精确查询
|
||||||
|
- 支持登录来源精确查询
|
||||||
|
- 支持操作状态精确查询
|
||||||
|
- 支持时间范围查询
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Query: 应用筛选后的SQLAlchemy查询对象
|
||||||
|
"""
|
||||||
|
# 基础查询:过滤已删除记录
|
||||||
|
query = LoginLog.query.filter(LoginLog.is_delete == 0)
|
||||||
|
|
||||||
|
# 登录账号模糊筛选
|
||||||
|
username = params.get("username")
|
||||||
|
if username:
|
||||||
|
query = query.filter(LoginLog.username.like(like_contains(username), escape="\\"))
|
||||||
|
|
||||||
|
# 日志类型精确筛选
|
||||||
|
log_type = params.get("logType")
|
||||||
|
if log_type:
|
||||||
|
query = query.filter(LoginLog.log_type == log_type)
|
||||||
|
|
||||||
|
# 登录来源精确筛选
|
||||||
|
source = params.get("source")
|
||||||
|
if source:
|
||||||
|
query = query.filter(LoginLog.source == source)
|
||||||
|
|
||||||
|
# 操作状态精确筛选
|
||||||
|
status = params.get("status")
|
||||||
|
if status:
|
||||||
|
query = query.filter(LoginLog.status == status)
|
||||||
|
|
||||||
|
# 时间范围筛选
|
||||||
|
start_time = params.get("startTime")
|
||||||
|
end_time = params.get("endTime")
|
||||||
|
|
||||||
|
if start_time:
|
||||||
|
try:
|
||||||
|
start_date = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
query = query.filter(LoginLog.create_time >= start_date)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if end_time:
|
||||||
|
try:
|
||||||
|
end_date = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
query = query.filter(LoginLog.create_time <= end_date)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def _build_page_data(log_list, total, page_size, page_no, pages):
|
||||||
|
"""
|
||||||
|
构建分页返回数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_list: 登录日志对象列表
|
||||||
|
total: 总记录数
|
||||||
|
page_size: 每页大小
|
||||||
|
page_no: 当前页码
|
||||||
|
pages: 总页数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 分页数据,包含 records、total、size、current、pages
|
||||||
|
"""
|
||||||
|
# 将模型对象列表转换为字典列表
|
||||||
|
records = models_to_list(log_list)
|
||||||
|
|
||||||
|
# 遍历数据源
|
||||||
|
for record in records:
|
||||||
|
# 日志来源
|
||||||
|
record["sourceText"] = OPERATION_LOG_SOURCE_LIST.get(record.get("source"), "")
|
||||||
|
# 日志类型
|
||||||
|
record["logTypeText"] = LOGIN_LOG_TYPE_LIST.get(record.get("logType"), "")
|
||||||
|
|
||||||
|
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志详情查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def get_login_log_detail(log_id):
|
||||||
|
"""
|
||||||
|
根据ID查询登录日志详情(返回字典格式)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_id: 登录日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict/None: 登录日志详情字典,不存在时返回 None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log = get_info(log_id)
|
||||||
|
if not log:
|
||||||
|
return None
|
||||||
|
return model_to_dict(log)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"查询登录日志详情异常,ID: {log_id}, 错误: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_info(log_id: int):
|
||||||
|
"""
|
||||||
|
根据ID查询登录日志信息(返回模型对象)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_id: 登录日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
LoginLog/None: 登录日志模型对象,不存在或已删除时返回 None
|
||||||
|
"""
|
||||||
|
return LoginLogRepository.get_by_id(log_id)
|
||||||
|
|
||||||
|
|
||||||
|
def record_login_log(username, source, status, **kwargs):
|
||||||
|
"""
|
||||||
|
记录登录日志(便捷方法)
|
||||||
|
|
||||||
|
供登录/退出接口直接调用,无需通过HTTP请求。
|
||||||
|
使用独立session写入,不受请求事务commit/rollback影响。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: 登录账号
|
||||||
|
source: 登录来源(0-后台,1-前台)
|
||||||
|
status: 操作状态(0-正常,1-异常)
|
||||||
|
**kwargs: 其他可选参数
|
||||||
|
- log_type: 日志类型(默认1-登录)
|
||||||
|
- method: 请求方法
|
||||||
|
- request_method: 请求方式
|
||||||
|
- url: 请求URL
|
||||||
|
- param: 请求参数
|
||||||
|
- result: 返回结果
|
||||||
|
- ip: IP地址
|
||||||
|
- location: IP归属地
|
||||||
|
- os: 操作系统
|
||||||
|
- browser: 浏览器
|
||||||
|
- consume_time: 请求耗时
|
||||||
|
- error: 错误信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
LoginLog/None: 创建的日志对象,失败返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log_data = {
|
||||||
|
"username": username,
|
||||||
|
"log_type": kwargs.get("log_type", LoginLog.LogType.LOGIN),
|
||||||
|
"source": source,
|
||||||
|
"method": kwargs.get("method", ""),
|
||||||
|
"request_method": kwargs.get("request_method", ""),
|
||||||
|
"url": kwargs.get("url", ""),
|
||||||
|
"param": kwargs.get("param", ""),
|
||||||
|
"result": kwargs.get("result", ""),
|
||||||
|
"ip": kwargs.get("ip", ""),
|
||||||
|
"location": kwargs.get("location", ""),
|
||||||
|
"os": kwargs.get("os", ""),
|
||||||
|
"browser": kwargs.get("browser", ""),
|
||||||
|
"consume_time": kwargs.get("consume_time", 0),
|
||||||
|
"status": status,
|
||||||
|
"error": kwargs.get("error", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
log = LoginLog(**log_data)
|
||||||
|
log.create_user = username
|
||||||
|
log.update_user = username
|
||||||
|
|
||||||
|
# 使用独立session写入,不受请求事务影响
|
||||||
|
# 日志是审计数据,无论业务成功或失败都应持久化
|
||||||
|
from src.core.database import commit_independently
|
||||||
|
|
||||||
|
commit_independently(log)
|
||||||
|
|
||||||
|
return log
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"记录登录日志异常: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 登录日志删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def delete_login_logs(log_ids: str) -> int:
|
||||||
|
"""
|
||||||
|
删除登录日志(批量删除)
|
||||||
|
|
||||||
|
删除
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_ids: 日志ID字符串,多个ID用逗号分隔,如 "1,2,3"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
R: 操作结果
|
||||||
|
"""
|
||||||
|
log_ids_list = parse_id_list(log_ids)
|
||||||
|
if not log_ids_list:
|
||||||
|
raise BizError("无效的记录ID")
|
||||||
|
return LoginLog.batch_delete(log_ids_list)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
操作日志管理服务模块
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
该模块实现了操作日志管理相关的所有业务逻辑。
|
||||||
|
包括操作日志的分页查询、详情查询、添加、删除、清空、统计等功能。
|
||||||
|
|
||||||
|
命名规范:
|
||||||
|
- 函数参数使用驼峰命名(如 logId, pageNo)
|
||||||
|
- 表单字段使用下划线命名(如 title, log_type)- 与数据库字段保持一致
|
||||||
|
- 返回数据使用驼峰命名(如 createUser, updateTime)- 与前端规范保持一致
|
||||||
|
- 私有函数以下划线开头(如 _apply_filters, _build_page_data)
|
||||||
|
|
||||||
|
功能模块:
|
||||||
|
1. 分页查询:支持按标题模糊搜索、操作类型筛选、来源筛选、状态筛选、时间范围筛选
|
||||||
|
2. 详情查询:根据ID获取单个操作日志的详细信息
|
||||||
|
3. 日志添加:记录用户操作日志
|
||||||
|
4. 日志删除:删除指定日志(支持批量)
|
||||||
|
5. 日志清空:清空所有操作日志
|
||||||
|
6. 统计查询:获取操作日志统计信息
|
||||||
|
7. 类型查询:获取操作日志类型列表
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志分页查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def get_operation_log_page(params: dict) -> dict:
|
||||||
|
"""
|
||||||
|
查询操作日志分页数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: Flask request对象,包含查询参数
|
||||||
|
- pageNo: 当前页码(可选,默认为1)
|
||||||
|
- pageSize: 每页记录数(可选,默认为PAGE_SIZE)
|
||||||
|
- title: 请求标题(可选,模糊查询)
|
||||||
|
- type: 请求类型(可选,精确查询)
|
||||||
|
- source: 请求来源(可选,0-后台,1-前台)
|
||||||
|
- status: 请求状态(可选,0-正常,1-异常)
|
||||||
|
- startTime: 开始时间(可选)
|
||||||
|
- endTime: 结束时间(可选)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
R: 包含分页数据的JSON响应,格式为:
|
||||||
|
{
|
||||||
|
"records": 日志列表,
|
||||||
|
"total": 总记录数,
|
||||||
|
"size": 每页大小,
|
||||||
|
"current": 当前页码,
|
||||||
|
"pages": 总页数
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# 获取分页参数
|
||||||
|
page_no = int(params.get("pageNo", 1))
|
||||||
|
page_size = int(params.get("pageSize", PAGE_SIZE))
|
||||||
|
|
||||||
|
# 应用筛选条件
|
||||||
|
query = _apply_operation_log_filters(params)
|
||||||
|
|
||||||
|
# 记录总数
|
||||||
|
total = query.count()
|
||||||
|
# 计算总页数
|
||||||
|
pages = (total + page_size - 1) // page_size if total > 0 else 0
|
||||||
|
|
||||||
|
# 分页查询(按创建时间倒序)
|
||||||
|
log_list = query.order_by(OperationLog.create_time.desc()).limit(page_size).offset((page_no - 1) * page_size).all()
|
||||||
|
|
||||||
|
# 构建返回数据
|
||||||
|
page_data = _build_operation_log_page_data(log_list, total, page_size, page_no, pages)
|
||||||
|
return page_data
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_operation_log_filters(params: dict):
|
||||||
|
"""
|
||||||
|
应用查询筛选条件
|
||||||
|
|
||||||
|
根据请求参数动态构建查询条件:
|
||||||
|
- 自动过滤已删除记录(is_delete == 0)
|
||||||
|
- 支持请求标题模糊查询
|
||||||
|
- 支持操作类型精确查询
|
||||||
|
- 支持请求来源精确查询
|
||||||
|
- 支持请求状态精确查询
|
||||||
|
- 支持时间范围查询
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Query: 应用筛选后的SQLAlchemy查询对象
|
||||||
|
"""
|
||||||
|
# 基础查询:过滤已删除记录
|
||||||
|
query = OperationLog.query.filter(OperationLog.is_delete == 0)
|
||||||
|
|
||||||
|
# 请求标题模糊筛选
|
||||||
|
title = params.get("title")
|
||||||
|
if title:
|
||||||
|
query = query.filter(OperationLog.title.like(like_contains(title), escape="\\"))
|
||||||
|
|
||||||
|
# 请求类型精确筛选
|
||||||
|
log_type = params.get("type")
|
||||||
|
if log_type:
|
||||||
|
query = query.filter(OperationLog.type == log_type)
|
||||||
|
|
||||||
|
# 请求来源精确筛选
|
||||||
|
source = params.get("source")
|
||||||
|
if source:
|
||||||
|
query = query.filter(OperationLog.source == source)
|
||||||
|
|
||||||
|
# 请求状态精确筛选
|
||||||
|
status = params.get("status")
|
||||||
|
if status:
|
||||||
|
query = query.filter(OperationLog.status == status)
|
||||||
|
|
||||||
|
# 时间范围筛选
|
||||||
|
start_time = params.get("startTime")
|
||||||
|
end_time = params.get("endTime")
|
||||||
|
|
||||||
|
if start_time:
|
||||||
|
try:
|
||||||
|
start_date = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
query = query.filter(OperationLog.create_time >= start_date)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if end_time:
|
||||||
|
try:
|
||||||
|
end_date = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
query = query.filter(OperationLog.create_time <= end_date)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def _build_operation_log_page_data(log_list, total, page_size, page_no, pages):
|
||||||
|
"""
|
||||||
|
构建分页返回数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_list: 操作日志对象列表
|
||||||
|
total: 总记录数
|
||||||
|
page_size: 每页大小
|
||||||
|
page_no: 当前页码
|
||||||
|
pages: 总页数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 分页数据,包含 records、total、size、current、pages
|
||||||
|
"""
|
||||||
|
# 将模型对象列表转换为字典列表
|
||||||
|
records = models_to_list(log_list)
|
||||||
|
|
||||||
|
# 遍历数据源
|
||||||
|
for record in records:
|
||||||
|
# 日志来源
|
||||||
|
record["sourceText"] = OPERATION_LOG_SOURCE_LIST.get(record.get("source"), "")
|
||||||
|
# 日志类型
|
||||||
|
record["typeText"] = OPERATION_LOG_TYPE_LIST.get(record.get("type"), "")
|
||||||
|
|
||||||
|
return {"records": records, "total": total, "size": page_size, "current": page_no, "pages": pages}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志详情查询
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def get_operation_log_detail(log_id):
|
||||||
|
"""
|
||||||
|
根据ID查询操作日志详情(返回字典格式)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_id: 操作日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict/None: 操作日志详情字典,不存在时返回 None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log = get_operation_log_info(log_id)
|
||||||
|
if not log:
|
||||||
|
return None
|
||||||
|
data = model_to_dict(log)
|
||||||
|
# 日志来源
|
||||||
|
data["sourceText"] = OPERATION_LOG_SOURCE_LIST.get(data.get("source"), "")
|
||||||
|
return data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"查询操作日志详情异常,ID: {log_id}, 错误: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_operation_log_info(log_id: int):
|
||||||
|
"""
|
||||||
|
根据ID查询操作日志信息(返回模型对象)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_id: 操作日志ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
OperationLog/None: 操作日志模型对象,不存在或已删除时返回 None
|
||||||
|
"""
|
||||||
|
return OperationLogRepository.get_by_id(log_id)
|
||||||
|
|
||||||
|
|
||||||
|
def record_operation_log(title, log_type, source, status, **kwargs):
|
||||||
|
"""
|
||||||
|
记录操作日志(便捷方法)
|
||||||
|
|
||||||
|
供业务接口直接调用,无需通过HTTP请求。
|
||||||
|
使用独立session写入,不受请求事务commit/rollback影响。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: 请求标题
|
||||||
|
log_type: 请求类型
|
||||||
|
source: 请求来源(0-后台,1-前台)
|
||||||
|
status: 操作状态(0-正常,1-异常)
|
||||||
|
**kwargs: 其他可选参数
|
||||||
|
- method: 请求方法
|
||||||
|
- request_method: 请求方式
|
||||||
|
- url: 请求URL
|
||||||
|
- param: 请求参数
|
||||||
|
- result: 返回结果
|
||||||
|
- ip: IP地址
|
||||||
|
- location: IP归属地
|
||||||
|
- os: 操作系统
|
||||||
|
- browser: 浏览器
|
||||||
|
- consume_time: 请求耗时
|
||||||
|
- error: 错误信息
|
||||||
|
- username: 操作用户
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
OperationLog/None: 创建的日志对象,失败返回None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
log_data = {
|
||||||
|
"title": title,
|
||||||
|
"type": log_type,
|
||||||
|
"source": source,
|
||||||
|
"method": kwargs.get("method", ""),
|
||||||
|
"request_method": kwargs.get("request_method", ""),
|
||||||
|
"url": kwargs.get("url", ""),
|
||||||
|
"param": kwargs.get("param", ""),
|
||||||
|
"result": kwargs.get("result", ""),
|
||||||
|
"ip": kwargs.get("ip", ""),
|
||||||
|
"location": kwargs.get("location", ""),
|
||||||
|
"os": kwargs.get("os", ""),
|
||||||
|
"browser": kwargs.get("browser", ""),
|
||||||
|
"consume_time": kwargs.get("consume_time", 0),
|
||||||
|
"status": status,
|
||||||
|
"error": kwargs.get("error", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
log = OperationLog(**log_data)
|
||||||
|
log.create_user = kwargs.get("username", "system")
|
||||||
|
log.update_user = kwargs.get("username", "system")
|
||||||
|
|
||||||
|
# 使用独立session写入,不受请求事务影响
|
||||||
|
# 日志是审计数据,无论业务成功或失败都应持久化
|
||||||
|
from src.core.database import commit_independently
|
||||||
|
|
||||||
|
commit_independently(log)
|
||||||
|
|
||||||
|
return log
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"记录操作日志异常: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 操作日志删除
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
def delete_operation_logs(log_ids: str) -> int:
|
||||||
|
"""
|
||||||
|
删除操作日志(批量删除)
|
||||||
|
|
||||||
|
删除
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_ids: 日志ID字符串,多个ID用逗号分隔,如 "1,2,3"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
R: 操作结果
|
||||||
|
"""
|
||||||
|
log_ids_list = parse_id_list(log_ids)
|
||||||
|
if not log_ids_list:
|
||||||
|
raise BizError("无效的记录ID")
|
||||||
|
return OperationLog.batch_delete(log_ids_list)
|
||||||
Reference in New Issue
Block a user