From f59e6f3c4569a03c6edecc1f146e969663ad785c Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 18 Aug 2026 16:23:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor(deploy):=20sd=5Faudit=5Flogs=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8=20+=20owner.audit=20=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E4=BB=8E=E5=90=AF=E5=8A=A8=E6=9C=9F=E8=BF=81=E5=88=B0=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E6=9C=9F(create=5Ftables.py=20+=20import=5Finit.py)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/create_tables.py | 26 ++++++++++++++++++++++++++ scripts/import_init.py | 15 +++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/scripts/create_tables.py b/scripts/create_tables.py index 1845201..374e2c2 100644 --- a/scripts/create_tables.py +++ b/scripts/create_tables.py @@ -124,6 +124,25 @@ _V2_COLUMNS = { "pipeline_tasks": {"retry_count": "int NOT NULL DEFAULT 0", "last_error": "text"}, } +# app_audit 审计日志表(原 app_audit/init.py::_init_audit 启动时建,现迁到部署期) +_AUDIT_LOGS_DDL = """ +CREATE TABLE IF NOT EXISTS sd_audit_logs ( + `id` varchar(32) NOT NULL, + `user_id` varchar(32), + `username` varchar(100), + `action` varchar(50) NOT NULL, + `target` varchar(200), + `detail` text, + `result` varchar(10), + `client_ip` varchar(64), + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user` (`user_id`), + KEY `idx_action` (`action`), + KEY `idx_created` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +""" + def split_ddl(ddl): """按分号分割 DDL,跳过注释行和空语句。""" @@ -257,6 +276,13 @@ async def main(): except Exception as e: print(f' WARN: v2 engine column migrate failed: {e}') + # app_audit 审计日志表(原启动时建,现迁到部署期幂等建表) + try: + await sor.execute(_AUDIT_LOGS_DDL, {}) + print(' tables: sd_audit_logs ensured (idempotent)') + except Exception as e: + print(f' WARN: sd_audit_logs ensure failed: {e}') + if __name__ == '__main__': asyncio.run(main()) diff --git a/scripts/import_init.py b/scripts/import_init.py index 8e7a735..cac7bef 100644 --- a/scripts/import_init.py +++ b/scripts/import_init.py @@ -27,6 +27,7 @@ INIT_MODULES = [ ('discount', 'pkgs/discount/init/data.json'), ('pipeline_core', 'pkgs/pipeline_core/init/data.json'), ('pipeline-sdlc', 'pkgs/pipeline-sdlc/init/data.json'), + ('app_audit', 'pkgs/app_audit/init/data.json'), ] @@ -65,7 +66,7 @@ async def main(): env = ServerEnv() env.get_module_dbname = lambda m: 'pipeline' if 'pipeline' in m else 'sage' - total = {'appcodes': 0, 'appcodes_kv': 0, 'organization': 0} + total = {'appcodes': 0, 'appcodes_kv': 0, 'organization': 0, 'roles': 0} async with DBPools().sqlorContext('pipeline') as sor: for mod, rel in INIT_MODULES: f = os.path.join(ROOT_DIR, rel) @@ -105,7 +106,17 @@ async def main(): 'orgname': org.get('orgname', ''), }) total['organization'] += 1 - print(f'import_init: appcodes {total["appcodes"]}, appcodes_kv {total["appcodes_kv"]}, organization {total["organization"]}') + # RBAC 角色(如 app_audit 的 owner.audit 审计独立角色),幂等按主键 id 检查 + for r in data.get('roles', []): + recs = await sor.R('role', {'id': r['id']}) + if not recs: + await sor.C('role', { + 'id': r['id'], + 'orgtypeid': r.get('orgtypeid', '0'), + 'name': r.get('name', ''), + }) + total['roles'] += 1 + print(f'import_init: appcodes {total["appcodes"]}, appcodes_kv {total["appcodes_kv"]}, organization {total["organization"]}, roles {total["roles"]}') if __name__ == '__main__':