"""Assign only our reconstructed logs to a requested current role; retain source evidence."""
|
import sys,json,csv
|
from pathlib import Path
|
from collections import Counter
|
P=Path(__file__).parent
|
sys.path.insert(0,str(P.parent/'iqc-import-20261012/deps'))
|
import pymysql
|
encode=lambda r:json.dumps(r,ensure_ascii=False,default=str,sort_keys=True)
|
snapshot=json.loads((P/'before-log-production-role.json').read_text(encoding='utf8'))
|
records=json.loads((P/'operation-log-supplement-records.json').read_text(encoding='utf8'))
|
bytrace={r['trace_id']:r for r in records}
|
c=pymysql.connect(host='36.213.150.237',port=10000,user='root',password=sys.stdin.readline().strip(),database='mom-hm',charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor,autocommit=False)
|
changes=[]
|
try:
|
with c.cursor() as q:
|
q.execute("SELECT GET_LOCK('codex-business-metadata-log-v1',0) locked");assert q.fetchone()['locked']==1
|
q.execute('SELECT NOW() now');now=q.fetchone()['now']
|
q.execute('SELECT u.id,u.nickname,r.id role_id,r.name role_name FROM system_users u JOIN system_user_role ur ON ur.user_id=u.id JOIN system_role r ON r.id=ur.role_id WHERE u.deleted=0 AND u.status=0 AND ur.deleted=0 AND r.deleted=0 AND r.status=0 AND r.name=%s AND u.tenant_id=0 AND r.tenant_id=0 FOR SHARE',('生产管理员',))
|
members={r['id']:r for r in q.fetchall()}
|
# Keep attribution to an existing source business account rather than distributing invented operators.
|
assert 244 in members, 'Existing business account is no longer an enabled production administrator'
|
member=members[244];name=member['nickname'].strip()
|
q.execute('SELECT * FROM system_operate_log ORDER BY id');before=q.fetchall()
|
(P/'before-operation-log-role-assignment.json').write_text(encode(before),encoding='utf8')
|
q.execute("SELECT * FROM system_operate_log WHERE trace_id LIKE 'BF-META-V1:%' FOR UPDATE")
|
logs=q.fetchall();snapshot_bytrace={r['trace_id']:r for r in snapshot['logs']}
|
assert len(logs)==len(records)==113
|
assert all(encode(r)==encode(snapshot_bytrace[r['trace_id']]) for r in logs), 'Supplemented logs changed since snapshot'
|
for log in logs:
|
if not log['type'].startswith(('MES ','MDM ')):continue
|
record=bytrace[log['trace_id']];extra=json.loads(log['extra'])
|
source_name=record['source_user_name'];source_id=extra.get('business_actor_user_id',log['user_id'])
|
source_role=extra.get('business_actor_role',record.get('business_role','建档人员'))
|
extra.update({'role_assignment':{'requested_role':'生产管理员','role_id':member['role_id'],
|
'assigned_user_id':member['id'],'assigned_user_name':name,'assigned_at':str(now),
|
'basis':'用户指定角色归属;采用既有业务建档账号;非历史请求操作者证明'},
|
'source_business_actor_user_id':source_id,'source_business_actor_name':source_name,
|
'source_business_actor_role':source_role,'attribution':'日志按当前生产管理员角色归属;保留原业务人员字段,非原始请求日志'})
|
date_precision=extra.get('event_time_precision','second')
|
when=str(log['create_time'])[:10] if date_precision=='day' else str(log['create_time'])
|
date_label='检测日期' if date_precision=='day' else '源建档时间'
|
action=(f'【历史数据补录/指定角色归属】{log["type"]}记录“{record["source_name"]}”(ID:{log["biz_id"]});'
|
f'日志归属人员:{name}({member["id"]}),当前角色:生产管理员;'
|
f'源业务人员:{source_name}({source_id},{source_role});{date_label}:{when}。'
|
+('原数据仅有日期,日志零点不代表实际操作时刻。' if date_precision=='day' else '')
|
+f'实际补录时间:{extra["recorded_at"]},角色归属调整时间:{now}。'
|
+'依据用户要求分配日志归属,保留业务人员原值;非原始操作请求日志,不证明历史实际操作者。')
|
assert len(action)<=2000 and len(encode(extra))<=2000
|
q.execute('UPDATE system_operate_log SET user_id=%s,sub_type=%s,action=%s,extra=%s,update_time=%s WHERE id=%s AND trace_id=%s',
|
(member['id'],'历史数据补录(角色归属)',action,encode(extra),now,log['id'],log['trace_id']))
|
assert q.rowcount==1
|
record.update(user_id=member['id'],source_user_name=name,business_role='生产管理员(日志归属)',
|
actor_source_field='system_user_role.role_id='+str(member['role_id']),sub_type='历史数据补录(角色归属)',
|
action=action,extra=encode(extra),update_time=str(now),original_business_actor_name=source_name,
|
original_business_actor_user_id=source_id,original_business_actor_role=source_role)
|
changes.append({'log_id':log['id'],'trace_id':log['trace_id'],'module':log['type'],
|
'before_user_id':log['user_id'],'after_user_id':member['id'],'source_business_actor':source_name})
|
assert len(changes)==93
|
q.execute('SELECT * FROM system_operate_log ORDER BY id');after={r['id']:r for r in q.fetchall()}
|
changed_ids={r['log_id'] for r in changes}
|
assert all(encode(r)==encode(after[r['id']]) for r in before if r['id'] not in changed_ids)
|
assert all(after[r['log_id']]['user_id'] in members for r in changes)
|
assert all(encode(after[r['id']]['create_time'])==encode(r['create_time']) for r in before)
|
c.commit()
|
result={'updated_logs':len(changes),'changed_user_count':sum(r['before_user_id']!=r['after_user_id'] for r in changes),
|
'assigned_user_id':member['id'],'assigned_user_name':name,'role_id':member['role_id'],
|
'by_module':dict(Counter(r['module'] for r in changes)),'untouched_logs':len(before)-len(changes),
|
'source_business_data_modified':False,'log_times_unchanged':True,'assigned_at':str(now)}
|
(P/'operation-log-role-assignment-result.json').write_text(encode(result),encoding='utf8')
|
(P/'operation-log-role-assignment-changes.json').write_text(encode(changes),encoding='utf8')
|
(P/'operation-log-supplement-records.json').write_text(encode(records),encoding='utf8')
|
with (P/'操作日志补录清单.csv').open('w',encoding='utf-8-sig',newline='') as f:
|
w=csv.writer(f);w.writerow(['模块','业务ID','业务编号或名称','日志归属人员','归属人员ID','归属角色','归属依据','源业务人员','源业务人员ID','源业务角色','日志显示时间','时间精度','实际补录时间','日志类型','来源表','追踪编号'])
|
for r in records:w.writerow([r['type'],r['biz_id'],r['source_name'],r['source_user_name'],r['user_id'],r['business_role'],r['actor_source_field'],r.get('original_business_actor_name',r['source_user_name']),r.get('original_business_actor_user_id',r['user_id']),r.get('original_business_actor_role',r['business_role']),r['create_time'],r['event_time_precision'],r['recorded_at'],r['sub_type'],r['source_table'],r['trace_id']])
|
print(encode(result))
|
finally:c.close()
|