| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- /** Read back this intake's allowlisted records; publish a credential-free reference snapshot. No DB writes. */
- import fs from 'node:fs/promises';
- import crypto from 'node:crypto';
- import { invoke, appId, functionsUrl } from '../server/scripts/platform-client.mjs';
- const archive = 'docs/noodle-innovation-20261108';
- const read = async name => JSON.parse(await fs.readFile(name, 'utf8'));
- const sha = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
- const must = (v, code) => { if (!v) throw Error(code); };
- const FIELDS = {
- KfCategory: ['code','name','axis','scopeNote','taxonomyVersion'],
- KfCategoryRelation: ['fromCategoryRef','toCategoryRef','relationType','rationale','claimKind'],
- KfStandard: ['standardCode','edition','title','status','effectiveDate','scope'],
- KfMaterial: ['name','aliases','materialType','function','constraints','specification','storageConditions','sensoryProfile'],
- KfProcess: ['name','processType','processKind','description','steps','parameters','factoryContext'],
- KfSupplier: ['name','supplierType','region','supplyScope','approvalState','qualification'],
- };
- async function main() {
- const [plan, receipt, sourceIndex, scope, contexts, locators] = await Promise.all([
- read('work/noodle-innovation-20261108-r1/intake-plan.json'), read(archive+'/receipts/intake-summary.json'),
- read(archive+'/source-index.json'), read(archive+'/scope-audit.json'),
- read(archive+'/review-context-candidates.json'), read(archive+'/product-locator-index.json'),
- ]);
- const config = {};
- for (const line of (await fs.readFile('F:/文档/康师傅数字人受限连接.env','utf8')).split(/\r?\n/)) {
- const m = line.match(/^(KSF_SERVICE_TOKEN|KSF_FUNCTIONS_URL|KSF_PARSE_APP_ID)=(.*)$/);
- if (m) config[m[1]]=m[2].trim();
- }
- must(config.KSF_SERVICE_TOKEN && config.KSF_FUNCTIONS_URL === functionsUrl && config.KSF_PARSE_APP_ID === appId, 'CONFIG_MISMATCH');
- const headers = {'X-Parse-Application-Id':appId,'X-Ksf-Service-Token':config.KSF_SERVICE_TOKEN,'Content-Type':'application/json'};
- let reads=0;
- async function get(className,objectId) {
- const result=await invoke('ksfGetRecord',{className,objectId},headers);
- must(result.http===200 && result.body.result?.ok, 'READBACK_FAILED:'+className+':'+objectId);
- reads++;return result.body.result.data.record;
- }
- const records=[], sources=[];
- const seen=new Set();
- // Sequential groups preserve source binding; no inferred IDs from array positions.
- for (const group of receipt.groups) {
- const planned=plan.groups.find(g=>g.key===group.key);
- const source=await get('KfSource',group.sourceId);
- must(sha(Buffer.from(source.rawText))===group.sourceContentHash,'SOURCE_CHANGED');
- if (!seen.has(group.sourceId)) {
- seen.add(group.sourceId);
- sources.push({className:'KfSource',objectId:group.sourceId,title:source.title,
- contentHash:group.sourceContentHash,originalUrl:sourceIndex.find(s=>s.id===group.key)?.originalUrl||null});
- }
- for (const ref of group.records) {
- const row=await get(ref.className,ref.objectId);
- const original=planned.records.find(r=>r.externalKey===row.externalKey);
- must(original && (!row.sourceRef || row.sourceRef.objectId===group.sourceId) && row.recordState==='active','RECORD_BINDING_CHANGED');
- must(['pending','approved','rejected'].includes(row.reviewState),'UNKNOWN_REVIEW_STATE');
- // Publish current DB values only, and only explicitly public research fields.
- records.push({key:original.itemKey,className:ref.className,objectId:ref.objectId,
- sourceId:group.sourceId,sourceBinding:row.sourceRef?'record_pointer':'verified_intake_receipt',reviewState:row.reviewState,
- ...Object.fromEntries(FIELDS[ref.className].filter(k=>row[k]!==undefined).map(k=>[k,row[k]]))});
- }
- }
- must(records.length===58 && sources.length===14,'INCOMPLETE_INTAKE');
- const snapshot=await read('work/industry-monitoring-20261107-r1/snapshot.json');
- const reviews=new Map(snapshot.entities.reviews.map(r=>[r.record_id,r]));
- const products=new Map(locators.products.map(p=>[p.productId,p]));
- for (const row of contexts) {
- const raw=reviews.get(row.reviewId);
- must(raw && raw.content===row.text && raw.product_id===row.productId && products.has(row.productId) &&
- raw.review_date_raw===row.date && raw.source_record_id===row.sourceRecordId && raw.rating_raw===row.rating,'REVIEW_CONTEXT_CHANGED');
- }
- const versionRes=await fetch('https://kangshifu.fmode.cn/api/library',{
- method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({op:'version'}),signal:AbortSignal.timeout(30000)});
- const version=await versionRes.json();
- must(versionRes.ok && version.ok && version.generation===plan.libraryGeneration,'LIBRARY_GENERATION_CHANGED');
- const data={schema:'ksf.development-references.v1',publishedAt:new Date().toISOString(),
- snapshotKind:'verified_database_publication',libraryGeneration:plan.libraryGeneration,
- coverageAsOf:scope.snapshotExportedAt,scope:{products:scope.scopeProducts,excluded:scope.excludedProducts,
- pending:scope.pendingScopeProducts,categories:scope.categories,coverage:scope.coverage,brandCoverage:scope.brandCoverage},
- records,sources,reviewExamples:contexts.map(r=>({...r,productTitle:products.get(r.productId).title})),
- locatorSourceId:'T_o8CUTF83',databaseWrites:0,paidCalls:0};
- await fs.mkdir('src/data',{recursive:true});
- const text=JSON.stringify(data,null,2)+'\n';
- await fs.writeFile('src/data/development-references.json',text);
- await fs.mkdir('docs/development-board-20261108',{recursive:true});
- await fs.writeFile('docs/development-board-20261108/data-publication.json',JSON.stringify({ok:true,
- publishedAt:data.publishedAt,reads,records:records.length,sources:sources.length,reviewExamples:contexts.length,
- sha256:sha(Buffer.from(text)),generation:version.generation,credentialsIncluded:false,databaseWrites:0,paidCalls:0},null,2)+'\n');
- console.log(JSON.stringify({ok:true,reads,records:records.length,sources:sources.length,databaseWrites:0,paidCalls:0}));
- }
- main().catch(e=>{console.error(JSON.stringify({ok:false,code:/^[A-Z_]+(?::[A-Za-z0-9:_-]+)?$/.test(e.message)?e.message:'PUBLICATION_FAILED',type:e.name}));process.exitCode=1;});
|