| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- async function readLegacyWritingOrder(id) {
- const config=await appLegacyConfig();
- if(!config.url||!config.apiId||!config.apiKey)throw Error('暂时无法核对旧系统写作预约,请刷新后重试');
- const params=new URLSearchParams({action:'content_list',modelId:'54',ids:String(id),cpage:'1',psize:'2',apiId:config.apiId,apiKey:config.apiKey});
- try {
- const url=config.url+(config.url.includes('?')?'&':'?')+params.toString();
- const response=await fetch(url,{headers:{accept:'application/json'},signal:AbortSignal.timeout(8000)});
- const payload=await response.json();
- const rows=parseArray(payload.result);
- if(response.ok&&[0,1].includes(Number(payload.retcode))&&rows.length===1&&
- Number(payload.page?.itemCount)===1&&Number(payload.page?.pageCount)===1)return rows[0];
- } catch (_) { /* The legacy URL contains credentials. Do not expose fetch errors. */ }
- throw Error('暂时无法核对旧系统写作预约,请刷新后重试');
- }
- function assertLegacyWritingPending(order,live) {
- const same=(key)=>String(live?.[key]??'').trim()===String(order[key]??'').trim();
- const time=(value)=>String(value??'').trim().replace('T',' ').slice(0,16);
- const model=live?.ModelID??live?.modelId;
- const writingBooking=model==null?Number(live?.NodeID??live?.nodeId)===29:Number(model)===54;
- if(String(live?.GeneralID??live?.generalId)!==String(order.GeneralID)||!writingBooking||
- !same('szyh')||!same('pl')||!same('kcid')||!same('dslx')||!same('jffs')||!same('bxrq')||
- time(live.yysj)!==time(order.yysj)||Number(live.Status??live.status??99)!==99){
- throw Error('旧系统写作预约与新系统不一致,请联系管理员核对');
- }
- if(String(live.dszt)!=='0'||String(live.kssj??'').trim()||String(live.jssj??'').trim()){
- throw Error('旧系统已开始上课或已完课,请等待记录同步,避免重复扣课时');
- }
- }
- async function confirmLegacyWritingStage(db,order,current) {
- // Keep the exact booking, student profile and the existing preparation in one transaction.
- const co=DEFAULT_COMPANY_ID,aid=order.objectId;
- const row=await db.oneOrNone(`SELECT c.title AS "studentName",a.szyh AS "studentId",
- a."yysj" AS "startsAt",a."updatedAt"::text AS "updatedAt",a."courseCategoryKey" AS category,
- a."categoryEvidence" AS evidence,a."bindingId",a."teacherPaySnapshot",a."creditCostsSnapshot",
- to_jsonb(u)->>'grade' AS grade,u."legacyUserData"->>'Position' AS position,
- u."legacyUserData"->'UserBase'->>'Position' AS "basePosition",
- u."legacyUserData"->>'HoneyName' AS "oldName",u."legacyUserData"->>'UserName' AS "oldUsername",
- u."realName",u.nickname,u.username
- FROM "CommonModel" c JOIN "CourseAppointment" a ON a.company=c.company AND a.id=c."itemId"
- JOIN "_User" u ON u.company=a.company AND u."legacyUserId"::text=a.szyh::text
- WHERE c.company=$1 AND c."objectId"=$2 AND c."modelId"=54 AND c."generalId"::text=$3 AND c.status=99
- AND c."sourceKey"='legacy:model:54:general:'||$3 AND a."sourceKey"=c."sourceKey"
- AND a.szyh::text=$4 AND a.pl::text=$5 AND a.yysj=$6 AND a.dslx::text=$7 AND a.jffs=$8 AND a.bxrq=$9
- AND a.kcid::text='398' AND a.yykcid::text='398' AND a.dszt='0'
- AND NULLIF(trim(a.kssj),'') IS NULL AND NULLIF(trim(a.jssj),'') IS NULL
- AND NOT COALESCE(a."teachingManaged",false) AND NOT COALESCE(u."isDisabled",false)
- AND NOT COALESCE((to_jsonb(u)->>'isDeleted')::boolean,false)
- AND NOT EXISTS(SELECT 1 FROM "LessonRecord" l WHERE l.company=a.company AND l.yyds::text=c."generalId"::text)
- AND NOT EXISTS(SELECT 1 FROM "LessonCompletionReport" r WHERE r.company=a.company AND r."appointmentGeneralId"::text=c."generalId"::text)
- AND NOT EXISTS(SELECT 1 FROM "CourseCreditReservation" r WHERE r.company=a.company AND r."appointmentId"=c."objectId" AND r.state='consumed')
- FOR UPDATE OF c,a,u`,[co,aid,String(order.GeneralID),String(order.szyh),String(order.pl),order.yysj,String(order.dslx),order.jffs,order.bxrq]);
- if(!row)throw Error('旧写作预约已变更,请刷新后核对');
- if(['primary_writing','middle_writing','high_writing'].includes(row.category)&&String(row.evidence??'').trim().length>=2)return;
- if(row.category||row.evidence||row.bindingId||row.teacherPaySnapshot!=null||row.creditCostsSnapshot?.length){
- throw Error('旧写作预约已有课程或课时凭据,请由管理员核对学段');
- }
- const prior=await db.query(`SELECT c."generalId" AS "appointmentGeneralId",a.szyh AS "studentId",
- a.yysj AS "startsAt",a."courseCategoryKey" AS category,a."categoryEvidence" AS evidence
- FROM "CommonModel" c JOIN "CourseAppointment" a ON a.company=c.company AND a.id=c."itemId"
- WHERE c.company=$1 AND c."modelId"=54 AND c.status=99 AND a.szyh::text=$2
- AND a."courseCategoryKey" IN ('primary_writing','middle_writing','high_writing')
- AND length(trim(COALESCE(a."categoryEvidence",'')))>=2 AND a."categoryEvidence" NOT LIKE '开课自动核对:%'`,[co,String(row.studentId)]);
- const today=appOverviewToday();
- const trusted=legacyWritingStageAssessment({studentId:row.studentId,startsAt:row.startsAt},prior,today);
- if(!trusted.canAutoConfirm)throw Error('缺少一致的本学年已确认写作课记录,请由管理员核对学段');
- const stage=legacyWritingStageAssessment(row,prior,today);
- if(!stage.canAutoConfirm)throw Error(stage.assessmentReason);
- await db.one('SELECT xs_payroll_writing_mapping($1,$2,$3,$4,$5,$6::timestamptz,$7) AS result',
- [co,current.id,aid,stage.suggestedCategoryKey,'开课自动核对:'+stage.evidence,row.updatedAt,'旧写作预约开课前按档案和本学年记录核对学段']);
- }
- async function prepareLegacyAppointment(order,current,startWriting=false) {
- // SQL rechecks ownership. Request parameters cannot supply the pricing rules.
- const prepare=async db=>(await db.one('SELECT xs_prepare_legacy_appointment($1,$2,$3,$4::jsonb) AS result',
- [DEFAULT_COMPANY_ID,order.objectId,current.id,JSON.stringify(legacyAppointmentSeedRules())])).result;
- try {
- if(String(order.kcid)!=='398')return await prepare(Psql);
- const uid=ownLegacyId(current);
- if(uid!==number(order.pl)||number(current.get('legacyGroupId'))!==3)throw Error('仅预约陪练可以核对旧写作课');
- if(!startWriting)assertLegacyWritingPending(order,await readLegacyWritingOrder(order.GeneralID));
- return await Psql.tx(async db=>{
- await db.one("SELECT pg_advisory_xact_lock(hashtext('xs-credit:'||$1))",[DEFAULT_COMPANY_ID]);
- if(startWriting)assertLegacyWritingPending(order,await readLegacyWritingOrder(order.GeneralID));
- await confirmLegacyWritingStage(db,order,current);
- const prepared=await prepare(db);
- if(startWriting)await db.one('SELECT xs_start_appointment_for_actor($1,$2,$3,$4) AS result',
- [DEFAULT_COMPANY_ID,order.objectId,current.id,uid]);
- return prepared;
- });
- } catch(error) {
- return {blockedReason:String(error.message||error).replace(/^.*ERROR:\s*/,'')};
- }
- }
|