68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
"""审计日志 — append-only 通用审计原语。
|
||
|
||
审计粒度(什么该记、记到什么程度)在 skill 里说明;本模块只提供 record_audit
|
||
通用原语,所有概念能力(task/question/deliverable)的状态迁移/流转都调它记一条。
|
||
|
||
设计约束:
|
||
- append-only:只 INSERT,不 UPDATE/DELETE(审计独立性,防自删)。
|
||
- 租户隔离:每条必带 tenant_id。
|
||
- sor 参数可选:在已有 sqlorContext 里调用时传入避免嵌套开连接。
|
||
"""
|
||
|
||
import logging
|
||
from datetime import datetime
|
||
from sqlor.dbpools import DBPools
|
||
from appPublic.uniqueID import getID
|
||
|
||
DBNAME = "pipeline"
|
||
logger = logging.getLogger("pipeline.audit")
|
||
|
||
|
||
def _get_db():
|
||
db = DBPools()
|
||
if not db.databases:
|
||
from appPublic.jsonConfig import getConfig
|
||
config = getConfig()
|
||
if config.databases:
|
||
db.databases = config.databases
|
||
return db, DBNAME
|
||
|
||
|
||
async def record_audit(tenant_id, entity, entity_id, action,
|
||
from_state=None, to_state=None, who=None,
|
||
agent_id=None, detail=None, sor=None):
|
||
"""追加一条审计记录(只追加不改删)。
|
||
|
||
Args:
|
||
tenant_id: 租户ID(必带,隔离)
|
||
entity: 实体类型(如 pipeline_tasks / pipeline_agent_questions / pipeline_deliverables)
|
||
entity_id: 实体ID
|
||
action: 动作(如 claim/submit/approve/reject/raise/escalate/resolve/set_state)
|
||
from_state / to_state: 状态迁移前后(可空)
|
||
who: 操作角色(agent.{role} 或 {orgtype}.{role})
|
||
agent_id: 具体 agent 标识
|
||
detail: 附加详情
|
||
sor: 可选的 sqlor context,传入则复用(避免嵌套开连接)
|
||
"""
|
||
data = {
|
||
'id': getID(),
|
||
'tenant_id': tenant_id or '',
|
||
'entity': entity or '',
|
||
'entity_id': entity_id or '',
|
||
'action': action or '',
|
||
'from_state': from_state,
|
||
'to_state': to_state,
|
||
'who': who,
|
||
'agent_id': agent_id,
|
||
'detail': detail,
|
||
# 微秒精度,保证 append-only 轨迹可精确排序(秒级同秒内会乱序)
|
||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'),
|
||
}
|
||
if sor is not None:
|
||
await sor.C('audit_log', data)
|
||
return True
|
||
db, dbname = _get_db()
|
||
async with db.sqlorContext(dbname) as s:
|
||
await s.C('audit_log', data)
|
||
return True
|