lrf 2 years ago
parent
commit
b3836a62c7

+ 6 - 2
app/controller/apply/config/.tempLessonApply.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['school_id', 'lesson_id', 'student_id', 'coach_id', 'result', 'reason', 'img_url'],
+    requestBody: ['money', 'pay_id', 'school_id', 'lesson_id', 'student_id', 'coach_id', 'result', 'reason', 'img_url'],
   },
   destroy: {
     params: ['!id'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['school_id', 'lesson_id', 'student_id', 'coach_id', 'result', 'reason', 'img_url'],
+    requestBody: ['money', 'pay_id', 'school_id', 'lesson_id', 'student_id', 'coach_id', 'result', 'reason', 'img_url'],
   },
   show: {
     parameters: {
@@ -26,6 +26,7 @@ module.exports = {
         student_id: 'student_id',
         coach_id: 'coach_id',
         result: 'result',
+        pay_id: 'pay_id',
       },
       // options: {
       //   "meta.state": 0 // 默认条件
@@ -39,4 +40,7 @@ module.exports = {
       count: true,
     },
   },
+  toPay: {
+    requestBody: ['!id', '!payer_id'],
+  },
 };

+ 3 - 2
app/controller/business/config/.lessonStudent.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status'],
+    requestBody: ['is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status'],
   },
   destroy: {
     params: ['!id'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status'],
+    requestBody: ['is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status'],
   },
   show: {
     parameters: {
@@ -27,6 +27,7 @@ module.exports = {
         money: 'money',
         is_try: 'is_try',
         try_status: 'try_status',
+        is_pay: 'is_pay',
       },
       // options: {
       //   "meta.state": 0 // 默认条件

+ 13 - 7
app/controller/business/config/.payOrder.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['openid', 'school_id', 'user_id', 'pay_for', 'from_id', 'money', 'time', 'order_no', 'desc', 'status'],
+    requestBody: ['openid', 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id', 'money', 'time', 'order_no', 'desc', 'status', 'config'],
   },
   destroy: {
     params: ['!id'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['openid', 'school_id', 'user_id', 'pay_for', 'from_id', 'money', 'time', 'order_no', 'desc', 'status'],
+    requestBody: ['openid', 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id', 'money', 'time', 'order_no', 'desc', 'status', 'config'],
   },
   show: {
     parameters: {
@@ -23,11 +23,8 @@ module.exports = {
         'meta.createdAt@end': 'meta.createdAt@end',
         openid: 'openid',
         school_id: 'school_id',
-        user_id: 'user_id',
-        order_no: 'order_no',
-        status: 'status',
-        'time@start': 'time@start',
-        'time@end': 'time@end',
+        payer_id: 'payer_id',
+        from_id: 'from_id',
       },
       // options: {
       //   "meta.state": 0 // 默认条件
@@ -41,4 +38,13 @@ module.exports = {
       count: true,
     },
   },
+  closeOrder: {
+    requestBody: ['!id'],
+  },
+  toRefund: {
+    requestBody: ['!id'],
+  },
+  toRePay: {
+    requestBody: ['!id'],
+  },
 };

+ 3 - 2
app/model/apply/tempLessonApply.js

@@ -8,9 +8,9 @@ const tempLessonApply = {
   lesson_id: { type: String, required: false, zh: '课程id', ref: 'Lesson', getProp: [ 'title' ] }, //
   student_id: { type: String, required: false, zh: '学员id', ref: 'Student', getProp: [ 'name' ] }, //
   coach_id: { type: String, required: false, zh: '教练id', ref: 'Coach', getProp: [ 'name' ] }, //
-  result: { type: String, required: false, default: '0', zh: '审核结果' }, // 0:未审核;1:审核通过;-1审核失败
-  reason: { type: String, required: false, zh: '审核原因' }, //
   img_url: { type: Array, required: false, zh: '证据' }, //
+  money: { type: Number, required: false, zh: '金额' }, //
+  pay_id: { type: Array, required: false, zh: '支付id', ref: 'PayOrder' }, //
 };
 const schema = new Schema(tempLessonApply, { toJSON: { virtuals: true } });
 schema.index({ id: 1 });
@@ -20,6 +20,7 @@ schema.index({ lesson_id: 1 });
 schema.index({ student_id: 1 });
 schema.index({ coach_id: 1 });
 schema.index({ result: 1 });
+schema.index({ pay_id: 1 });
 
 schema.plugin(metaPlugin);
 module.exports = app => {

+ 2 - 0
app/model/business/lessonStudent.js

@@ -10,6 +10,7 @@ const lessonStudent = {
   money: { type: String, required: false, zh: '缴费金额' }, //
   is_try: { type: String, required: false, default: '0', zh: '是否试课' }, // 0:非试课;1:试课
   try_status: { type: String, default: '0', zh: '试课审核状态' }, // 0:未审核;1:审核通过;2审核拒绝
+  is_pay: { type: String, default: '0', zh: '是否已支付' }, // 0:未支付;1:已支付:-1:支付失败:-2已退款
 };
 const schema = new Schema(lessonStudent, { toJSON: { virtuals: true } });
 schema.index({ id: 1 });
@@ -19,6 +20,7 @@ schema.index({ school_id: 1 });
 schema.index({ student_id: 1 });
 schema.index({ money: 1 });
 schema.index({ is_try: 1 });
+schema.index({ is_pay: 1 });
 
 schema.plugin(metaPlugin);
 module.exports = app => {

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

@@ -6,24 +6,24 @@ const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
 const payOrder = {
   openid: { type: String, required: false, zh: '微信id' }, // 其实和user_id有些重复,但是这里一定要留痕,因为有可能发生更换绑定
   school_id: { type: String, required: false, zh: '学校id', ref: 'School', getProp: [ 'name' ] }, //
-  user_id: { type: String, required: false, zh: '用户id' }, // 不是角色的数据id,是普通用户的那个id,因为付款需要确定openid,和你是什么角色没关系
+  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:上课,写表名等...
   from_id: { type: String, required: false, zh: '关联id' }, // 支付原因的数据id.有就写,没有不写
   money: { type: Number, required: false, zh: '金额' }, //
   time: { type: String, required: false, zh: '时间' }, //
   order_no: { type: String, required: false, zh: '订单号' }, //
   desc: { type: String, required: false, zh: '支付说明' }, //
-  status: { type: String, required: false, default: '0', zh: '支付状态' }, // 0:未支付;1:已支付;-1:支付失败-2:已退款
+  status: { type: String, required: false, default: '0', zh: '支付状态' }, // 0:未支付;1:已支付;-1:支付失败;-2:已退款
+  config: { type: Object, zh: '设置' },
 };
 const schema = new Schema(payOrder, { toJSON: { virtuals: true } });
 schema.index({ id: 1 });
 schema.index({ 'meta.createdAt': 1 });
 schema.index({ openid: 1 });
 schema.index({ school_id: 1 });
-schema.index({ user_id: 1 });
-schema.index({ order_no: 1 });
-schema.index({ time: 1 });
-schema.index({ status: 1 });
+schema.index({ payer_id: 1 });
+schema.index({ from_id: 1 });
 
 schema.plugin(metaPlugin);
 module.exports = app => {

+ 1 - 0
app/router.js

@@ -39,6 +39,7 @@ module.exports = 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/apply/billApply')(app); // 教练费申请
   require('./z_router/apply/tryLessonApply')(app); // 试课申请

+ 35 - 2
app/service/apply/tempLessonApply.js

@@ -3,12 +3,45 @@ const { CrudService } = require('naf-framework-mongoose-free/lib/service');
 const { BusinessError, ErrorCode } = require('naf-core').Error;
 const _ = require('lodash');
 const assert = require('assert');
-
-// 
+const moment = require('moment');
+//
 class TempLessonApplyService extends CrudService {
   constructor(ctx) {
     super(ctx, 'templessonapply');
     this.model = this.ctx.model.Apply.TempLessonApply;
+    this.payOrderService = this.ctx.service.business.payOrder;
+  }
+
+  async beforeCreate(data) {
+    const query = _.pick(data, [ 'school_id', 'lesson_id', 'student_id', 'coach_id' ]);
+    const num = await this.model.count(query);
+    if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '学生已申请临时参加课程');
+    return data;
+  }
+
+  // 去支付,生成支付单
+  async toPay({ id, payer_id }) {
+    const data = await this.model.findById(id);
+    if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到临时上课的申请,无法进行支付');
+    const { student_id, coach_id, lesson_id, school_id, money } = data;
+    const payer_role = payer_id === student_id ? 'Student' : payer_id === coach_id ? 'Coach' : null;
+    if (!payer_role) throw new BusinessError(ErrorCode.DATA_INVALID, '该支付用户不是学生也不是教练');
+    const obj = { payer_id, payer_role, pay_for: 'Lesson', from_id: lesson_id, school_id, money, time: moment().format('YYYY-MM-DD HH:mm:ss'), desc: '私教课' };
+    // 找openid
+    const user = await this.ctx.model.User[payer_role].findById(payer_id).populate('user_id');
+    if (!user) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户信息');
+    const openid = _.get(user, 'user_id.openid');
+    if (!openid) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户微信信息');
+    obj.openid = openid;
+    const res = await this.payOrderService.create(obj);
+    return res;
+  }
+
+  // 处理审核成功的情况
+  async afterUpdate(filter, body, data) {
+
+
+    return data;
   }
 }
 

+ 2 - 1
app/service/business/lesson.js

@@ -25,7 +25,8 @@ class LessonService extends CrudService {
   async afterUpdate(filter, body, data) {
     const { _id: lesson_id, type, status } = data;
     if (status !== '4') return data;
-    // 公开课,每个教师都带着
+    // 公开课,每个教师都带着自己的金额. 按 人头数 * 单价 算钱
+    // 私教课, 按学生实际缴费
 
     return data;
   }

+ 105 - 1
app/service/business/payOrder.js

@@ -3,13 +3,117 @@ const { CrudService } = require('naf-framework-mongoose-free/lib/service');
 const { BusinessError, ErrorCode } = require('naf-core').Error;
 const _ = require('lodash');
 const assert = require('assert');
-
+const moment = require('moment');
 //
 class PayOrderService extends CrudService {
   constructor(ctx) {
     super(ctx, 'payorder');
     this.model = this.ctx.model.Business.PayOrder;
     this.payService = this.ctx.service.wxpay;
+    this.lessonStudentModel = this.ctx.model.Business.LessonStudent;
+  }
+
+  async beforeCreate(data) {
+    if (!_.get(data, 'order_no')) data.order_no = this.getOrderNo();
+    return data;
+  }
+
+  async afterCreate(body, data) {
+    await this.syncData(data);
+    const wxSign = await this.payService.create(data);
+    return { data, wxSign };
+  }
+
+  async afterUpdate(filter, body, data) {
+    await this.syncData(data);
+    return data;
+  }
+  /**
+   * 支付同步
+   * @param {Object} data 支付单数据
+   */
+  async syncData(data) {
+    const pay_for = _.get(data, 'pay_for');
+    if (!pay_for) {
+      // 不知道该去同步哪个表的支付状态,不处理
+      return;
+    }
+    if (pay_for === 'Lesson') {
+      // 因为上课产生的支付,去找lessonStudent,修改指定学生的支付状态
+      const { form_id: _id, status: is_pay } = data;
+      await this.lessonStudentModel.updateOne({ _id }, { is_pay });
+    }
+  }
+
+  /**
+   * 重新支付
+   * @param {String} body 参数体
+   * @property {String} id 数据id
+   */
+  async toRePay({ id }) {
+    const data = await this.model.findById(id);
+    if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付的信息');
+    // 找到这个订单,然后获取到订单号
+    const { order_no } = data;
+    // 利用订单号去查询微信那边,该订单是否还存在
+    const wxOrder = await this.payService.search(order_no);
+    let wxSign;
+    if (_.get(wxOrder, 'trade_state') === 'NOTPAY') {
+      // 未支付就用原数据,去支付
+      wxSign = await this.payService.create(data);
+    } else if (_.get(wxOrder, 'trade_state') === 'SUCCESS') {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单已支付,无需重复支付');
+    } else if (_.get(wxOrder, 'trade_state') === 'USERPAYING') {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单支付中');
+    } else {
+      // 其他情况就当订单被关闭了.直接新订单
+      const order_no = this.getOrderNo();
+      data.order_no = order_no;
+      await data.save();
+      wxSign = await this.payService.create(data);
+    }
+    return { data, wxSign };
+  }
+
+  /**
+   * 申请退款
+   * @param {Object} body 参数体
+   * @property {String} id 支付单的数据id
+   */
+  async toRefund({ id }) {
+    // 查询支付
+    const data = await this.model.findById(id);
+    if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到要退款的支付数据');
+    const wxOrderReq = await this.payService.search(data.order_no);
+    if (wxOrderReq) {
+      if (!wxOrderReq) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未查询到微信支付订单');
+      const wxRefundReq = await this.payService.refund(data.order_no, '退赛');
+      if (wxRefundReq) {
+        if (wxRefundReq.status !== 'REFUND') throw new BusinessError(ErrorCode.SERVICE_FAULT, '退款失败');
+        // 退款成功
+        data.status = '-3';
+        await data.save();
+        await this.syncData(data);
+        return 'ok';
+      }
+    }
+    throw new BusinessError(ErrorCode.SERVICE_FAULT, '查询微信支付订单失败');
+  }
+
+  /**
+   * 关闭订单
+   * @param {String} id 支付id
+   * @return {Object} 关闭订单结果
+   */
+  async closeOrder(id) {
+    const data = await this.model.findById(id);
+    if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到要退款的支付数据');
+    const res = await this.payService.close(data.order_no);
+    return res;
+  }
+
+  getOrderNo() {
+    return `ONCAPP${moment().format('YYYYMMDDHHmmss')}-${_.random(1, 999999)}`;
   }
 }
 

+ 2 - 1
app/z_router/apply/tempLessonApply.js

@@ -7,9 +7,10 @@ const rkey = 'tempLessonApply';
 const ckey = 'apply.tempLessonApply';
 const keyZh = '临时上课申请';
 const routes = [
+  { method: 'post', path: `${rkey}/toPay`, controller: `${ckey}.toPay`, name: `${ckey}ToPay`, zh: `${keyZh}-支付` },
   { 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`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: ['password'], 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}` },
 ];

+ 22 - 0
app/z_router/business/payOrder.js

@@ -0,0 +1,22 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'payOrder';
+const ckey = 'business.payOrder';
+const keyZh = '支付订单';
+const routes = [
+  { method: 'post', path: `${rkey}/toRePay`, controller: `${ckey}.toRePay`, name: `${ckey}toRePay`, zh: `${keyZh}-重新支付订单` },
+  { method: 'post', path: `${rkey}/closeOrder`, controller: `${ckey}.closeOrder`, name: `${ckey}CloseOrder`, zh: `${keyZh}-关闭订单` },
+  { method: 'post', path: `${rkey}/toRefund`, controller: `${ckey}.toRefund`, name: `${ckey}ToRefund`, zh: `${keyZh}-退款` },
+  { 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);
+};

+ 1 - 1
config/config.default.js

@@ -109,7 +109,7 @@ module.exports = appInfo => {
   // 数据库设置
   config.dbName = 'court_v2';
   config.mongoose = {
-    url: `mongodb://120.48.146.1:27017/${config.dbName}`, // 120.48.146.1 127.0.0.1
+    url: `mongodb://127.0.0.1:27017/${config.dbName}`, // 120.48.146.1 127.0.0.1
     options: {
       user: 'admin',
       pass: 'admin',