"""Import reviewed native MES process masters and disabled routes, no backflush.""" import sys,json,datetime,unicodedata,re,csv from pathlib import Path P=Path(__file__).parent sys.path.insert(0,str(P.parent/'iqc-import-20261012/deps')) import pymysql plan=json.loads((P/'route-plan.json').read_text(encoding='utf8')) snapshot=json.loads((P/'remote-snapshot.json').read_text(encoding='utf8')) assert len(plan['processes'])==46 and len(plan['routes'])==28 assert all(not st['backflush_flag'] and (not st['starred'] or st['check_flag']) for r in plan['routes'] for st in r['steps']) assert all(sum(st['key_flag'] for st in r['steps'])==1 for r in plan['routes']) def dump(n,v): (P/n).write_text(json.dumps(v,ensure_ascii=False,indent=2,default=str),encoding='utf8') def normal(n):return re.sub(r'\s+','',unicodedata.normalize('NFKC',n)) def same(old,new): for k,v in new.items(): actual=old[k] if isinstance(actual,bytes):actual=int.from_bytes(actual,'big') if actual!=v:return False return 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,connect_timeout=10,read_timeout=60) log=[];newprocess=[];newroutes=[];newcontents=0;newsteps=0 try: with c.cursor() as q: q.execute('SELECT DATABASE() db');assert q.fetchone()['db']=='mom-hm' q.execute("SELECT GET_LOCK('stp-routes-20261012',10) ok");assert q.fetchone()['ok']==1 c.begin();before={} tables=['mes_pro_process','mes_pro_process_content','mes_pro_route','mes_pro_route_process','mes_pro_route_product','mes_pro_route_product_bom'] for t in tables: q.execute('SELECT ENGINE FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=%s',(t,));assert q.fetchone()['ENGINE']=='InnoDB' q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id FOR UPDATE');before[t]=list(q.fetchall()) dump('before-route-import.json',before) for t in tables:assert [(x['id'],x.get('code')) for x in before[t]]==[(x['id'],x.get('code')) for x in sorted(snapshot[t],key=lambda x:x['id'])], 'Remote data changed; review again' def insert(t,values): cols=list(values);sql='INSERT INTO '+t+' (`'+'`,`'.join(cols)+"`,creator,updater) VALUES ("+','.join(['%s']*len(cols))+",'1','1')" args=tuple(values[k] for k in cols);log.append(q.mogrify(sql,args)+';');q.execute(sql,args);return q.lastrowid def insert_many(t,values): if not values:return cols=list(values[0]);sql='INSERT INTO '+t+' (`'+'`,`'.join(cols)+"`,creator,updater) VALUES ("+','.join(['%s']*len(cols))+",'1','1')" args=[tuple(v[k] for k in cols) for v in values] log.extend(q.mogrify(sql,a)+';' for a in args);q.executemany(sql,args) processes={normal(x['name']):x for x in before['mes_pro_process']} codes={x['code']:x for x in before['mes_pro_process']} nameids={} for m in plan['processes']: existing=processes.get(normal(m['name'])) if existing: m['id']=existing['id'];m['action']='复用现有工序' else: assert m['code'] not in codes,'Process code occupied' m['id']=insert('mes_pro_process',dict(code=m['code'],name=m['name'],attention=m['attention'],status=0, remark='来源:生产工艺规程工序去重;参数按原文产品和版本执行。')) newprocess.append(m['id']);m['action']='新增工序' contentrows=[] for idx,ev in enumerate(m['contents'],1): prefix='【'+ev['document_code']+';段落'+str(ev['paragraph'])+'】' raw=ev['text'];parts=[raw[i:i+500-len(prefix)] for i in range(0,len(raw),500-len(prefix))] for part in parts:contentrows.append(dict(process_id=m['id'],sort=len(contentrows)+1,content=prefix+part, remark='原文摘录;仅适用所列产品规程。共同工序的不同版本参数不合并。')) if not contentrows:contentrows.append(dict(process_id=m['id'],sort=1,content='来源流程图:'+m['name']+'。详细参数以对应原规程确认。',remark='来源:'+','.join(m['sources']))) insert_many('mes_pro_process_content',contentrows);newcontents+=len(contentrows) nameids[m['name']]=m['id'] oldroutes={x['code']:x for x in before['mes_pro_route']} for r in plan['routes']: notes=';'.join(r['issues']) description='来源:'+r['document_code']+';保留原文工序顺序,全部非倒冲、手动领料;未启用,待核实产品/BOM和参数。' if r['document_id']==15:description+='红、黑枸杞分支独立制浆,在搅拌乳化处合流。' header=dict(code=r['code'],name=r['name'],description=description,status=1,remark=(notes or '按规程生成;系统唯一产出关键工序用于数量统计,原文*控制点逐项设置需要质检。')) # Long exception text stays complete in JSON/CSV; database remark gives a pointer. if len(header['remark'])>255:header['remark']=header['remark'][:220]+';完整事项见来源规程及生成清单。' assert len(header['name'])<=100 and len(header['description'])<=255 old=oldroutes.get(r['code']) if old:assert same(old,header);r['id']=old['id'];r['action']='复用现有路线' else:r['id']=insert('mes_pro_route',header);newroutes.append(r['id']);r['action']='新增未启用路线' values=[] for i,st in enumerate(r['steps']): st['process_id']=nameids[st['name']] nextpid=nameids[r['steps'][i+1]['name']] if i+120:remark+=';'+parameter[:room] assert len(remark)<=255 value=dict(route_id=r['id'],process_id=st['process_id'],sort=st['sort'],next_process_id=nextpid, link_type=3,prepare_time=None,wait_time=None,key_flag=int(st['key_flag']),check_flag=int(st['check_flag']), backflush_flag=0,remark=remark) values.append(value);st['stored']=value if old: q.execute('SELECT * FROM mes_pro_route_process WHERE route_id=%s AND deleted=0 ORDER BY sort',(r['id'],));existing=q.fetchall() assert len(existing)==len(values) and all(same(a,b) for a,b in zip(existing,values)) else:insert_many('mes_pro_route_process',values);newsteps+=len(values) # No products, BOM, or existing route rows altered by this transaction. for t in ['mes_pro_route_product','mes_pro_route_product_bom']: q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id');assert list(q.fetchall())==before[t] for row in before['mes_pro_route']: q.execute('SELECT * FROM mes_pro_route WHERE id=%s',(row['id'],));assert q.fetchone()==row dump('route-plan-with-ids.json',plan) (P/'route-import-executed.sql').write_text('START TRANSACTION;\n'+'\n'.join(log)+'\nCOMMIT;\n',encoding='utf8') c.commit() for r in plan['routes']: q.execute('SELECT * FROM mes_pro_route WHERE id=%s',(r['id'],));row=q.fetchone();assert row['status']==1 and row['code']==r['code'] q.execute('SELECT * FROM mes_pro_route_process WHERE route_id=%s AND deleted=0 ORDER BY sort',(r['id'],));found=q.fetchall() assert len(found)==len(r['steps']) and all(same(a,b['stored']) for a,b in zip(found,r['steps'])) for t,n in [('mes_pro_process',len(newprocess)),('mes_pro_process_content',newcontents),('mes_pro_route',len(newroutes)),('mes_pro_route_process',newsteps)]: q.execute('SELECT COUNT(*) n FROM '+t+' WHERE deleted=0');assert q.fetchone()['n']==len(before[t])+n result=dict(target='36.213.150.237:10000/mom-hm',source_files=26,unique_documents=24,duplicates=plan['duplicate_documents'], new_processes=len(newprocess),new_process_contents=newcontents,new_routes=len(newroutes),new_route_steps=newsteps, starred_check_steps=sum(st['starred'] for r in plan['routes'] for st in r['steps']), check_steps=sum(st['check_flag'] for r in plan['routes'] for st in r['steps']),all_backflush_false=True, route_status='未启用',products_bound=0,existing_test_route_preserved=True,post_commit_verified=True) dump('route-import-result.json',result) with (P/'工艺路线生成结果.csv').open('w',encoding='utf-8-sig',newline='') as f: w=csv.writer(f);w.writerow(['路线ID','路线编码','路线名称','来源文件编号','工序数','需质检数','星号质检数','倒冲','状态','工序顺序','待核实事项']) for r in plan['routes']:w.writerow([r['id'],r['code'],r['name'],r['document_code'],len(r['steps']),sum(st['check_flag'] for st in r['steps']),sum(st['starred'] for st in r['steps']),False,'未启用',' → '.join(st['name']+('*' if st['starred'] else '') for st in r['steps']),';'.join(r['issues']+r['binding_issues'])]) print(json.dumps(result,ensure_ascii=False)) q.execute("SELECT RELEASE_LOCK('stp-routes-20261012')") except Exception:c.rollback();raise finally:c.close()