| 12345678910111213141516171819202122232425262728293031323334353637383940 |
- // Learning content is chosen from the student's enrollments. Billing stays on the appointment.
- async function appLessonCourses(input,current) {
- assertLessonTenant(current);
- const order=await orderDetailRow(input.orderId||input.id,current,false);
- if(!order)fail(404,'预约不存在');
- if(number(current.get('legacyGroupId'))!==3||ownLegacyId(current)!==number(order.pl))fail(403,'仅预约老师可以选择学员课程');
- if(order.cancelled)fail(409,'预约已取消');
- const rows=await Psql.query('SELECT b.*,c."generalId",n."nodeName" AS "courseName" FROM "CourseBinding" b JOIN "CommonModel" c ON c.company=b.company AND c."modelId"=58 AND c."itemId"=b.id JOIN "Node" n ON n.company=b.company AND n."nodeId"::text=b.kcid WHERE b.company=$1 AND b.yhid=$2 AND b."enrollmentStatus"=\'active\' AND c.status=99 AND COALESCE(n.zstatus,99)=99 ORDER BY b.id DESC,b."objectId"',[DEFAULT_COMPANY_ID,String(order.szyh)]);
- return {items:rows.map(b=>({bindingId:b.objectId,GeneralID:b.generalId,kcid:b.kcid,Title:b.courseName,categoryKey:b.categoryKey,categoryName:b.categoryName,syjd:b.syjd,scheduled:b.objectId===order.bindingId})),studentId:number(order.szyh),scheduledBindingId:order.bindingId,source:'database'};
- }
- async function lessonStudyBinding(addon,current,uid,order,words=[]) {
- assertLessonTenant(current);
- if(order){
- if(number(order.szyh)!==uid||order.cancelled)fail(403,'学习记录与预约学员不匹配');
- if(ownLegacyId(current)!==uid&&(number(current.get('legacyGroupId'))!==3||ownLegacyId(current)!==number(order.pl)))fail(403,'仅预约师生可以保存本节课学习记录');
- if(number(order.dszt)!==10)fail(409,'请在课程进行中保存学习记录');
- }
- const id=String(addon.bindingId||order?.bindingId||'');
- if(!id)return null;
- const binding=await Psql.oneOrNone('SELECT b."objectId",b.kcid FROM "CourseBinding" b JOIN "CommonModel" c ON c.company=b.company AND c."modelId"=58 AND c."itemId"=b.id JOIN "Node" n ON n.company=b.company AND n."nodeId"::text=b.kcid WHERE b.company=$1 AND b.yhid=$2 AND b."objectId"=$3 AND b."enrollmentStatus"=\'active\' AND c.status=99 AND COALESCE(n.zstatus,99)=99',[DEFAULT_COMPANY_ID,String(uid),id]);
- if(!binding)fail(409,'所选课程尚未开通或已停用,请刷新课程列表');
- if(words.length){
- const ids=[...new Set(words.map(w=>String(w.GeneralID)))];
- const found=await Psql.one('WITH RECURSIVE nodes AS (SELECT "nodeId" FROM "Node" WHERE company=$1 AND "nodeId"::text=$2 AND COALESCE(zstatus,99)=99 UNION SELECT n."nodeId" FROM "Node" n JOIN nodes p ON n."parentId"=p."nodeId" WHERE n.company=$1 AND COALESCE(n.zstatus,99)=99) SELECT count(DISTINCT c."generalId")::int total FROM "CommonModel" c JOIN nodes n ON n."nodeId"=c."nodeId" WHERE c.company=$1 AND c."modelId"=52 AND c.status=99 AND c."generalId"::text=ANY($3::text[])',[DEFAULT_COMPANY_ID,String(binding.kcid),ids]);
- if(number(found.total)!==ids.length)fail(409,'学习单词与所选课程不匹配,请重新进入课程');
- }
- return binding;
- }
- async function startLessonWithContent(current,appointmentId,bindingId,expected) {
- // Selection and assignment are checked under the same lock as enrollment recycling and scheduling.
- try{return await Psql.tx(async t=>{
- await t.one('SELECT pg_advisory_xact_lock(hashtext($1))',['xs-credit:'+DEFAULT_COMPANY_ID]);
- const a=await t.one('SELECT a.szyh,a."updatedAt"::text AS version FROM "CourseAppointment" a JOIN "CommonModel" c ON c.company=a.company AND c."modelId"=54 AND c."itemId"=a.id WHERE c.company=$1 AND c."objectId"=$2',[DEFAULT_COMPANY_ID,appointmentId]);
- if(expected){const v=await t.one('SELECT $1::timestamptz=$2::timestamptz same',[a.version,String(expected)]);if(!v.same)throw Error('预约已更新,请刷新详情后重试');}
- const b=await t.one('SELECT b."objectId",b.kcid FROM "CourseBinding" b JOIN "CommonModel" c ON c.company=b.company AND c."modelId"=58 AND c."itemId"=b.id JOIN "Node" n ON n.company=b.company AND n."nodeId"::text=b.kcid WHERE b.company=$1 AND b.yhid=$2 AND b."objectId"=$3 AND b."enrollmentStatus"=\'active\' AND c.status=99 AND COALESCE(n.zstatus,99)=99',[DEFAULT_COMPANY_ID,String(a.szyh),String(bindingId)]);
- await t.one('SELECT xs_course_size($1,$2)',[DEFAULT_COMPANY_ID,String(b.kcid)]);
- await t.one('SELECT xs_start_appointment_for_actor($1,$2,$3,$4)',[DEFAULT_COMPANY_ID,appointmentId,current.id,ownLegacyId(current)]);
- return {bindingId:b.objectId,courseId:b.kcid};
- });}catch(e){fail(409,String(e.message||e));}
- }
|