404 lines
19 KiB
Transact-SQL
404 lines
19 KiB
Transact-SQL
/* ============================================================================
|
||
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 },
|
||
{ "field": "b_os", "width": 110 },
|
||
{ "field": "b_device_type", "width": 100 }
|
||
]
|
||
}', 1),
|
||
('v_s_log_login', 'query', N'{
|
||
"schemaVersion": 1,
|
||
"groups": [
|
||
{ "conditions": [
|
||
{ "field": "b_occurdatetime", "operator": "between" },
|
||
{ "field": "v_user_name", "operator": "like" },
|
||
{ "field": "v_login_result", "operator": "eq" },
|
||
{ "field": "b_ip", "operator": "like" }
|
||
] }
|
||
],
|
||
"quick": ["b_occurdatetime", "v_user_name"]
|
||
}', 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 },
|
||
{ "field": "b_visibility", "width": 100 },
|
||
{ "field": "b_data_id", "width": 140 }
|
||
]
|
||
}', 1),
|
||
('v_s_log_audit', 'query', N'{
|
||
"schemaVersion": 1,
|
||
"groups": [
|
||
{ "conditions": [
|
||
{ "field": "b_occurdatetime", "operator": "between" },
|
||
{ "field": "b_operator_name", "operator": "like" },
|
||
{ "field": "b_module_id", "operator": "like" },
|
||
{ "field": "b_operation", "operator": "like" },
|
||
{ "field": "b_business_no", "operator": "like" },
|
||
{ "field": "b_trace_id", "operator": "like" }
|
||
] }
|
||
],
|
||
"quick": ["b_occurdatetime", "b_operator_name", "b_business_no"]
|
||
}', 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 },
|
||
{ "field": "v_user_name", "width": 120 },
|
||
{ "field": "b_duration_ms", "width": 100 },
|
||
{ "field": "b_error_code", "width": 120 },
|
||
{ "field": "b_error_message", "width": 260 }
|
||
]
|
||
}', 1),
|
||
('v_s_log_technical', 'query', N'{
|
||
"schemaVersion": 1,
|
||
"groups": [
|
||
{ "conditions": [
|
||
{ "field": "b_occurdatetime", "operator": "between" },
|
||
{ "field": "b_loglevel", "operator": "eq" },
|
||
{ "field": "b_logcategory", "operator": "like" },
|
||
{ "field": "b_operation", "operator": "like" },
|
||
{ "field": "b_error_code", "operator": "like" },
|
||
{ "field": "b_trace_id", "operator": "like" }
|
||
] }
|
||
],
|
||
"quick": ["b_occurdatetime", "b_logcategory"]
|
||
}', 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
|