lessonStudent.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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 moment = require('moment');
  7. //
  8. class LessonStudentService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'lessonstudent');
  11. this.model = this.ctx.model.Business.LessonStudent;
  12. this.rscModel = this.ctx.model.Relation.RelationStudentCoach;
  13. this.lessonCoachModel = this.ctx.model.Business.LessonCoach;
  14. this.lessonModel = this.ctx.model.Business.Lesson;
  15. this.payOrderService = this.ctx.service.business.payOrder;
  16. this.payOrderModel = this.ctx.model.Business.PayOrder;
  17. this.billModel = this.ctx.model.Business.Bill;
  18. }
  19. async checkCanUse({ school_id, student_id, lesson_id }) {
  20. const num = await this.model.count({ school_id, student_id, lesson_id, $or: [{ is_try: '0', is_pay: { $ne: '-3' } }, { is_try: '1' }] });
  21. if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '已存在有效的报名信息');
  22. return true;
  23. }
  24. async beforeCreate(body) {
  25. const { lesson_id, school_id, student_id } = body;
  26. // 检查是否已经有数据
  27. const data = await this.model.findOne({ lesson_id, school_id, student_id, $or: [{ is_try: '0', is_pay: { $ne: '-3' } }, { is_try: '1' }] });
  28. // 数据已存在
  29. if (data) throw new BusinessError(ErrorCode.DATA_EXISTED, '您已报名');
  30. // 数据不存在
  31. const { is_try } = body;
  32. // 检查是否使用试听名额
  33. if (is_try === '1') {
  34. const num = await this.model.count({ student_id, is_try: '1' });
  35. if (num > 0) throw new BusinessError(ErrorCode.SERVICE_FAULT, '您已使用过免费试听的机会');
  36. }
  37. return body;
  38. }
  39. // 计算价格
  40. async toComputed({ lesson_id, student_id }) {
  41. // 通过课程id找到该课的所有教练
  42. const lesson = await this.lessonModel.findById(lesson_id);
  43. if (!lesson) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到数据');
  44. const data = JSON.parse(JSON.stringify(lesson));
  45. const lessonMoney = _.get(data, 'money');
  46. const lessonType = _.get(data, 'type');
  47. if (!lessonMoney) throw new BusinessError(ErrorCode.DATA_INVALID, '课程设置错误,缺少教练费');
  48. // lesson中的money: 是学生应缴费用;
  49. // lessonStudent中的money:是学生应缴费用(公开课不计算,私教课需要查下有没有教师的优惠);
  50. // lessonCoach中的money: 是教师公开课每人应收的费用
  51. // 学生应缴计算方式: 公开课:lesson中的money; 私教课 lesson中的money - 和该教练的优惠 (可以没有)
  52. // 教练应收计算方式: 公开课: lessonCoach中设置的单价*人数; 私教课: (lesson的money - 对该学生的优惠) 之和
  53. if (lessonType === '0') {
  54. // 公开课
  55. data.real_money = lessonMoney;
  56. } else {
  57. // 找教练和学生的设置
  58. // 虽然用find,但实际上只有一个教练,但凡有2个,就应该不是私教课了
  59. const lessonCoachs = await this.lessonCoachModel.find({ lesson_id });
  60. if (lessonCoachs.length <= 0) return data;
  61. const coach_ids = lessonCoachs.map(i => i.coach_id);
  62. const rsc = await this.rscModel.findOne({ coach_id: coach_ids, student_id });
  63. if (rsc) {
  64. const discountType = _.get(rsc, 'config.discount_type'); // fixed;subtract;discount
  65. const number = _.get(rsc, 'config.number');
  66. if (discountType && number) {
  67. // 优惠目前设置3种: 固定金额; ${num}(num>1):减num元; x折; 打x折(money * (x/100))
  68. if (discountType === 'fixed') {
  69. // 固定金额
  70. data.real_money = number >= 0 ? number : 0;
  71. } else if (discountType === 'subtract') {
  72. data.real_money = lessonMoney - number >= 0 ? lessonMoney - number : 0;
  73. } else if (discountType === 'discount') {
  74. data.real_money = _.round(_.multiply(lessonMoney, _.divide(number, 10)), 2);
  75. }
  76. data.config = _.get(rsc, 'config');
  77. }
  78. }
  79. }
  80. return data;
  81. }
  82. // 退课,将钱退至余额
  83. async toRefund({ lesson_id, student_id }) {
  84. const data = await this.model.findOne({ lesson_id, student_id });
  85. if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到数据');
  86. if (data.is_pay === '-3') throw new BusinessError(ErrorCode.DATA_INVALID, '已经完成退课,无法再次退课');
  87. if (data.is_try === '1') {
  88. // 试课 修改退款状态, 而不需要退钱,同时也浪费了试课机会
  89. data.is_pay = '-3';
  90. await data.save();
  91. return;
  92. }
  93. // 正常交钱的课,将 pay_id 的status 修改为 -3, 同时触发修改这条数据,但是没有退款;
  94. await this.payOrderService.updateOne({ _id: data.pay_id }, { status: '-3' });
  95. const payOrder = await this.payOrderModel.findById(data.pay_id);
  96. // 再生成账单记录
  97. const obj = _.pick(payOrder, [ 'school_id', 'payer_role', 'payer_id', 'pay_for', 'from_id' ]);
  98. obj.type = '2';
  99. obj.is_pay = '1';
  100. obj.time = moment().format('YYYY-MM-DD HH:mm:ss');
  101. await this.billModel.create(obj);
  102. }
  103. }
  104. module.exports = LessonStudentService;