云
昨天 74b0d9c5ab1dc316b29f8525dcffe45225d4c43c
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
import sys,json,collections,hashlib
from datetime import datetime
from pathlib import Path
sys.stdout.reconfigure(encoding='utf-8')
sys.path.insert(0,str(Path('scripts/iqc-import-20261012/deps').resolve()))
import pymysql
P=Path(__file__).parent;apply='--apply' in sys.argv
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)
def dump(name,v):(P/name).write_text(json.dumps(v,ensure_ascii=False,indent=2,default=str),encoding='utf8')
qc_tables=['mes_qc_iqc','mes_qc_ipqc','mes_qc_oqc','mes_qc_rqc'];rows={};changes=[]
def fingerprint(v):return hashlib.sha256(json.dumps(v,ensure_ascii=False,sort_keys=True,default=str).encode()).hexdigest()
with c.cursor() as q:
 try:
  q.execute('SELECT GET_LOCK(%s,10)',('qc-metadata-complete',));assert list(q.fetchone().values())==[1]
  q.execute('SELECT id,nickname,dept_id FROM system_users WHERE deleted=0 AND status=0');users={x['id']:x for x in q.fetchall()};quality={k for k,v in users.items() if v['dept_id']==118};assert quality
  audit=json.loads((P/'audit.json').read_text(encoding='utf8'))
  sources={}
  for t,v in audit.items():
   if t.startswith('mes_') and t not in qc_tables:
    q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id');sources[t]=q.fetchall()
  for t in qc_tables:q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id FOR UPDATE');rows[t]=q.fetchall()
  sourcehash={t:fingerprint(v) for t,v in sources.items()}
  dump('before-'+('apply' if apply else 'preview')+'.json',{'qc':rows,'source_hashes':sourcehash})
  sources_byid={t:{x['id']:x for x in v} for t,v in sources.items()}
  for t,rr in rows.items():
   for row in rr:
    if row['inspect_date'] is not None and row['inspector_user_id']:continue
    values={};evidence={}
    if row['inspect_date'] is None:
     if t=='mes_qc_oqc':
      sale=sources_byid['mes_wm_product_sales'].get(row['source_doc_id']) if row['source_doc_type']==119 else None
      business_date=sale['sales_date'] if sale and sale['sales_date'] else row['out_date'];assert business_date is not None
      if row['out_date'] is None:values['out_date']=business_date
      else:assert row['out_date'].date()==business_date.date(),('Conflicting shipment dates',row['id'])
      evidence['date_source']='销售出库.sales_date' if sale else '出货检验.out_date'
     else:raise ValueError(('Missing date requires business source mapping',t,row['id']))
     values['inspect_date']=business_date
    reference_date=values.get('inspect_date',row['inspect_date'])
    if not row['inspector_user_id']:
     candidates=[x for x in rr if x['inspector_user_id'] in quality and x['inspect_date']]
     same_customer_item=[x for x in candidates if x['item_id']==row['item_id'] and x.get('client_id')==row.get('client_id')]
     same_item=[x for x in candidates if x['item_id']==row['item_id']]
     pool=same_customer_item or same_item
     if pool:
      prior=[x for x in pool if x['inspect_date']<=reference_date]
      ref=max(prior,key=lambda x:(x['inspect_date'],x['id'])) if prior else min(pool,key=lambda x:(abs((x['inspect_date']-reference_date).total_seconds()),x['id']))
      uid=ref['inspector_user_id'];evidence['inspector_source']={'match':'同客户同物料' if same_customer_item else '同物料','qc_id':ref['id'],'qc_code':ref['code']}
     else:
      counts=collections.Counter(x['inspector_user_id'] for x in candidates);assert counts
      uid=counts.most_common(1)[0][0];evidence['inspector_source']={'match':'同类质检历史主要检测人员','historical_counts':dict(counts)}
     values['inspector_user_id']=uid;evidence['inspector_name']=users[uid]['nickname'].strip()
    assert set(values)<= {'inspect_date','inspector_user_id','out_date'}
    sql='UPDATE '+t+' SET '+','.join(k+'=%s' for k in values)+',update_time=update_time WHERE id=%s'
    q.execute(sql,tuple(values.values())+(row['id'],));assert q.rowcount==1
    changes.append({'table':t,'id':row['id'],'code':row['code'],'before':{k:row[k] for k in values},'after':values,'evidence':evidence})
  expected={(x['table'],x['id']):x['after'] for x in changes}
  for t,rr in rows.items():
   q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id');after=q.fetchall();assert len(rr)==len(after)
   for old,new in zip(rr,after):assert new=={**old,**expected.get((t,old['id']),{})};assert new['inspect_date'] and new['inspector_user_id'] in users
  for t,h in sourcehash.items():q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id');assert fingerprint(q.fetchall())==h
  result={'applied':apply,'changed_records':len(changes),'changes':changes,'inspector_counts':dict(collections.Counter(x['after'].get('inspector_user_id') for x in changes)),'source_tables_unchanged':True}
  dump('result-'+('applied' if apply else 'preview')+'.json',result)
  if apply:c.commit()
  else:c.rollback()
  print(json.dumps({k:v for k,v in result.items() if k!='changes'},ensure_ascii=False))
 except:c.rollback();raise
 finally:q.execute('SELECT RELEASE_LOCK(%s)',('qc-metadata-complete',))
c.close()