lrf 2 years ago
parent
commit
e01b11cdd8

+ 44 - 0
app/controller/business/config/.costDetail.js

@@ -0,0 +1,44 @@
+module.exports = {
+  create: {
+    requestBody: ['school_id', 'payer_role', 'payer_id', 'pay_for', 'from_id', 'type', 'pay_id'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['school_id', 'payer_role', 'payer_id', 'pay_for', 'from_id', 'type', 'pay_id'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        school_id: 'school_id',
+        payer_role: 'payer_role',
+        payer_id: 'payer_id',
+        pay_for: 'pay_for',
+        from_id: 'from_id',
+        type: 'type',
+        pay_id: 'pay_id',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 13 - 0
app/controller/business/costDetail.js

@@ -0,0 +1,13 @@
+'use strict';
+const meta = require('./config/.costDetail.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+// 
+class CostDetailController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.costDetail;
+  }
+}
+module.exports = CrudController(CostDetailController, meta);

+ 33 - 0
app/model/business/costDetail.js

@@ -0,0 +1,33 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+const MoneyPlugin = require('naf-framework-mongoose-free/lib/model/type-money-plugin');
+
+// 消费明细
+const costDetail = {
+  school_id: { type: String, required: false, zh: '学校id', ref: 'School', getProp: [ 'name' ] }, //
+  payer_role: { type: String, required: false, zh: '消费用户角色i' }, //
+  payer_id: { type: String, required: false, zh: '消费用户角色id', refPath: 'payer_role' }, //
+  pay_for: { type: String, required: false, zh: '消费原因' }, //
+  from_id: { type: String, required: false, zh: '消费原因id', refPath: 'pay_for' }, //
+  type: { type: String, required: false, default: '0', zh: '扣款来源' }, // 0:直接支付;1余额
+  pay_id: { type: String, required: false, zh: '支付id', ref: 'PayOrder' }, //
+};
+const schema = new Schema(costDetail, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ school_id: 1 });
+schema.index({ payer_role: 1 });
+schema.index({ payer_id: 1 });
+schema.index({ pay_for: 1 });
+schema.index({ from_id: 1 });
+schema.index({ type: 1 });
+schema.index({ pay_id: 1 });
+
+schema.plugin(metaPlugin);
+schema.plugin(MoneyPlugin({ zh: '消费金额', required: false }));
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('CostDetail', schema, 'costDetail');
+};

+ 1 - 1
app/model/business/payOrder.js

@@ -9,7 +9,7 @@ const payOrder = {
   school_id: { type: String, required: false, zh: '学校id', ref: 'School', getProp: [ 'name' ] }, //
   payer_id: { type: String, required: false, zh: '支付的用户id', refPath: 'payer_role' }, // 由payer_role决定是教练还是学员
   payer_role: { type: String, required: false, zh: '支付的用户角色' }, //
-  pay_for: { type: String, required: false, zh: '支付原因' }, // lesson:上课,写表名等...; 学生上课(公开课/私教课):lessonStudent; 学生临时上课:tempLessonApply
+  pay_for: { type: String, required: false, zh: '支付原因' }, // lesson:上课,写表名等...; 学生上课(公开课/私教课):lessonStudent; 学生临时上课:tempLessonApply;充值:charge
   from_id: { type: String, required: false, zh: '关联id' }, // 支付原因的数据id.有就写,没有不写
   time: { type: String, required: false, zh: '时间' }, //
   order_no: { type: String, required: false, zh: '订单号' }, //

+ 2 - 2
app/router.js

@@ -33,14 +33,14 @@ module.exports = app => {
   require('./z_router/relation/relationCoachSchool')(app); // 教练与学校关系
   require('./z_router/relation/relationStudentSchool')(app); // 学员与学校关系
   require('./z_router/relation/studentApplyForSchool')(app); // 学员入学申请
-  // require('./z_router/business/lessonPrivate')(app); // 私教课
-  // require('./z_router/business/lessonPublic')(app); // 公开课
+
   require('./z_router/business/lesson')(app); // 课程
   require('./z_router/business/lessonCoach')(app); // 课程-教练
   require('./z_router/business/lessonStudent')(app); // 课程-学员
   require('./z_router/business/coachInBill')(app); // 教练费明细
   require('./z_router/business/payOrder')(app); // 支付订单
   require('./z_router/business/charge')(app); // 充值
+  require('./z_router/business/costDetail')(app); // 消费明细
 
   require('./z_router/apply/billApply')(app); // 教练费申请
   require('./z_router/apply/tryLessonApply')(app); // 试课申请

+ 2 - 1
app/schedule/task.js

@@ -6,6 +6,7 @@ module.exports = {
     immediate: true,
   },
   async task(ctx) {
-    console.log('line 8 in function:');
+    // console.log('line 8 in function:');
+    // 清除
   },
 };

+ 15 - 0
app/service/business/costDetail.js

@@ -0,0 +1,15 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const _ = require('lodash');
+const assert = require('assert');
+
+//
+class CostDetailService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'costdetail');
+    this.model = this.ctx.model.Business.CostDetail;
+  }
+}
+
+module.exports = CostDetailService;

+ 75 - 6
app/service/business/payOrder.js

@@ -17,12 +17,75 @@ class PayOrderService extends CrudService {
     this.userModel = this.ctx.model.User.User;
     this.studentModel = this.ctx.model.User.Student;
     this.coachModel = this.ctx.model.User.Coach;
+    this.rcsModel = this.ctx.model.Relation.RelationCoachSchool;
+    this.rssModel = this.ctx.model.Relation.RelationStudentSchool;
+    this.costDetailModel = this.ctx.model.Business.CostDetail;
   }
 
   async beforeCreate(data) {
     if (!_.get(data, 'order_no')) data.order_no = this.getOrderNo();
+    const { data: nd, next } = await this.checkSurplus(data);
+    if (!next) throw new BusinessError(0, '扣除余额成功');
+    data = nd;
     return data;
   }
+  // 检查是否扣除余额
+  async checkSurplus(data) {
+    const pay_for = _.get(data, 'pay_for');
+    // 充值不检查余额
+    if (pay_for && pay_for === 'charge') return { data, next: true };
+    const payer_id = _.get(data, 'payer_id');
+    const payer_role = _.get(data, 'payer_role');
+    let costDetailData;
+    let relation;
+    // 没有要支付的人及其角色,那就找不到余额,返回下单
+    if (!(payer_id && payer_role)) return { data, next: true };
+    // 有支付的人,可以查查这个人的余额
+    if (payer_role === 'Student') relation = await this.rssModel.findOne({ student_id: payer_id });
+    else if (payer_role === 'Coach') relation = await this.rcsModel.findOne({ coach_id: payer_id });
+    // 没找到关系,那就直接返回正常下单缴费
+    if (!relation) return { data, next: true };
+    // 有关系,找余额
+    const { money: surplus } = relation;
+    const { money } = data;
+    // 没有余额,或者余额没钱,返回正常下单缴费
+    if (surplus && surplus <= 0) return { data, next: true };
+    // 有余额,看余额够不够
+    if (surplus >= money) {
+      try {
+        // 余额够, 无需生成订单,直接去使用余额缴费
+        const costDetail = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id' ]);
+        // 本次扣除余额的金额就是本次需要支付的金额
+        costDetail.money = money;
+        costDetail.type = '1';
+        costDetailData = await this.costDetailModel.create(costDetail);
+        // 余额需要扣除本次支付的费用
+        relation.money = surplus - money;
+        await relation.save();
+        return false;
+      } catch (error) {
+        // 抓取数据库创建和修改的异常,
+        // 如果 costDetailData没有值,说明是创建消费记录时发生的错误,不需要做回滚处理;如果有值,那就删除
+        if (costDetailData) {
+          await this.costDetailModel.deleteOne({ _id: costDetailData._id });
+        }
+        // 如果是余额修改错误,上面的回滚删除就足够了.因为保存新余额发生错误,余额不会保存,只要把消费记录删了,就回到原样了
+      }
+    } else {
+      // 余额不够,在config里记录部分使用了余额,再进行下单,下单完后,生成余额的扣款
+      const needPay = surplus - money;
+      const costDetail = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id' ]);
+      costDetail.money = surplus;
+      costDetail.type = '1';
+      if (!data.config) data.config = {};
+      // 添加余额的设置, 等支付成功后,再生成余额的消费记录
+      data.config.useSurplus = true;
+      data.config.costDetail = costDetail;
+      data.money = needPay;
+    }
+
+    return { data, next: true };
+  }
 
   async afterCreate(body, data) {
     await this.syncData(data);
@@ -40,10 +103,8 @@ class PayOrderService extends CrudService {
    */
   async syncData(data) {
     const pay_for = _.get(data, 'pay_for');
-    if (!pay_for) {
-      // 不知道该去同步哪个表的支付状态,不处理
-      return;
-    }
+    // 不知道该去同步哪个表的支付状态,不处理
+    if (!pay_for) return;
     const { from_id: _id, status: is_pay, money, _id: pay_id, config } = data;
     if (pay_for === 'lessonStudent') {
       // 因为上课产生的支付,去找lessonStudent,修改指定学生的支付状态
@@ -59,8 +120,16 @@ class PayOrderService extends CrudService {
       const obj = { lesson_id, student_id, school_id, is_pay, pay_id, config, money };
       await this.lessonStudentService.create(obj);
     } else if (pay_for === 'charge') {
-      // 充值
-      await this.chargeModel.updateOne({ _id }, { is_pay });
+      // 充值记录,找到充值记录,没有就生成
+      const chargeData = await this.chargeModel.findOne({ pay_id });
+      if (chargeData) {
+        // 有就修改
+        chargeData.is_pay = is_pay;
+        await chargeData.save();
+      } else {
+        const obj = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_id' ]);
+        await this.chargeModel.create({ ...obj, is_pay, time: moment().format('YYYY-MM-DD HH:mm:ss') });
+      }
     }
   }
 

+ 19 - 0
app/z_router/business/costDetail.js

@@ -0,0 +1,19 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'costDetail';
+const ckey = 'business.costDetail';
+const keyZh = '消费明细';
+const routes = [
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};