Files
workspace/code/fms/sql/fms_module_log.sql
2026-09-23 16:54:54 +08:00

472 lines
23 KiB
Transact-SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================================
FMS 模块注册脚本 — 日志(系统管理 → 日志 → 登录日志 / 操作日志 / 技术日志)
依据文档:FMS新系统核心表结构设计.md「12. 日志」
前置条件:
s_log_technical / s_log_login / s_log_audit / s_log_audit_field 已由 fms_core.sql 建表。
使用说明:
1. 执行前先切换到目标数据库(USE FMS)。
2. 本脚本只写模块元数据(s_module / s_field / s_module_schema)、查询视图与菜单,不含日志数据。
3. 脚本可重复执行:先清空本分组下的 s_field / s_module / s_module_schema 行再重建。
4. 三张日志表都是只读数据源:b_save_table 为 NULL,列表页不提供新增 / 编辑 / 删除。
写入侧现状:
- 登录日志:后端 LoginLogService 已在登录成功 / 失败时落库;
- 操作日志(审计)/ 技术日志:写入逻辑待实现,页面先建好,有数据即可看到。
============================================================================ */
SET NOCOUNT ON;
GO
/* ----------------------------------------------------------------------------
1. 查询视图
用途:作为 s_module.b_view_table 的数据源;s_field.b_field 必须是视图里真实存在的列。
v_ 前缀是派生列(只读,不参与保存):日志表存的是编码 / 枚举值,展示用的可读文本
在视图里拼好,列表列设置与查询条件都直接用这些派生列,避免前端再写转换逻辑。
---------------------------------------------------------------------------- */
/* 登录日志:b_login_result 是 0/1,派生一列中文结果;浏览器 / 系统拼成「名称 版本」 */
create or alter view dbo.v_s_log_login as
select l.b_id,
l.b_user_id,
-- 登录账号(派生):直接取登录时输入的账号,不联 b_user 取姓名 ——
-- 账号是登录事件的稳定标识(成功 / 密码错误 / 账号不存在都有值),
-- 而姓名只对已存在账号有意义,失败记录会缺一半;少一次 join 也不吃亏
l.b_user_id as v_user_name,
l.b_login_type,
case when l.b_login_result = 1 then N'成功' else N'失败' end as v_login_result, -- 登录结果(派生)
l.b_fail_reason,
l.b_ip,
l.b_ip_location,
l.b_browser,
l.b_browser_version,
l.b_os,
l.b_os_version,
l.b_device_type,
l.b_session_id,
l.b_request_id,
l.b_trace_id,
l.b_occurdatetime
from dbo.s_log_login l;
GO
/* 操作日志(审计):操作人名称在写入时已冗余落库,这里不联表 */
create or alter view dbo.v_s_log_audit as
select a.b_id,
a.b_event_code,
a.b_module_id,
a.b_data_id,
a.b_business_no,
a.b_operation,
a.b_operator_id,
a.b_operator_name,
a.b_source_type,
a.b_visibility,
a.b_payload_text,
a.b_trace_id,
a.b_occurdatetime
from dbo.s_log_audit a;
GO
/* 技术日志:b_exception_text / b_context_text 是大文本,不进程列表字段,仅按需查库 */
create or alter view dbo.v_s_log_technical as
select t.b_id,
t.b_user_id,
u.b_name as v_user_name, -- 用户名(派生)
t.b_sourcetype,
t.b_loglevel,
t.b_logcategory,
t.b_operation,
t.b_module_id,
t.b_duration_ms,
t.b_result_code,
t.b_error_code,
t.b_error_message,
t.b_server_node,
t.b_trace_id,
t.b_occurdatetime
from dbo.s_log_technical t
left join dbo.b_user u on u.b_id = t.b_user_id;
GO
/* ----------------------------------------------------------------------------
2. 父节点路径修正 + 清理(脚本可重复执行)
b_path 是派生字段(权威关系是 b_parent_id),格式形如 /system/log/。
已存在的 system 节点 b_path 是历史遗留的 /-1/,与派生规则不一致:
不先修正,本脚本新增的 log 节点就无法与之拼成连贯路径。
---------------------------------------------------------------------------- */
update dbo.s_module
set b_path = '/system/'
where b_id = 'system'
and b_path <> '/system/';
GO
delete from dbo.s_module_schema
where b_module_id in (select b_id from dbo.s_module where b_path like '/system/log/%');
GO
delete from dbo.s_field
where b_module_id in (select b_id from dbo.s_module where b_path like '/system/log/%');
GO
delete from dbo.s_module
where b_path like '/system/log/%' or b_id = 'log';
GO
/* ----------------------------------------------------------------------------
3. 分组节点(module 类型):系统管理 → 日志
---------------------------------------------------------------------------- */
insert into dbo.s_module (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_module_type, b_canuse, b_xh, b_bz)
values ('log', 'system', 1, '/system/log/', N'日志', 'module.log', 'module', 1, 20,
N'系统运行痕迹:登录 / 操作(审计)/ 技术日志,均为只读数据源');
GO
/* ----------------------------------------------------------------------------
4. 数据模块(data 类型,只读)
b_view_table 指向本脚本的视图;b_save_table 留空表示不可写 —— 列表页因此不提供
新增 / 编辑 / 删除,符合日志「只增不改」的语义(写入只发生在后端)。
b_order_sql 统一按发生时间倒序:看日志永远是「最近的先出现」。
---------------------------------------------------------------------------- */
insert into dbo.s_module (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_module_type,
b_view_table, b_save_table, b_key_field, b_order_sql, b_canuse, b_xh, b_bz)
values ('v_s_log_login', 'log', 2, '/system/log/v_s_log_login/', N'登录日志', 'module.v_s_log_login', 'data',
'v_s_log_login', null, 'b_id', 'b_occurdatetime DESC', 1, 10,
N'登录成功 / 失败记录(含 IP、设备、失败原因),由后端 LoginLogService 写入');
GO
insert into dbo.s_module (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_module_type,
b_view_table, b_save_table, b_key_field, b_order_sql, b_canuse, b_xh, b_bz)
values ('v_s_log_audit', 'log', 2, '/system/log/v_s_log_audit/', N'操作日志', 'module.v_s_log_audit', 'data',
'v_s_log_audit', null, 'b_id', 'b_occurdatetime DESC', 1, 20,
N'业务操作审计:谁在何时对哪条数据做了什么(字段级前后值见 s_log_audit_field)');
GO
insert into dbo.s_module (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_module_type,
b_view_table, b_save_table, b_key_field, b_order_sql, b_canuse, b_xh, b_bz)
values ('v_s_log_technical', 'log', 2, '/system/log/v_s_log_technical/', N'技术日志', 'module.v_s_log_technical', 'data',
'v_s_log_technical', null, 'b_id', 'b_occurdatetime DESC', 1, 30,
N'后端异常 / 性能记录,供开发排查;与登录、操作日志通过 b_trace_id 关联');
GO
/* ----------------------------------------------------------------------------
5. 字段定义
b_field 必须存在于对应视图;b_type 决定单元格渲染与查询操作符分组:
input(文本)/ number / datetime。
---------------------------------------------------------------------------- */
/* 5.1 登录日志 */
insert into dbo.s_field (b_module_id, b_field, b_name, b_i18n, b_type, b_canuse, b_xh)
values ('v_s_log_login', 'b_occurdatetime', N'登录时间', null, 'datetime', 1, 10),
('v_s_log_login', 'v_user_name', N'登录账号', null, 'input', 1, 20),
('v_s_log_login', 'b_user_id', N'用户编码', null, 'input', 1, 30),
('v_s_log_login', 'v_login_result', N'登录结果', null, 'input', 1, 40),
('v_s_log_login', 'b_login_type', N'登录类型', null, 'input', 1, 50),
('v_s_log_login', 'b_fail_reason', N'失败原因', null, 'input', 1, 60),
('v_s_log_login', 'b_ip', N'IP', null, 'input', 1, 70),
('v_s_log_login', 'b_ip_location', N'IP 归属地', null, 'input', 1, 80),
('v_s_log_login', 'b_browser', N'浏览器', null, 'input', 1, 90),
('v_s_log_login', 'b_browser_version', N'浏览器版本', null, 'input', 1, 100),
('v_s_log_login', 'b_os', N'操作系统', null, 'input', 1, 110),
('v_s_log_login', 'b_os_version', N'系统版本', null, 'input', 1, 120),
('v_s_log_login', 'b_device_type', N'设备类型', null, 'input', 1, 130),
('v_s_log_login', 'b_session_id', N'会话 ID', null, 'input', 1, 140),
('v_s_log_login', 'b_trace_id', N'链路 ID', null, 'input', 1, 150),
('v_s_log_login', 'b_id', N'日志 ID', null, 'input', 1, 160);
GO
/* 5.2 操作日志 */
insert into dbo.s_field (b_module_id, b_field, b_name, b_i18n, b_type, b_canuse, b_xh)
values ('v_s_log_audit', 'b_occurdatetime', N'操作时间', null, 'datetime', 1, 10),
('v_s_log_audit', 'b_operator_name', N'操作人', null, 'input', 1, 20),
('v_s_log_audit', 'b_module_id', N'模块', null, 'input', 1, 30),
('v_s_log_audit', 'b_operation', N'操作', null, 'input', 1, 40),
('v_s_log_audit', 'b_business_no', N'业务单号', null, 'input', 1, 50),
('v_s_log_audit', 'b_event_code', N'事件编码', null, 'input', 1, 60),
('v_s_log_audit', 'b_data_id', N'数据 ID', null, 'input', 1, 70),
('v_s_log_audit', 'b_source_type', N'来源类型', null, 'input', 1, 80),
('v_s_log_audit', 'b_visibility', N'可见性', null, 'input', 1, 90),
('v_s_log_audit', 'b_operator_id', N'操作人编码', null, 'input', 1, 100),
('v_s_log_audit', 'b_trace_id', N'链路 ID', null, 'input', 1, 110),
('v_s_log_audit', 'b_payload_text', N'载荷文本', null, 'input', 1, 120),
('v_s_log_audit', 'b_id', N'日志 ID', null, 'input', 1, 130);
GO
/* 5.3 技术日志 */
insert into dbo.s_field (b_module_id, b_field, b_name, b_i18n, b_type, b_canuse, b_xh)
values ('v_s_log_technical', 'b_occurdatetime', N'发生时间', null, 'datetime', 1, 10),
('v_s_log_technical', 'b_loglevel', N'级别', null, 'input', 1, 20),
('v_s_log_technical', 'b_logcategory', N'分类', null, 'input', 1, 30),
('v_s_log_technical', 'b_operation', N'操作', null, 'input', 1, 40),
('v_s_log_technical', 'b_module_id', N'模块', null, 'input', 1, 50),
('v_s_log_technical', 'v_user_name', N'用户', null, 'input', 1, 60),
('v_s_log_technical', 'b_duration_ms', N'耗时(ms)', null, 'number', 1, 70),
('v_s_log_technical', 'b_error_code', N'错误码', null, 'input', 1, 80),
('v_s_log_technical', 'b_error_message', N'错误信息', null, 'input', 1, 90),
('v_s_log_technical', 'b_result_code', N'结果码', null, 'input', 1, 100),
('v_s_log_technical', 'b_sourcetype', N'来源类型', null, 'input', 1, 110),
('v_s_log_technical', 'b_server_node', N'服务节点', null, 'input', 1, 120),
('v_s_log_technical', 'b_trace_id', N'链路 ID', null, 'input', 1, 130),
('v_s_log_technical', 'b_user_id', N'用户编码', null, 'input', 1, 140),
('v_s_log_technical', 'b_id', N'日志 ID', null, 'input', 1, 150);
GO
/* ----------------------------------------------------------------------------
6. 界面配置(s_module_schema):view / query
日志只读,不生成 edit 配置(列表页不进入可编辑模式)。
查询条件的时间字段用 between:日志排查的第一步永远是「圈一个时间段」。
---------------------------------------------------------------------------- */
/* 6.1 登录日志 */
insert into dbo.s_module_schema (b_module_id, b_schema_type, b_schema_json, b_canuse)
values ('v_s_log_login', 'view', N'{
"schemaVersion": 1,
"columns": [
{ "field": "b_occurdatetime", "width": 170 },
{ "field": "v_user_name", "width": 140 },
{ "field": "v_login_result", "width": 90 },
{ "field": "b_fail_reason", "width": 180 },
{ "field": "b_ip", "width": 130 },
{ "field": "b_ip_location", "width": 150 },
{ "field": "b_browser", "width": 110, "visible": false },
{ "field": "b_os", "width": 110, "visible": false },
{ "field": "b_device_type", "width": 100 }
]
}', 1),
('v_s_log_login', 'query', N'{
"schemaVersion": 2,
"defaultViewCode": "all",
"views": [
{ "code": "all", "label": "全部", "order": 10 },
{ "code": "today", "label": "今天", "order": 20, "showCount": true,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "today" } } ] },
{ "code": "this_week", "label": "本周", "order": 30,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "this_week" } } ] },
{ "code": "failed", "label": "登录失败", "order": 40, "showCount": true,
"conditions": [ { "field": "v_login_result", "operator": "eq", "value": "失败" } ] }
],
"keywordSearch": {
"placeholder": "搜索账号、IP、归属地",
"trigger": "enter_or_debounce",
"fields": ["v_user_name", "b_ip", "b_ip_location"]
},
"quickFilters": [
{ "field": "b_occurdatetime", "order": 10 },
{ "field": "v_login_result", "order": 20 }
],
"filterGroups": [
{ "code": "basic", "label": "基础信息", "order": 10, "fields": ["v_user_name", "b_ip", "b_ip_location"] },
{ "code": "device", "label": "终端环境", "order": 20, "fields": ["b_browser", "b_os", "b_device_type", "b_login_type"] }
],
"queryFields": {
"b_occurdatetime": { "component": "datetime", "operator": "between" },
"v_user_name": { "component": "input", "operator": "like" },
"v_login_result": { "component": "select", "operator": "eq", "options": [{ "label": "成功", "value": "成功" }, { "label": "失败", "value": "失败" }] },
"b_login_type": { "component": "input", "operator": "like" },
"b_ip": { "component": "input", "operator": "like" },
"b_ip_location": { "component": "input", "operator": "like" },
"b_browser": { "component": "input", "operator": "like" },
"b_os": { "component": "input", "operator": "like" },
"b_device_type": { "component": "input", "operator": "like" }
}
}', 1);
GO
/* 6.2 操作日志 */
insert into dbo.s_module_schema (b_module_id, b_schema_type, b_schema_json, b_canuse)
values ('v_s_log_audit', 'view', N'{
"schemaVersion": 1,
"columns": [
{ "field": "b_occurdatetime", "width": 170 },
{ "field": "b_operator_name", "width": 120 },
{ "field": "b_module_id", "width": 150 },
{ "field": "b_operation", "width": 110 },
{ "field": "b_business_no", "width": 150 },
{ "field": "b_event_code", "width": 150, "visible": false },
{ "field": "b_visibility", "width": 100 },
{ "field": "b_data_id", "width": 140 }
]
}', 1),
('v_s_log_audit', 'query', N'{
"schemaVersion": 2,
"defaultViewCode": "all",
"views": [
{ "code": "all", "label": "全部", "order": 10 },
{ "code": "today", "label": "今天", "order": 20, "showCount": true,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "today" } } ] },
{ "code": "this_week", "label": "本周", "order": 30,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "this_week" } } ] },
{ "code": "this_month", "label": "本月", "order": 40,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "this_month" } } ] }
],
"keywordSearch": {
"placeholder": "搜索操作人、模块、业务单号",
"trigger": "enter_or_debounce",
"fields": ["b_operator_name", "b_module_id", "b_business_no"]
},
"quickFilters": [
{ "field": "b_occurdatetime", "order": 10 },
{ "field": "b_operation", "order": 20 }
],
"filterGroups": [
{ "code": "basic", "label": "基础信息", "order": 10, "fields": ["b_operator_name", "b_module_id", "b_operation", "b_business_no"] },
{ "code": "trace", "label": "追踪", "order": 20, "fields": ["b_event_code", "b_trace_id", "b_data_id"] }
],
"queryFields": {
"b_occurdatetime": { "component": "datetime", "operator": "between" },
"b_operator_name": { "component": "input", "operator": "like" },
"b_module_id": { "component": "input", "operator": "like" },
"b_operation": { "component": "input", "operator": "like" },
"b_business_no": { "component": "input", "operator": "like" },
"b_event_code": { "component": "input", "operator": "like" },
"b_trace_id": { "component": "input", "operator": "like" },
"b_data_id": { "component": "input", "operator": "like" }
}
}', 1);
GO
/* 6.3 技术日志 */
insert into dbo.s_module_schema (b_module_id, b_schema_type, b_schema_json, b_canuse)
values ('v_s_log_technical', 'view', N'{
"schemaVersion": 1,
"columns": [
{ "field": "b_occurdatetime", "width": 170 },
{ "field": "b_loglevel", "width": 90 },
{ "field": "b_logcategory", "width": 130 },
{ "field": "b_operation", "width": 140 },
{ "field": "b_module_id", "width": 150, "visible": false },
{ "field": "v_user_name", "width": 120 },
{ "field": "b_duration_ms", "width": 100, "visible": false },
{ "field": "b_error_code", "width": 120 },
{ "field": "b_error_message", "width": 260 }
]
}', 1),
('v_s_log_technical', 'query', N'{
"schemaVersion": 2,
"defaultViewCode": "all",
"views": [
{ "code": "all", "label": "全部", "order": 10 },
{ "code": "today", "label": "今天", "order": 20,
"conditions": [ { "field": "b_occurdatetime", "operator": "between", "value": { "preset": "today" } } ] },
{ "code": "error", "label": "有错误", "order": 30, "showCount": true,
"conditions": [ { "field": "b_error_code", "operator": "is_not_null" } ] }
],
"keywordSearch": {
"placeholder": "搜索分类、错误码、追踪号",
"trigger": "enter_or_debounce",
"fields": ["b_logcategory", "b_error_code", "b_trace_id"]
},
"quickFilters": [
{ "field": "b_occurdatetime", "order": 10 },
{ "field": "b_loglevel", "order": 20 }
],
"filterGroups": [
{ "code": "basic", "label": "基础信息", "order": 10, "fields": ["b_logcategory", "b_operation", "b_module_id", "v_user_name"] },
{ "code": "error", "label": "错误排查", "order": 20, "fields": ["b_error_code", "b_error_message", "b_trace_id"] }
],
"queryFields": {
"b_occurdatetime": { "component": "datetime", "operator": "between" },
"b_loglevel": { "component": "input", "operator": "eq" },
"b_logcategory": { "component": "input", "operator": "like" },
"b_operation": { "component": "input", "operator": "like" },
"b_module_id": { "component": "input", "operator": "like" },
"v_user_name": { "component": "input", "operator": "like" },
"b_error_code": { "component": "input", "operator": "like" },
"b_error_message": { "component": "input", "operator": "like" },
"b_trace_id": { "component": "input", "operator": "like" }
}
}', 1);
GO
/* ----------------------------------------------------------------------------
7. 菜单(s_menu)
业务菜单通常由「菜单管理」维护,这里随脚本一并初始化(幂等):
系统管理 → 日志(目录)→ 登录日志 / 操作日志。
路由地址与前端 routes.js 的 /system/log/ 下各页面一一对应。
技术日志默认不进菜单:它的受众是开发 / 运维,字段(trace_id、服务节点、
错误堆栈)对业务用户没有意义;需要时按下面第 3 行同样的写法补一条即可。
---------------------------------------------------------------------------- */
if not exists (select 1 from dbo.s_menu where b_id = 'log')
begin
insert into dbo.s_menu (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_menu_type, b_route, b_icon, b_xh, b_canuse)
values ('log', 'system', 1, '/system/log/', N'日志', 'menu.log', 'directory', null, null, 20, 1);
end
GO
if not exists (select 1 from dbo.s_menu where b_id = 'log_login')
begin
insert into dbo.s_menu (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_menu_type, b_route, b_icon, b_xh, b_canuse)
values ('log_login', 'log', 2, '/system/log/log_login/', N'登录日志', 'menu.log_login', 'page', '/system/log/login', null, 10, 1);
end
GO
if not exists (select 1 from dbo.s_menu where b_id = 'log_audit')
begin
insert into dbo.s_menu (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_menu_type, b_route, b_icon, b_xh, b_canuse)
values ('log_audit', 'log', 2, '/system/log/log_audit/', N'操作日志', 'menu.log_audit', 'page', '/system/log/audit', null, 20, 1);
end
GO
-- 技术日志(默认不启用;需要时取消注释)
-- if not exists (select 1 from dbo.s_menu where b_id = 'log_technical')
-- begin
-- insert into dbo.s_menu (b_id, b_parent_id, b_depth, b_path, b_name, b_i18n, b_menu_type, b_route, b_icon, b_xh, b_canuse)
-- values ('log_technical', 'log', 2, '/system/log/log_technical/', N'技术日志', 'menu.log_technical', 'page', '/system/log/technical', null, 30, 1);
-- end
-- GO
/* ----------------------------------------------------------------------------
8. 多语言(s_i18n):模块名与菜单名,默认语言为中文时写入中文值
资源键命名见设计文档第 11 节:模块名 module.*、菜单名 menu.*。
已存在同键同语言的行不覆盖(人工翻译优先)。
---------------------------------------------------------------------------- */
declare @i18n table (k varchar(150) primary key, v nvarchar(1000) not null);
insert into @i18n (k, v) values
('module.log', N'日志'),
('module.v_s_log_login', N'登录日志'),
('module.v_s_log_audit', N'操作日志'),
('module.v_s_log_technical', N'技术日志'),
('menu.log', N'日志'),
('menu.log_login', N'登录日志'),
('menu.log_audit', N'操作日志');
insert into dbo.s_i18n (b_key, b_locale, b_value, b_canuse)
select i.k, t.b_id, i.v, 1
from @i18n i
cross join (select b_id from dbo.s_i18n_type where b_default = 1) t
where not exists (select 1 from dbo.s_i18n x where x.b_key = i.k and x.b_locale = t.b_id);
GO
/* ----------------------------------------------------------------------------
9. 自检
---------------------------------------------------------------------------- */
select '模块' as 项, count(*) as 行数 from dbo.s_module where b_path like '/system/log/%' or b_id = 'log'
union all
select '字段', count(*) from dbo.s_field where b_module_id in ('v_s_log_login', 'v_s_log_audit', 'v_s_log_technical')
union all
select '界面配置', count(*) from dbo.s_module_schema where b_module_id in ('v_s_log_login', 'v_s_log_audit', 'v_s_log_technical')
union all
select '菜单', count(*) from dbo.s_menu where b_path like '/system/log/%' or b_id = 'log'
union all
select '登录日志行数', count(*) from dbo.s_log_login
union all
select '操作日志行数', count(*) from dbo.s_log_audit
union all
select '技术日志行数', count(*) from dbo.s_log_technical;
GO