| 1234567891011121314151617181920212223242526272829303132333435363738394041 |
- // Read-only inventory for the project work report. Never fetch identity/config tables.
- import fs from 'node:fs';
- import crypto from 'node:crypto';
- import {request,classRows} from '../server/scripts/platform-client.mjs';
- const schemas=(await request('GET','/schemas')).results;
- const groups={
- 品类与竞品:['KfCategory','KfCategoryRelation','KfProduct','KfSku','KfRetailPriceSnapshot'],
- 消费者与证据:['KfRawXhsNote','KfRawXhsComment','KfRawDouyinVideo','KfRawDouyinComment','KfRawTaobaoItem','KfRawTaobaoReview','KfRawJdItem','KfRawJdReview','KfRawBilibiliPost','KfEvidence','KfEvidenceLink','KfInsight'],
- 市场与验证:['KfMarketMetric','KfAssessment'],
- 原料工艺与供应链:['KfMaterial','KfProcess','KfProcessStep','KfStandard','KfSupplier','KfSupplyQuote','KfRaw1688Offer'],
- 工作坊与成果:['KfSession','KfArtifact','KfOpportunity','KfProject','KfConcept','KfFormulaItem','KfStageEvent'],
- 来源与采集:['KfSource','KfVocRun','KfImportJob','KfImportItem'],
- 追问与问卷:['KfReviewIssue','KfQuestionnaireQuestion','KfQuestionnaireAnswer']
- };
- const wanted=['objectId','dataMode','recordState','title','name','sourceKind','platform','sourceRef','sourceIds','externalObjectId','idOrigin','reportType','artifactType','stageCode','dashboardState','progressState','pendingQuestions','status','version','content','demandSummary','targetAudience','usageScenario','specification','description','quote','text','sourceUrl','observedRetailPrice','priceObservedAt','unit','value','metricName','question','answer','rawAnswer','code','sourceRefs','evidenceRefs','reportContentHash','summary','rawText','sources','verificationState','body','evidenceLevel','claimKind','report','stage','questionList'];
- const result={at:new Date().toISOString(),readOnly:true,businessWrites:0,groups,classes:[],functionStatus:[],publicBoard:{}};
- const names=[...new Set(Object.values(groups).flat())];
- for(let offset=0;offset<names.length;offset+=5){
- const settled=await Promise.allSettled(names.slice(offset,offset+5).map(async name=>{
- const schema=schemas.find(x=>x.className===name);if(!schema)return{name,exists:false,total:0,complete:true};
- const keys=wanted.filter(k=>schema.fields[k]||k==='objectId'),rows=[];let after=null,total=0,complete=false;
- for(let page=0;page<10;page++){
- const q=new URLSearchParams({keys:keys.join(','),limit:'1000',order:'objectId',...(page===0?{count:'1'}:{}),...(after?{where:JSON.stringify({objectId:{$gt:after}})}:{})});
- const response=await request('GET','/classes/'+name+'?'+q);if(page===0)total=response.count;
- if(!Array.isArray(response.results))throw Error('BAD_RESULTS');rows.push(...response.results);
- if(response.results.length<1000){complete=true;break;}after=response.results.at(-1).objectId;
- }
- const modes={},states={};for(const row of rows){modes[row.dataMode||'unmarked']=(modes[row.dataMode||'unmarked']||0)+1;states[row.recordState||'unmarked']=(states[row.recordState||'unmarked']||0)+1;}
- return{name,exists:true,total,read:rows.length,complete,modes,states,rows};
- }));
- settled.forEach((x,i)=>result.classes.push(x.status==='fulfilled'?x.value:{name:names[offset+i],error:'READ_FAILED'}));
- }
- const functions=await classRows('Function');
- for(const name of ['ksfStartWorkshopSession','ksfGetIngestContract','ksfRegisterSource','ksfSubmitImport','ksfCheckDuplicates']){
- const rows=functions.filter(x=>x.name===name&&x.path==='/'+name);result.functionStatus.push({name,found:rows.length===1,...(rows.length===1?{sha256:crypto.createHash('sha256').update(rows[0].code).digest('hex')}:{})});
- }
- const response=await fetch('https://kangshifu.fmode.cn/api/functions',{method:'POST',headers:{'Content-Type':'application/json','X-Parse-Application-Id':'FKieRbIoGt_main'},body:JSON.stringify({path:'/ksfGetPublicBoard',params:{limit:500}}),redirect:'error',signal:AbortSignal.timeout(30000)});
- const publicResult=(await response.json()).result;
- result.publicBoard={http:response.status,ok:publicResult?.ok===true,asOf:publicResult?.data?.asOf,nextCursor:publicResult?.data?.nextCursor||null,counts:Object.fromEntries(Object.entries(publicResult?.data||{}).filter(([k,v])=>Array.isArray(v)).map(([k,v])=>[k,v.length]))};
- fs.writeFileSync('work/workshop-report-inventory-20261102.json',JSON.stringify(result,null,2),{mode:0o600});
- console.log(JSON.stringify({...result,classes:result.classes.map(({rows,...x})=>({...x,titles:rows?.map(r=>r.title||r.name||r.question||r.metricName).filter(Boolean).slice(0,8)}))},null,2));
|