function text(value) { return String(value ?? '').trim(); } function number(value, fallback = 0) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : fallback; } function dateValue(value) { const raw = text(value); if (!/^\d{4}-\d{1,2}-\d{1,2}(?:[ T]\d{1,2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?/.test(raw)) return null; const normalized = raw.replace(/^(\d{4})-(\d)(?=-)/, '$1-0$2').replace(/-(\d)(?=[ T])/, '-0$1').replace(' ', 'T'); const date = new Date(normalized + (normalized.includes('T') ? '+08:00' : 'T00:00:00+08:00')); return Number.isNaN(date.getTime()) ? null : { __type: 'Date', iso: date.toISOString() }; } function setIf(body, key, value) { if (value !== null && value !== undefined && value !== '') body[key] = value; } const legacyAddonFields = { 53: ['kcid','scid','xxcs','yhid','jrscb'], 54: ['pl','bxrq','dslx','dszt','fxpl','jffs','jssj','kcid','kssj','plxm','scsj','sdsd','szmd','szyh','yysj','yykcid'], 56: ['pl','con','djq','ygg','dqrq','dsid','fxrl','xxqs','szmdid','userId','learned'], 58: ['yxx','cksl','kcid','syjd','yhid'], 59: ['pf','jffs','jsmz','kcid','kclx','kcmc','kzsj','pjnr','pldp','xymz','yyds','plpjsj','szmdid'], 60: ['fxzt','kcid','plid','wcsj','yhid','kywrq','kywsj','xxjlid','orderId'], 61: ['df','askid','wrong','userId','answerid','dontKnow','prevScore','totalScore'], }; const numericAddonFields = new Set(['scid','xxcs','yhid@53','djq','ygg','userId@56','learned','yxx','cksl','pf','kcid@60','plid','yhid@60','xxjlid','orderId']); function signatureValue(modelId, field, value) { return numericAddonFields.has(field) || numericAddonFields.has(`${field}@${modelId}`) ? number(value) : text(value); } function addonSignature(modelId, source) { return JSON.stringify(legacyAddonFields[modelId].map((field) => signatureValue(modelId, field, source[field]))); } function commonBody(modelId, row, itemId) { const spec = specs[modelId]; const generalId = number(row.GeneralID ?? row.generalId); const body = { company, sourceKey: `legacy:model:${modelId}:general:${generalId}`, generalId, itemId, modelId, nodeId: number(row.NodeID ?? row.nodeId, spec.nodeId), tableName: spec.tableName, title: text(row.Title ?? row.title), subtitle: text(row.Subtitle ?? row.subtitle), inputer: text(row.Inputer ?? row.inputer), topImg: text(row.TopImg ?? row.topImg), template: text(row.Template ?? row.template), hits: number(row.Hits ?? row.hits), status: 99, }; const created = dateValue(row.CreateTime ?? row.createTime); if (created) { body.createTime = created; body.upDateTime = created; } return body; } function addonBody(modelId, row, itemId, generalId) { // Parse REST/SDK reserves the property name `id`. The physical legacy id is // assigned in one controlled SQL step after both objects exist. const body = { company, sourceKey: `legacy:model:${modelId}:general:${generalId}` }; if (modelId === 53) Object.assign(body, { kcid: text(row.kcid), scid: number(row.scid), xxcs: number(row.xxcs), yhid: number(row.yhid), jrscb: text(row.jrscb), }); if (modelId === 54) for (const field of ['pl','bxrq','dslx','dszt','fxpl','jffs','jssj','kcid','kssj','plxm','scsj','sdsd','szmd','szyh','yysj','yykcid']) body[field] = text(row[field]); if (modelId === 56) Object.assign(body, { pl: text(row.pl), con: text(row.con), djq: number(row.djq), ygg: number(row.ygg), dqrq: text(row.dqrq), dsid: text(row.dsid), fxrl: text(row.fxrl), xxqs: text(row.xxqs), szmdid: text(row.szmdid), userId: number(row.UserID ?? row.userId), learned: number(row.learned), }); if (modelId === 58) Object.assign(body, { yxx: number(row.yxx), cksl: number(row.cksl), kcid: text(row.kcid), syjd: text(row.syjd), yhid: text(row.yhid), }); if (modelId === 59) { for (const field of ['jffs','jsmz','kcid','kclx','kcmc','kzsj','pjnr','pldp','xymz','yyds','plpjsj','szmdid']) body[field] = text(row[field]); setIf(body, 'pf', row.pf === null || row.pf === '' ? null : number(row.pf)); body.legacyGeneralId = generalId; body.studentName = text(row.Title ?? row.title); body.coachName = text(row.Inputer ?? row.inputer); body.courseName = text(row.kcmc ?? row.Subtitle ?? row.subtitle); body.storeId = number(row.szmdid); const lessonAt = dateValue(row.plpjsj) || dateValue(row.CreateTime ?? row.createTime); const sourceCreatedAt = dateValue(row.CreateTime ?? row.createTime); if (lessonAt) body.lessonAt = lessonAt; if (sourceCreatedAt) body.sourceCreatedAt = sourceCreatedAt; } if (modelId === 60) Object.assign(body, { fxzt: text(row.fxzt), kcid: number(row.kcid), plid: number(row.plid), wcsj: text(row.wcsj), yhid: number(row.yhid), kywrq: text(row.kywrq), kywsj: text(row.kywsj), xxjlid: number(row.xxjlid), orderId: number(row.orderId), }); if (modelId === 61) Object.assign(body, { df: text(row.df), askid: text(row.askid), wrong: text(row.wrong), userId: text(row.UserID ?? row.userId), answerid: text(row.answerid), dontKnow: text(row.dontKnow), prevScore: text(row.prev_score ?? row.prevScore), totalScore: text(row.totalScore), }); return body; } export function projectionFor(companyValue, specsValue) { company=companyValue; specs=specsValue; return {commonBody,addonBody,legacyAddonFields,signatureValue}; } let company, specs; export { commonBody, addonBody, legacyAddonFields, signatureValue };