feat: 用户登录/管理 + 操作日志模块
- tb_user 用户表、tb_log 日志表 - Flask-Login 认证(login/logout/权限装饰器) - 用户管理页(admin 专有):增删改查、改密、角色设置 - 操作日志页:分页查询、按用户/类型筛选 - 测试操作区指令自动记录日志 - 所有页面加 @login_required 保护 - 默认管理员 admin/admin123(首次启动自动创建)
This commit is contained in:
@@ -236,3 +236,108 @@ def get_automation_averages(dnt_id: int) -> dict:
|
||||
if row:
|
||||
return {k: round(v, 2) if v else 0 for k, v in row.items()}
|
||||
return {}
|
||||
|
||||
|
||||
# ─── 用户管理 ──────────────────────────────────────────────────────
|
||||
|
||||
def get_user_by_username(username: str) -> dict | None:
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM tb_user WHERE username=%s", (username,))
|
||||
return cur.fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_all_users() -> list[dict]:
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT id, username, role, is_active, create_time FROM tb_user ORDER BY id")
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def create_user(username: str, password_hash: str, role: str = "operator"):
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"INSERT INTO tb_user (username, password_hash, role) VALUES (%s,%s,%s)",
|
||||
(username, password_hash, role),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def update_user(user_id: int, password_hash: str = None, role: str = None, is_active: bool = None):
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
parts = []
|
||||
params = []
|
||||
if password_hash is not None:
|
||||
parts.append("password_hash=%s")
|
||||
params.append(password_hash)
|
||||
if role is not None:
|
||||
parts.append("role=%s")
|
||||
params.append(role)
|
||||
if is_active is not None:
|
||||
parts.append("is_active=%s")
|
||||
params.append(int(is_active))
|
||||
if parts:
|
||||
params.append(user_id)
|
||||
cur.execute(f"UPDATE tb_user SET {', '.join(parts)} WHERE id=%s", params)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
# ─── 日志管理 ──────────────────────────────────────────────────────
|
||||
|
||||
def insert_log(user_id: int, username: str, action_type: str,
|
||||
target: str = "", detail: str = "", result: str = "ok",
|
||||
ip: str = ""):
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"INSERT INTO tb_log (user_id, username, action_type, target, detail, result, ip) "
|
||||
"VALUES (%s,%s,%s,%s,%s,%s,%s)",
|
||||
(user_id, username, action_type, target, detail, result, ip),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_logs(page: int = 1, per_page: int = 30,
|
||||
username: str = "", action_type: str = "") -> tuple[list[dict], int]:
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
where = []
|
||||
params = []
|
||||
if username:
|
||||
where.append("username LIKE %s")
|
||||
params.append(f"%{username}%")
|
||||
if action_type:
|
||||
where.append("action_type=%s")
|
||||
params.append(action_type)
|
||||
where_clause = " AND ".join(where) if where else "1=1"
|
||||
|
||||
cur.execute(f"SELECT COUNT(*) as total FROM tb_log WHERE {where_clause}", params)
|
||||
total = cur.fetchone()["total"]
|
||||
|
||||
offset = (page - 1) * per_page
|
||||
cur.execute(
|
||||
f"SELECT * FROM tb_log WHERE {where_clause} "
|
||||
f"ORDER BY id DESC LIMIT %s OFFSET %s",
|
||||
params + [per_page, offset],
|
||||
)
|
||||
return cur.fetchall(), total
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user