/* ============================================================================ 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