Files
workspace/code/fms/.codebuddy/plans/登录日志功能_前后端__eef08f78.md
T
2026-07-26 22:52:21 +08:00

9.7 KiB
Raw Blame History

name, overview, design, todos
name overview design todos
登录日志功能(前后端) 后端在登录时统一采集客户端信息(IP、IP归属地、浏览器/版本、操作系统/版本、设备类型、原始UA),复用通用 saveobjt 机制写入新表 s_log_login(成功与失败都记录);前端新增只读列表页展示登录日志。表名 s_log_login,权限暂不接入。
styleKeywords fontSystem colorSystem
Enterprise
Clean
Audit Table
Teal Accent
Light Theme
fontFamily heading subheading body
PingFang SC
size weight
20px 600
size weight
14px 500
size weight
13px 400
primary background text functional
#15998E
#173A42
#F5F7F8
#FFFFFF
#19323A
#5B6B70
#52C41A
#FF4D4F
#15998E
id content status
add-deps-xdb pom.xml 新增 ip2region/uap-java 依赖并下载 ip2region.xdb 到 resources completed
id content status dependencies
client-info-utils 实现 IpUtils、ClientInfo、IpRegionSearcher、ClientInfoExtractor 采集工具 completed
add-deps-xdb
id content status dependencies
login-log-service 实现 LoginLogService 并在 AuthController/AuthService 登录成功与失败分支串联记录 completed
client-info-utils
id content status
migration-table 新增 003_create_login_log.sql 建表 s_log_login 及索引 completed
id content status dependencies
frontend-log-page 用 [skill:antdv-next] 新增路由、侧边栏菜单与登录日志只读列表页(筛选+分页) completed
migration-table

用户需求

统一在登录环节采集客户端信息,落地「登录日志」功能,覆盖登录成功与失败(账号不存在、密码错误)两类行为,供后续操作日志与系统审计复用。前端提供只读的登录日志列表页。

产品概述

登录时由后端统一采集客户端环境信息并落库,形成可审计的登录轨迹;管理员可在前端以表格形式分页查看每次登录的客户端画像与结果。

核心功能

  • 后端登录流程中采集:IP 地址、IP 归属地(国家/省/市/运营商)、浏览器及版本、操作系统及版本、设备类型、原始 User-Agent。
  • 登录成功与失败均写登录日志(表 s_log_login),失败记录失败原因,写日志失败不影响登录。
  • 前端新增「登录日志」只读列表页:分页展示全部采集字段,成功/失败以标签区分,支持按账号、结果、时间筛选。
  • 权限按用户要求暂不做限制(路由不带 moduleCode,登录用户均可访问)。

技术栈

  • 后端:Java 21 + Spring Boot 4.1,沿用 DbUtils 原生 JDBC 与多机构 OrgRoutingDataSource;新增依赖 org.lionsoul:ip2region(本地 xdb 解析)、com.github.ua-parser:uap-java(User-Agent 解析)。
  • 前端:Vue 3 + Vite + antdv-next + Pinia,复用现有 pageDataApi 通用分页接口。

实现思路

  • 客户端信息全部在后端登录时采集:IP 与 UA 本就是 HTTP 请求头,服务端获取更可靠、不可被前端伪造,也契合规范中「优先通用接口、不新增业务接口」。
  • IpUtils:按 X-Real-IP → X-Forwarded-For(取首个有效 IP)→ request.getRemoteAddr() 顺序取值,兼容 Nginx 代理。
  • IpRegionSearcher:以 @Component 单例在启动时把 ip2region.xdb 读入内存(Searcher.newWithBuffer(bytes)),避免每次文件 IO;search(ip) 返回 国家|省|市|运营商,异常时返回「未知」。
  • ClientInfoExtractor:@Component,注入 IpRegionSearcher 与 uap-java Parser(单例),extract(HttpServletRequest) 解析 UA 得到浏览器/版本、系统/版本、设备类型(依据 device.family 映射 Mobile/Tablet/Desktop/Bot/Unknown)。
  • LoginLogService.recordLogin(...):在方法内 OrgContext.setOrgId(orgId) 后,组装 s_log_login 行(b_id = IdGenerator.nextId()),以通用保存结构 {table:'s_log_login', key_field:'b_id', inserts:[row]} 调用 DataSaveService.save(...)(即 saveobjt 后端实现),整段 try/catch 包裹、失败仅记录日志、finally 中 OrgContext.clear(),保证登录绝不因写日志失败而中断;机构无效(无数据源)时自然抛错被捕获跳过落库。
  • AuthController.login 将 HttpServletRequest 透传给 AuthService.login;在成功(含 sessionId)与失败(账号不存在/密码错误)分支均调用 recordLogin。
  • 前端:新增路由(无 moduleCode)+ 侧边栏菜单项(无 moduleCode 经 canModule 恒为可见)+ 只读 a-table 列表页,调用 pageDataApi({viewName:'s_log_login', orderBy:'b_login_time DESC'}),分页依据响应 rows/total。

性能与可靠性

  • ip2region 内存检索为 O(1) 级、uap-java 解析为微秒级,登录非高频操作,无性能瓶颈;不引入每请求文件 IO 或额外 SQL。
  • 写日志与登录主流程解耦(try/catch + 独立事务回滚不影响登录),OrgContext 严格 finally 清理,避免线程上下文污染。
  • 不改动 saveobjt/通用查询等既有接口与共享组件,向后兼容。

架构设计

flowchart TD
    A[AuthController.login HttpServletRequest] --> B[AuthService.login]
    B -->|成功/失败| C[LoginLogService.recordLogin]
    C --> D[ClientInfoExtractor.extract]
    D --> E[IpUtils 取IP]
    D --> F[IpRegionSearcher 归属地]
    D --> G[uap-java 解析UA]
    C -->|OrgContext.setOrgId| H[DataSaveService.save 通用saveobjt]
    H --> I[(s_log_login 机构库)]
    subgraph 前端
        J[LoginLogView] --> K[pageDataApi /data/page]
        K --> L[(s_log_login)]
    end

目录结构

fms-api/
├── pom.xml                                          # [MODIFY] 新增 ip2region、uap-java 依赖
├── src/main/resources/ip2region.xdb                 # [NEW] 官方 ip2region 二进制库(约11MB,不入库/可 gitignore)
├── src/main/resources/application.yaml              # [MODIFY/可选] 如需配置 xdb 路径占位
├── config/migrations/003_create_login_log.sql      # [NEW] 建表 s_log_login + 索引
└── src/main/java/cn/g3soft/fmsapi/
    ├── utils/
    │   ├── IpUtils.java                             # [NEW] 从请求头/远程地址提取客户端 IP
    │   ├── ClientInfo.java                          # [NEW] 客户端信息载体(ip/location/browser/os/deviceType/ua)
    │   ├── IpRegionSearcher.java                    # [NEW] 启动初始化 xdb Searcher,提供 search(ip)
    │   └── ClientInfoExtractor.java                 # [NEW] 组合 IpUtils+IpRegionSearcher+uap-java,extract(request)
    ├── service/
    │   ├── LoginLogService.java                     # [NEW] recordLogin:组装并调用 DataSaveService.save
    │   └── AuthService.java                         # [MODIFY] 注入 LoginLogService,login 增 HttpServletRequest 参数并在成功/失败分支记录
    └── controller/AuthController.java               # [MODIFY] login 透传 HttpServletRequest 给 service

fms-vue/
├── src/services/api.js                              # [MODIFY] 新增 pageLoginLogApi 包装 pageDataApi
├── src/router/routes.js                             # [MODIFY] 新增 /system/login-log 路由(无 moduleCode)
├── src/layouts/components/AppSidebar.vue           # [MODIFY] systemPages 增加「登录日志」菜单项(无 moduleCode)
└── src/views/system/login-log/
    └── index.vue                                    # [NEW] 只读列表页:筛选栏 + a-table 分页展示

关键代码结构

// ClientInfo:客户端环境信息载体
public record ClientInfo(
    String ip, String ipLocation,
    String browser, String browserVersion,
    String os, String osVersion,
    String deviceType, String userAgent
) {}

// LoginLogService 写日志入口(成功/失败均调用)
void recordLogin(String orgId, String account, Map<String,Object> user,
                 boolean success, String failReason, String sessionId,
                 HttpServletRequest request);

设计风格

沿用系统现有的企业级货运物流后台风格:浅色画布、左侧深色(#173a42)侧栏、青绿主色(#15998e)点缀。登录日志页为「筛选栏 + 只读数据表格」的经典审计列表布局,保持与模块管理页一致的视觉语言与栅格间距,整体克制、清晰、专业。

页面规划(1 屏)

  • 顶部区块:页标题「登录日志」+ 筛选栏(账号输入框、登录结果下拉「成功/失败」、登录时间范围选择器、查询按钮)。
  • 主体区块:antdv a-table 只读表格,列含 登录时间、账号、用户姓名、IP、IP 归属地、浏览器(版本)、操作系统(版本)、设备类型、登录结果(标签)、原始 UA(溢出省略,tooltip 全显);底部分页。
  • 结果列用 a-tag 区分:成功=绿色、失败=红色。

交互与响应式

  • 筛选条件变更触发分页查询(pageDataApi),加载态用 a-spin/表格 loading。
  • 表格列在窄屏下横向滚动(scroll.x),筛选栏在移动端自动换行。
  • 行 hover 高亮;UA 等超长文本 ellipsis + tooltip,避免撑破布局。

Agent Extensions

Skill

  • antdv-next
  • Purpose: 在实现登录日志只读列表页时,获取 antdv-next 表格(a-table)、筛选控件(a-input / a-select / a-range-picker)、a-tag 等组件的准确 API、事件与插槽用法。
  • Expected outcome: 前端列表页使用符合项目自动导入约定的 antdv-next 组件,分页/筛选/标签渲染正确且无 API 误用。