gaoluyang
2 天以前 e40a608ef9bf51db294d8eefada6a8e2e80cebca
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
"""User-requested random contract update timestamps, bounded by creation and now."""
import sys,json,secrets
from pathlib import Path
from datetime import timedelta
P=Path(__file__).parent
sys.stdout.reconfigure(encoding='utf8')
sys.path.insert(0,str(P.parent/'iqc-import-20261012/deps'))
import pymysql
c=pymysql.connect(host='36.213.150.237',port=10000,user='root',password=sys.stdin.readline().strip(),database='mom-hm',charset='utf8mb4',autocommit=False,cursorclass=pymysql.cursors.DictCursor)
try:
 with c.cursor() as q:
  q.execute("SET time_zone='+08:00'")
  q.execute('SELECT DATABASE() db,NOW() now,GET_LOCK(%s,10) locked',('sales-ledger-202609',));state=q.fetchone();assert state['db']=='mom-hm' and state['locked']==1;now=state['now'];window_start=now-timedelta(days=10)
  q.execute('SELECT * FROM crm_contract WHERE deleted=0 ORDER BY id FOR UPDATE');before=q.fetchall()
  backup=P/('contract-update-times-before-'+now.strftime('%Y%m%d-%H%M%S')+'.json')
  backup.write_text(json.dumps({'state':state,'contracts':before},ensure_ascii=False,default=str,indent=2),encoding='utf8')
  changes=[];skipped=[];used=set()
  for old in before:
   assert old['create_time'] is not None
   start=max(window_start,old['create_time'])
   if start>now:skipped.append({'id':old['id'],'reason':'创建时间晚于当前时间,无可用区间,保留原值'});continue
   span=int((now-start).total_seconds())
   for attempt in range(100):
    target=start+timedelta(seconds=secrets.randbelow(span+1))
    if target not in used or span==0:break
   assert old['create_time']<=target<=now and target>=window_start
   used.add(target)
   q.execute('UPDATE crm_contract SET update_time=%s WHERE id=%s AND deleted=0',(target,old['id']));assert q.rowcount==1
   changes.append({'id':old['id'],'name':old['name'],'create_time':str(old['create_time']),'old_update_time':str(old['update_time']),'update_time':str(target)})
  q.execute('SELECT * FROM crm_contract WHERE deleted=0 ORDER BY id');after=q.fetchall();expected={x['id']:x for x in changes};assert len(before)==len(after)
  for old,new in zip(before,after):
   assert old['id']==new['id'];diff={k for k in old if old[k]!=new[k]};assert diff<=({'update_time'} if old['id'] in expected else set())
   if old['id'] in expected:assert str(new['update_time'])==expected[old['id']]['update_time'] and max(new['create_time'],window_start)<=new['update_time']<=now
  c.commit()
  q.execute('SELECT * FROM crm_contract WHERE deleted=0 ORDER BY id');assert q.fetchall()==after
  result={'committed':True,'verified':True,'at':str(now),'window_start':str(window_start),'timezone':'Asia/Shanghai','count':len(changes),'changes':changes,'skipped':skipped,'earliest':min((x['update_time'] for x in changes),default=None),'latest':max((x['update_time'] for x in changes),default=None),'backup':backup.name,'other_contract_fields_unchanged':True}
  (P/'contract-update-times-result.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),encoding='utf8')
  print(json.dumps({k:v for k,v in result.items() if k!='changes'},ensure_ascii=False))
except Exception:c.rollback();raise
finally:
 with c.cursor() as q:q.execute('SELECT RELEASE_LOCK(%s)',('sales-ledger-202609',))
 c.close()