云
2 天以前 a87fcc60346dfc799bd3c2cddc7acfffb105cb61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import sys,json,csv
from pathlib import Path
from datetime import datetime
from collections import Counter
P=Path(__file__).parent
sys.path.insert(0,str(P.parent/'iqc-import-20261012/deps'))
import pymysql
s=json.loads((P/'before-log-business-actors.json').read_text(encoding='utf8'))
old_records=json.loads((P/'operation-log-supplement-records.json').read_text(encoding='utf8'))
record_by_trace={r['trace_id']:r for r in old_records}
roles={'mes_qc_iqc':('inspector_user_id','检验人员'),
       'mes_qc_oqc':('inspector_user_id','检验人员'),
       'crm_contract':('owner_user_id','合同负责人'),
       'erp_sale_order':('sale_user_id','销售员')}
encode=lambda r:json.dumps(r,ensure_ascii=False,default=str,sort_keys=True)
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)
result_rows=[];changes=[];unresolved=[]
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 id,nickname FROM system_users WHERE deleted=0')
  users={str(r['id']):r for r in q.fetchall()}
  q.execute("SELECT * FROM system_operate_log WHERE trace_id LIKE 'BF-META-V1:%' FOR UPDATE")
  logs=q.fetchall();by_trace={r['trace_id']:r for r in logs}
  assert len(logs)==len(s['logs'])==len(old_records)==113
  assert all(encode(by_trace[r['trace_id']])==encode(r) for r in s['logs']), 'Supplemented logs changed since snapshot'
  q.execute('SELECT * FROM system_operate_log ORDER BY id');before=q.fetchall()
  (P/'before-operation-log-actor-correction.json').write_text(encode(before),encoding='utf8')
  for log in logs:
   trace=log['trace_id'];old=record_by_trace[trace];extra=json.loads(log['extra'])
   table=extra['source_table'];biz_id=log['biz_id']
   actor_field,role=roles.get(table,('creator','建档人员(业务表未设专用人员字段)'))
   time_field='inspect_date' if table in ['mes_qc_iqc','mes_qc_oqc'] else 'create_time'
   q.execute('SELECT `'+actor_field+'` actor, `'+time_field+'` event_time FROM `'+table+'` WHERE id=%s AND deleted=0 FOR SHARE',(biz_id,))
   source=q.fetchone()
   if not source or str(source['actor']) not in users or source['event_time'] is None:
    unresolved.append({'trace_id':trace,'source_table':table,'biz_id':biz_id,'reason':'业务人员或时间无法确认'})
    continue
   user=users[str(source['actor'])];name=user['nickname'].strip();event=source['event_time']
   if not isinstance(event,datetime):event=datetime.combine(event,datetime.min.time())
   assert event<=now
   is_qc=time_field=='inspect_date'
   when=event.strftime('%Y-%m-%d') if is_qc else str(event)
   subtype='历史数据补录(检验记录)' if is_qc else '历史数据补录(业务归属)'
   extra.update({'reconstructed':True,'evidence':'business_record_role_and_date' if is_qc else 'business_record_actor_field',
      'actor_source_field':actor_field,'business_actor_user_id':user['id'],'business_actor_role':role,
      'event_time_source_field':time_field,'event_time_precision':'day' if is_qc else 'second',
      'corrected_at':str(now),'attribution':'依据业务人员字段归属,不证明原始请求操作者'})
   action=(f'【历史数据补录/业务记录归属】{log["type"]}记录“{old["source_name"]}”(ID:{biz_id});'
           f'{role}:{name}({user["id"]}),依据字段:{actor_field};'
           f'{"检测日期" if is_qc else "建档时间"}:{when}。'
           +('原数据仅有日期,日志零点为日期展示值,不代表实际操作时刻。' if is_qc 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,create_time=%s,update_time=%s WHERE id=%s AND trace_id=%s',
     (user['id'],subtype,action,encode(extra),event,now,log['id'],trace))
   assert q.rowcount==1
   updated=dict(old,user_id=user['id'],sub_type=subtype,action=action,extra=encode(extra),create_time=str(event),update_time=str(now),source_user_name=name,business_role=role,actor_source_field=actor_field,event_time_precision=extra['event_time_precision'])
   result_rows.append(updated)
   changes.append({'log_id':log['id'],'business_table':table,'business_id':biz_id,'before_user_id':log['user_id'],'after_user_id':user['id'],'actor':name,'role':role,'actor_source_field':actor_field,'before_time':str(log['create_time']),'after_time':str(event),'date_precision':extra['event_time_precision']})
  assert not unresolved,encode(unresolved)
  q.execute('SELECT * FROM system_operate_log ORDER BY id');after={r['id']:r for r in q.fetchall()}
  target_ids={r['id'] for r in logs}
  assert all(encode(r)==encode(after[r['id']]) for r in before if r['id'] not in target_ids), 'Original logs changed'
  assert all(after[x['log_id']]['user_id']==x['after_user_id'] and str(after[x['log_id']]['create_time'])==x['after_time'] for x in changes)
  c.commit()
  result={'updated_supplemented_logs':len(changes),'changed_actor_count':sum(r['before_user_id']!=r['after_user_id'] for r in changes),
          'changed_time_count':sum(r['before_time']!=r['after_time'] for r in changes),'by_business_actor':dict(Counter(r['actor'] for r in changes)),
          'by_role':dict(Counter(r['role'] for r in changes)),'unresolved':unresolved,'original_logs_unchanged':len(before)-len(logs),'corrected_at':str(now)}
  (P/'operation-log-actor-correction-result.json').write_text(encode(result),encoding='utf8')
  (P/'operation-log-actor-corrections.json').write_text(encode(changes),encoding='utf8')
  (P/'operation-log-supplement-records.json').write_text(encode(result_rows),encoding='utf8')
  with (P/'操作日志补录清单.csv').open('w',encoding='utf-8-sig',newline='') as f:
   w=csv.writer(f);w.writerow(['模块','业务ID','业务编号或名称','业务人员','人员ID','人员角色','人员来源字段','日志显示时间','时间精度','实际补录时间','日志类型','来源表','追踪编号'])
   for r in result_rows: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['create_time'],r['event_time_precision'],r['recorded_at'],r['sub_type'],r['source_table'],r['trace_id']])
  print(encode(result))
finally:c.close()