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()
|