Files
baiyue-firstleg/output/local-schema-repair/tail-flow.mjs
T
2026-10-20 11:33:20 +08:00

85 lines
8.1 KiB
JavaScript

import fs from 'node:fs';
const base=process.env.ADMIN_TEST_GATEWAY;
if(!base||!['127.0.0.1','localhost'].includes(new URL(base).hostname))throw Error('仅允许本机');
const marker='LOCAL-TAIL-20261020';
const file=new URL('tail-flow-state.json',import.meta.url);
const state=fs.existsSync(file)?JSON.parse(fs.readFileSync(file,'utf8')):{marker,environment:'LOCAL',photoEvidence:'SYNTHETIC_TEST_PNG',checks:[],taskId:'2102273311474982914',containerId:'2092814929307344898',inboundId:'2092814880129130499',orderId:'2092814878690398210',warehouseId:'193000000000200001'};
const save=()=>fs.writeFileSync(file,JSON.stringify(state,null,2));
async function api(token,method,path,body,key){
const form=body instanceof FormData;
const r=await fetch(base+path,{method,headers:{...(token?{Authorization:'Bearer '+token}:{}),...(!form&&body!==undefined?{'Content-Type':'application/json'}:{}),...(key?{'Idempotency-Key':key}:{})},body:body===undefined?undefined:form?body:JSON.stringify(body),signal:AbortSignal.timeout(60000)});
const v=await r.json();
if(!path.includes('/auth/')){state.checks.push({method,path,http:r.status,code:v.code,message:String(v.code)==='200'?undefined:v.message});save();console.log(method,path,v.code,String(v.code)==='200'?'':v.message);}
if(!r.ok||String(v.code)!=='200')throw Error(`${method} ${path}: ${v.code} ${v.message}`);
return v.data;
}
const admin=(await api('','POST','/api/auth/login',{loginId:process.env.ADMIN_TEST_USERNAME,password:process.env.ADMIN_TEST_PASSWORD})).accessToken;
const root='/api/platform/v1/first-leg/pda-loading/tasks/'+state.taskId;
const task=()=>api(admin,'GET',root+'?warehouseId='+state.warehouseId);
const action=process.argv[2]||'inspect';
try {
if(action==='gate'){
for(const suffix of ['?containerBookingId='+state.containerId,'/'+state.containerId+'/gate']){
console.log(JSON.stringify(await api(admin,'GET','/api/platform/v1/first-leg/outbound-dispatches'+suffix)));
}
}
if(action==='verify'){
const doc=await api(admin,'GET','/api/platform/v1/containers/'+state.containerId+'/clearance');
const rows={};
for(const status of ['SAILING_CLEARANCE','WAIT_UNLOAD','WAIT_DISPATCH']){
const page=await api(admin,'GET','/api/platform/v1/orders?page=1&size=200&mainStatus='+status);
rows[status]={total:page.total,matching:page.records.filter(r=>r.inboundId===state.inboundId)};
}
if(rows.SAILING_CLEARANCE.matching.length||rows.WAIT_UNLOAD.matching.length!==1||rows.WAIT_DISPATCH.matching.length!==1)throw Error('测试来源页签流转不符合预期');
const r=await fetch(base+'/api/platform/v1/containers/'+state.containerId+'/clearance/document/'+doc.fileId+'/download',{headers:{Authorization:'Bearer '+admin},signal:AbortSignal.timeout(30000)});
const downloaded=await r.text();
if(!r.ok||!downloaded.includes(marker))throw Error('测试清关附件下载不一致');
state.verification={at:new Date().toISOString(),document:doc,rows,download:{http:r.status,containsLocalTestMarker:true}};save();
console.log(JSON.stringify({document:doc,rows:Object.fromEntries(Object.entries(rows).map(([s,p])=>[s,{total:p.total,matching:p.matching.map(r=>r.inboundId)}])),downloadHttp:r.status}));
}
if(action==='prepare'){
if(!state.pdaUserId && state.checks.some(c=>c.method==='POST'&&c.path==='/api/platform/v1/iam/users'&&String(c.code)==='200')){ const a=await api('','POST','/api/auth/login',{loginId:state.pdaUsername,password:process.env.LOCAL_TRANSFER_PDA_PASSWORD}); state.pdaUserId=JSON.parse(Buffer.from(a.accessToken.split('.')[1],'base64url')).sub;save(); }
if(!state.pdaUserId){
const roles=await api(admin,'GET','/api/platform/v1/iam/roles?page=1&size=200');
const role=(roles.records||roles).find(r=>r.roleCode==='CN_FIRSTLEG_PDA_OPERATOR');
if(!role)throw Error('缺少国内PDA角色');
state.pdaUsername='local_tail_pda_20261020';save();
await api(admin,'POST','/api/platform/v1/iam/users',{username:state.pdaUsername,displayName:'本地尾程流转验证PDA',phone:'18'+String(Date.now()).slice(-9),tenantId:'0',roleIds:[role.id],domesticWarehouseId:state.warehouseId,password:process.env.LOCAL_TRANSFER_PDA_PASSWORD,status:'ACTIVE'});
const users=await api(admin,'GET','/api/platform/v1/iam/users?page=1&size=20&keyword='+state.pdaUsername);
const user=users.records.find(r=>r.username===state.pdaUsername);
if(!user)throw Error('PDA创建后未查到');state.pdaUserId=user.userId;save();
}
const pda=(await api('','POST','/api/auth/login',{loginId:state.pdaUsername,password:process.env.LOCAL_TRANSFER_PDA_PASSWORD})).accessToken;
let current=await task();
if(current.assignment?.assigneeId!==state.pdaUserId)current=await api(admin,'POST',root+'/assign',{assigneeId:state.pdaUserId,assigneeName:'本地尾程流转验证PDA',expectedAssignmentVersion:current.assignment?.version||0,operationId:marker+'-assign'});
if(!state.before){state.before=await api(admin,'GET','/api/platform/v1/orders/status-counts');save();}
for(const item of current.items){if(item.itemStatus!=='PICKED')await api(pda,'POST',root+'/items/'+item.id+'/pick',{warehouseId:state.warehouseId,pickedPackageCount:item.plannedPackageCount,version:item.version,operationId:marker+'-pick-'+item.id});}
const evidence=await api(pda,'GET',root+'/evidence?warehouseId='+state.warehouseId);
for(const type of ['PHOTO_STEP_1','PHOTO_STEP_2','PHOTO_STEP_3','PHOTO_STEP_4','SEAL']){
if(evidence.evidences?.some(e=>e.evidenceType===type&&e.status==='ACTIVE'))continue;
if(state.evidence?.includes(type))continue;
const q=new URLSearchParams({warehouseId:state.warehouseId,expectedVersion:'0',evidenceType:type,remark:marker+' 合成测试图片;仅本地流转验收'});
if(type==='SEAL')q.set('sealNo','LOCALTEST20261020');
const bytes=Uint8Array.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,6,0,0,0,31,21,196,137,0,0,0,13,73,68,65,84,8,215,99,248,207,192,240,31,0,5,0,1,255,137,153,61,29,0,0,0,0,73,69,78,68,174,66,96,130]);
const body=new FormData();body.append('file',new Blob([bytes],{type:'image/png'}),marker+'-'+type+'.png');
await api(pda,'POST',root+'/evidence?'+q,body,marker+'-'+type);(state.evidence??=[]).push(type);save();
}
current=await task();
if(current.taskStatus==='LOADING'||current.taskStatus==='COMPLETION_FAILED')state.completion=await api(pda,'POST',root+'/complete',{expectedTaskVersion:state.completion?.completionCommand?.expectedTaskVersion??current.version,operationId:marker+'-complete',remark:marker+' 本地测试装柜交接验证'});
save();
}
if(action==='inspect'||action==='prepare') {state.task=await task();state.counts=await api(admin,'GET','/api/platform/v1/orders/status-counts');save();console.log(JSON.stringify({taskStatus:state.task.taskStatus,completion:state.task.completionCommand,counts:state.counts}));}
if(action==='depart'){
if((await task()).taskStatus!=='COMPLETED')throw Error('必须先装柜完成');
state.nodeTime??=new Date(Date.now()+8*3600000).toISOString().slice(0,19);save();
state.departure=await api(admin,'POST','/api/platform/v1/container-shipping-nodes/'+state.containerId,{standardNode:'DEPARTED_ORIGIN_PORT',observedAt:state.nodeTime,actualAt:state.nodeTime,locationName:'本地手动开船流转验收',reason:marker+' 用户授权本地手动节点验证'},marker+'-depart');
state.afterDeparture=await api(admin,'GET','/api/platform/v1/orders/status-counts');save();console.log(JSON.stringify(state.afterDeparture));
}
if(action==='clearance'){
state.clearanceBefore=await api(admin,'GET','/api/platform/v1/containers/'+state.containerId+'/clearance');save();
const form=new FormData();form.append('file',new Blob([marker+' LOCAL TEST ONLY 清关资料上传流转测试\n'],{type:'text/plain'}),marker+'-clearance.txt');
state.clearance=await api(admin,'POST','/api/platform/v1/containers/'+state.containerId+'/clearance/document/upload?expectedVersion='+(state.clearanceBefore?.version||0)+'&operationReason='+encodeURIComponent('本地流转测试资料'),form,marker+'-clearance');
state.afterClearance=await api(admin,'GET','/api/platform/v1/orders/status-counts');save();console.log(JSON.stringify(state.afterClearance));
}
}catch(e){state.lastError=e.message;save();console.error(e.message);process.exitCode=1;}