"""Refine the visible descriptions of this batch; retain all provenance and log metadata."""
|
import sys,json
|
from pathlib import Path
|
from collections import defaultdict,Counter
|
from decimal import Decimal
|
from read_sources import P,connect,encode
|
|
LABELS={
|
'crm_customer':'客户档案','crm_business':'商机信息','crm_sale_quotation':'销售报价',
|
'crm_contract':'销售合同','crm_invoice':'开票记录','crm_receivable':'回款记录',
|
'crm_receivable_plan':'回款计划','erp_sale_order':'销售订单',
|
'mes_pro_mps':'生产主计划','mes_pro_work_order':'生产工单','mes_pro_task':'生产任务',
|
'mes_pro_feedback':'生产报工','mes_qc_iqc':'来料检验','mes_qc_ipqc':'过程检验',
|
'mes_qc_oqc':'出货检验','mes_wm_sales_notice':'发货通知','mes_wm_product_sales':'销售出库',
|
'mes_wm_product_produce':'生产入库','mes_wm_product_issue':'生产领料',
|
'mes_wm_misc_receipt':'其他入库','mdm_item':'物料档案','mes_md_item':'物料档案',
|
'mes_md_product_bom':'产品BOM','mes_pro_process':'工序信息','mes_pro_route':'工艺路线',
|
'mes_md_workstation':'工作站信息','mes_pd_process_param':'生产参数',
|
'mes_pd_process_param_template':'参数模板','mes_dv_repair':'设备维修',
|
'mes_dv_check_record':'设备点检','mes_dv_mainten_record':'设备保养'}
|
BANNED=('补充','补录','非原始','原始请求','重建','归属','来源','非真实','演示')
|
def number(v):
|
return format(Decimal(str(v)).normalize(),'f')
|
def display(table,s,actor,customers,items,machines):
|
label=LABELS[table]
|
code=s.get('no') or s.get('code') or s.get('param_code') or s.get('template_code')
|
name=s.get('name') or s.get('param_name') or s.get('template_name')
|
if name and any(x in name for x in BANNED):name=None
|
parts=[label+(f'【{code}】' if code else '')+(f':{name}' if name and name!=code else '')]
|
def add(k,v):
|
if v is not None and v!='':parts.append(f'{k}:{v}')
|
def quantity(k,v):
|
if v is not None:add(k,number(v))
|
if table=='crm_customer':add('负责人',actor)
|
elif table.startswith('crm_') or table=='erp_sale_order':
|
add('客户',customers.get(s.get('customer_id')))
|
add('销售员' if table=='erp_sale_order' else '负责人',actor)
|
field={'crm_sale_quotation':'quotation_time','crm_contract':'order_date','crm_invoice':'invoice_time',
|
'crm_receivable':'return_time','crm_receivable_plan':'return_time','erp_sale_order':'order_time'}.get(table)
|
if field:
|
title={'crm_sale_quotation':'报价日期','crm_contract':'合同日期','crm_invoice':'开票日期',
|
'crm_receivable':'回款日期','crm_receivable_plan':'计划回款日期','erp_sale_order':'订单日期'}[table]
|
add(title,str(s[field])[:10] if s.get(field) else None)
|
quantity('计划回款金额(元)' if table=='crm_receivable_plan' else '金额(元)',s.get('price',s.get('total_price')))
|
elif table.startswith('mes_qc_'):
|
add('产品',items.get(s.get('item_id')));add('检验人员',actor)
|
add('检验日期',str(s['inspect_date'])[:10] if s.get('inspect_date') else None)
|
quantity('抽检数量',s.get('check_quantity'))
|
elif table=='mes_dv_repair':
|
add('设备',machines.get(s.get('machinery_id')));add('维修人员',actor)
|
add('报修时间',s.get('require_date'))
|
elif table in ('mes_dv_check_record','mes_dv_mainten_record'):
|
add('设备',machines.get(s.get('machinery_id')))
|
add('点检人员' if table=='mes_dv_check_record' else '保养人员',actor)
|
add('点检时间' if table=='mes_dv_check_record' else '保养时间',s.get('check_time',s.get('mainten_time')))
|
elif table=='mes_pro_feedback':
|
add('产品',items.get(s.get('item_id')));add('报工人员',actor);add('报工时间',s.get('feedback_time'))
|
quantity('报工数量',s.get('feedback_quantity'))
|
elif table in ('mes_pro_mps','mes_pro_work_order','mes_pro_task'):
|
add('产品',items.get(s.get('product_id',s.get('item_id'))));add('生产管理员',actor)
|
quantity('计划数量',s.get('quantity'))
|
elif table.startswith('mes_wm_'):
|
add('经办人',actor)
|
field={'mes_wm_sales_notice':'sales_date','mes_wm_product_sales':'sales_date','mes_wm_product_produce':'produce_date',
|
'mes_wm_product_issue':'issue_date','mes_wm_misc_receipt':'receipt_date'}[table]
|
add('业务时间',s.get(field))
|
else:
|
add('维护人',actor)
|
if table=='mes_md_product_bom':
|
add('产品',items.get(s.get('item_id')));quantity('用量',s.get('quantity'))
|
text=';'.join(parts)+'。'
|
assert not any(x in text for x in BANNED),text
|
assert len(text)<=2000
|
return label,text
|
|
def main():
|
assert not (P/'display-result.json').exists(),'Display refinement already applied'
|
plan=json.loads((P/'plan.json').read_text(encoding='utf8'))
|
c=connect(sys.stdin.readline().strip())
|
try:
|
with c.cursor() as q:
|
q.execute("SET time_zone='+08:00'")
|
q.execute('SELECT DATABASE() db,NOW() now');state=q.fetchone();assert state['db']=='mom-hm'
|
q.execute("SELECT GET_LOCK('codex-business-metadata-log-v1',0) locked");assert q.fetchone()['locked']==1
|
q.execute('SELECT * FROM system_operate_log ORDER BY id');before=q.fetchall();bytrace={r['trace_id']:r for r in before}
|
backup=P/('display-before-'+state['now'].strftime('%Y%m%d-%H%M%S')+'.json')
|
backup.write_text(encode(before),encoding='utf8')
|
q.execute('SELECT id,nickname FROM system_users WHERE deleted=0');users={r['id']:r['nickname'].strip() for r in q.fetchall()}
|
q.execute('SELECT id,name FROM crm_customer WHERE deleted=0');customers={r['id']:r['name'] for r in q.fetchall()}
|
q.execute('SELECT id,name FROM mes_md_item WHERE deleted=0');items={r['id']:r['name'] for r in q.fetchall()}
|
q.execute('SELECT id,name FROM mes_dv_machinery WHERE deleted=0');machines={r['id']:r['name'] for r in q.fetchall()}
|
groups=defaultdict(list);changes=[]
|
for r in plan['records']:groups[r['_table']].append(r)
|
for table,records in groups.items():
|
ids=[r['biz_id'] for r in records]
|
q.execute('SELECT * FROM `'+table+'` WHERE deleted=0 AND id IN ('+','.join(['%s']*len(ids))+') FOR SHARE',ids)
|
current={r['id']:r for r in q.fetchall()}
|
for p in records:
|
log=bytrace[p['trace_id']];extra=json.loads(log['extra'])
|
assert extra['batch']=='operation-logs-20261015' and extra['reconstructed']
|
subtype,action=display(table,current[p['biz_id']],users[log['user_id']],customers,items,machines)
|
q.execute('UPDATE system_operate_log SET sub_type=%s,action=%s,update_time=%s WHERE id=%s AND trace_id=%s',
|
(subtype,action,log['update_time'],log['id'],log['trace_id']))
|
assert q.rowcount==1
|
changes.append({'id':log['id'],'trace_id':p['trace_id'],'sub_type':subtype,'action':action})
|
q.execute('SELECT * FROM system_operate_log ORDER BY id');after={r['id']:r for r in q.fetchall()};changed={r['id']:r for r in changes}
|
assert len(after)==len(before)
|
for old in before:
|
expected=dict(old)
|
if old['id'] in changed:expected.update(sub_type=changed[old['id']]['sub_type'],action=changed[old['id']]['action'])
|
assert encode(after[old['id']])==encode(expected),'Unexpected field change'
|
c.commit()
|
(P/'display-changes.json').write_text(encode(changes),encoding='utf8')
|
result={'updated':len(changes),'backup':str(backup),'refined_at':str(state['now']),
|
'fields_changed':['sub_type','action'],'other_log_fields_unchanged':True,
|
'untouched_logs':len(before)-len(changes),'by_operation':dict(Counter(r['sub_type'] for r in changes))}
|
(P/'display-result.json').write_text(encode(result),encoding='utf8');print(encode(result))
|
except BaseException:c.rollback();raise
|
finally:c.close()
|
if __name__=='__main__':main()
|