lesson.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const _ = require('lodash');
  5. const assert = require('assert');
  6. const Transaction = require('mongoose-transactions');
  7. const moment = require('moment');
  8. //
  9. class LessonService extends CrudService {
  10. constructor(ctx) {
  11. super(ctx, 'lesson');
  12. this.model = this.ctx.model.Business.Lesson;
  13. this.lessonCoachModel = this.ctx.model.Business.LessonCoach;
  14. this.lessonStudentModel = this.ctx.model.Business.LessonStudent;
  15. this.coachInBillService = this.ctx.service.business.coachInBill;
  16. this.lessonStudentModel = this.ctx.model.Business.LessonStudent;
  17. this.tempLessonApplyModel = this.ctx.model.Apply.TempLessonApply;
  18. this.rssModel = this.ctx.model.Relation.RelationStudentSchool;
  19. this.payOrderModel = this.ctx.model.Business.PayOrder;
  20. this.payService = this.ctx.service.wxpay;
  21. this.tran = new Transaction();
  22. }
  23. /**
  24. * 处理下课的课程进行结算
  25. * @param {Object} filter 修改条件
  26. * @param {Object} body 修改内容
  27. * @param {Object} data 修改后的结果
  28. * @return {Object} 返回整理后的内容
  29. */
  30. async afterUpdate(filter, body, data) {
  31. const { _id: lesson_id, type, status, school_id } = data;
  32. if (status !== '4') return data;
  33. if (status === '-1') {
  34. await this.refundThisLesson(data);
  35. return data;
  36. }
  37. const arr = [];
  38. // if (type === '0') {
  39. // // 公开课,每个教师都带着自己的金额. 按 人头数 * 单价 算钱
  40. // const lessonCoachList = await this.lessonCoachModel.find({ lesson_id });
  41. // // 这节课付了款的学生总数
  42. // const studentNum = await this.lessonStudentModel.count({ lesson_id, is_pay: '1' });
  43. // const obj = { lesson_id, type, school_id };
  44. // for (const c of lessonCoachList) {
  45. // const { coach_id, money } = c;
  46. // const total = money * studentNum;
  47. // arr.push({ ...obj, coach_id, money: total });
  48. // }
  49. // } else {
  50. // // 私教课, 按学生实际缴费
  51. // const lessonCoach = await this.lessonCoachModel.findOne({ lesson_id });
  52. // const studentList = await this.lessonStudentModel.find({ lesson_id });
  53. // const obj = { lesson_id, type, school_id };
  54. // obj.coach_id = lessonCoach.coach_id;
  55. // const total = studentList.reduce((p, n) => p + (n.money || 0), 0);
  56. // obj.money = total;
  57. // arr.push(obj);
  58. // }
  59. // // 创建,如果有该学校,这节课的这个教练已经有数据了,就不需要生成了
  60. // for (const i of arr) {
  61. // try {
  62. // await this.coachInBillService.create(i);
  63. // } catch (error) {
  64. // // 不是数据有问题,就是有数据了,要不一般不会出错
  65. // }
  66. // }
  67. return data;
  68. }
  69. /**
  70. * 退课,退款(能退到账户余额就退到账户余额,不能的就直接走退款接口)
  71. * @param {Lesson} lesson 课程信息
  72. */
  73. async refundThisLesson(lesson) {
  74. const lesson_id = _.get(lesson, '_id');
  75. if (!lesson_id) return;
  76. try {
  77. // 取出学生id,支付单id,缴费金额
  78. const studentList = await this.lessonStudentModel.find({ lesson_id, is_pay: '1', is_try: '0' }, 'student_id, pay_id, money, school_id');
  79. for (const s of studentList) {
  80. // 修改支付状态,返回金额
  81. const r = await this.refundByRrs(s, this.tran);
  82. // 没有关系,那就直接去用支付订单退款
  83. if (!r) await this.refundByPayOrder(s, this.tran);
  84. }
  85. const tempLessonList = await this.tempLessonApplyModel.find({ lesson_id, is_pay: '1' }, 'student_id, school_id, money,pay_id,');
  86. for (const t of tempLessonList) {
  87. // 如果有学生id,说明是有账号的,退余额
  88. const r = await this.refundByTempLesson(t, this.tran);
  89. // 没有学生id,需要原路返回
  90. if (!r) await this.refundByPayOrder(t, this.tran);
  91. }
  92. await this.tran.run();
  93. } catch (error) {
  94. await this.tran.rollback();
  95. console.error(error);
  96. } finally {
  97. this.tran.clean();
  98. }
  99. }
  100. /**
  101. * 退款至余额
  102. * @param {Object} lessonStudent 学生上课报名信息
  103. * @param {Transaction} tran 数据库事务
  104. */
  105. async refundByRrs(lessonStudent, tran) {
  106. const { school_id, student_id, pay_id, money, _id } = lessonStudent;
  107. const rss = await this.rssModel.find({ school_id, student_id });
  108. if (!rss) return false;
  109. const surplus = _.get(rss, 'money');
  110. const newMoney = this.ctx.plus(surplus, money);
  111. // 退款
  112. tran.update('RelationStudentSchool', rss._id, { money: newMoney });
  113. // 改状态
  114. tran.update('LessonStudent', lessonStudent._id, { is_pay: '-3' });
  115. tran.update('PayOrder', pay_id, { is_pay: '-3' });
  116. // 生成账单数据
  117. const billData = {
  118. school_id,
  119. payer_id: student_id,
  120. payer_role: 'Student',
  121. pay_for: 'LessonStudent',
  122. from_id: _id,
  123. type: '2',
  124. pay_id,
  125. is_pay: '1',
  126. time: moment().format('YYYY-MM-DD HH:mm:ss'),
  127. money,
  128. };
  129. tran.insert('Bill', billData);
  130. return true;
  131. }
  132. /**
  133. * 退临时上课申请至余额
  134. * @param {Object} tempLessonApply 临时上课申请数据
  135. * @param {Transaction} tran 数据库事务
  136. */
  137. async refundByTempLesson(tempLessonApply, tran) {
  138. const { school_id, student_id, pay_id, money, _id } = tempLessonApply;
  139. if (!student_id) return false;
  140. const rss = await this.rssModel.find({ school_id, student_id });
  141. if (!rss) return false;
  142. const surplus = _.get(rss, 'money');
  143. const newMoney = this.ctx.plus(surplus, money);
  144. // 退款
  145. tran.update('RelationStudentSchool', rss._id, { money: newMoney });
  146. // 改状态
  147. tran.update('TempLessonApply', _id, { is_pay: '-3' });
  148. tran.update('PayOrder', pay_id, { is_pay: '-3' });
  149. // 生成账单数据
  150. const billData = {
  151. school_id,
  152. payer_id: student_id,
  153. payer_role: 'Student',
  154. pay_for: 'TempLessonApply',
  155. from_id: _id,
  156. type: '2',
  157. pay_id,
  158. is_pay: '1',
  159. time: moment().format('YYYY-MM-DD HH:mm:ss'),
  160. money,
  161. };
  162. tran.insert('Bill', billData);
  163. return true;
  164. }
  165. /**
  166. * 原路退款, 因为没有关系,才走原路退款的,所以别查关系了,没有滴
  167. * @param {Object} data 学生上课报名信息
  168. * @param {Transaction} tran 数据库事务
  169. */
  170. async refundByPayOrder(data, tran) {
  171. const { pay_id, school_id } = data;
  172. const payOrder = await this.payOrderModel.findById(pay_id);
  173. if (!payOrder) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付单信息');
  174. const order_no = _.get(data, 'order_no');
  175. const pay_for = _.get(payOrder, 'pay_for');
  176. const from_id = _.get(payOrder, 'from_id');
  177. if (!order_no) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付单号');
  178. await this.payService.refund(order_no, '课程取消');
  179. // 修改状态
  180. tran.update('PayOrder', pay_id, { is_pay: '-3' });
  181. tran.update(pay_for, from_id, { is_pay: '-3' });
  182. // 生成账单
  183. const billData = {
  184. school_id,
  185. pay_for,
  186. from_id,
  187. type: '2',
  188. pay_id,
  189. is_pay: '1',
  190. time: moment().format('YYYY-MM-DD HH:mm:ss'),
  191. };
  192. tran.insert('Bill', billData);
  193. }
  194. }
  195. module.exports = LessonService;