引言:报名,销售旅程的终点,机构运营的起点

在之前的教程中,我们循序渐进地搭建了招生CRM系统的各个模块,从线索管理到跟进记录,再到试听安排。现在,我们来到了整个招生业务的终点,也是教培机构运营的起点——报名管理

报名不仅仅是数据记录,它涉及多方面的复杂逻辑:

  • 学员转化
    :将潜在线索转化为正式学员。
  • 课程与课时
    :记录学员报名的具体课程、费用和课时数,为后续排课消课提供数据基础。
  • 业绩计算与提成
    :记录销售顾问的提成,并处理复杂的退费扣减逻辑,确保财务数据的可追溯性。
  • 财务处理
    :处理收款和可能发生的退费。

本篇作为本套教程的收官之作,我们将搭建一个完整的报名管理模块,打通从CRM到实际教务管理的最后一公里,展现一个业务的完整闭环。


第一部分:数据模型搭建——报名管理的核心基石

为了支持报名管理的复杂逻辑,我们需要新增几个关键的数据模型:学生表 (Students)课程表 (Courses)报名表 (Enrollments) 和 提成表 (Commissions)

1.1 学生表 (Students)

该表用于将线索转化为正式学员,存储学员的永久信息。

字段名
数据类型
长度/约束
描述
备注
id INT

 / UUID
PK, AI
学员唯一标识符
主键
full_name VARCHAR
100
学员姓名
继承自线索表
phone_number VARCHAR
20
联系电话
继承自线索表
source_channel VARCHAR
100
来源渠道
继承自线索表
created_at DATETIME

成为学员时间
自动记录

添加字段,学员姓名(full_name),类型选择文本

添加字段,联系电话(phone_number),类型选择电话

添加字段,来源渠道(source_channel),类型选择枚举

1.2 课程表 (Courses)

该表用于存储教培机构的课程信息,是报名表的数据来源。

字段名
数据类型
长度/约束
描述
备注
id INT

 / UUID
PK, AI
课程唯一标识符
主键
course_name VARCHAR
100
课程名称
例如:“团体小课-乒乓球”
total_price FLOAT

课程总价

total_hours INT

总课时数

commission_rate FLOAT

销售提成比例
例如:0.05 代表 5%

添加字段,课程名称(course_name),类型选择文本

添加字段,课程总价(total_price),类型选择数字

添加字段,总课时数(total_hours),类型选择数字

添加字段,销售提成比例(commission_rate),类型选择数字

1.3 报名表 (Enrollments)

这是连接所有业务的核心表,将学员、课程、顾问和财务信息关联起来。

字段名
数据类型
长度/约束
描述
备注
id INT

 / UUID
PK, AI
报名记录ID
主键
student_id INT

 / UUID
FK to Students.id
学员ID
外键
course_id INT

 / UUID
FK to Courses.id
课程ID
外键
adviser_id INT

 / UUID
FK to SalesAdvisers.id
课程顾问ID
外键
lead_id INT

 / UUID
FK to Lead.id
线索ID
外键
enrollment_at DATETIME

报名时间
自动记录
actual_payment FLOAT

实际支付金额

payment_status ENUM
(‘paid’, ‘partial’, ‘refunded’)
支付状态
已付、部分支付、已退费
total_hours_left INT

剩余课时数
初始值为课程总课时,用于消课

添加字段,学员ID(student_id),类型选择关联关系,关联到学生表

添加字段,课程ID (course_id),类型选择关联关系,关联到课程表

添加字段,课程顾问ID(adviser_id),类型选择关联关系,关联到课程顾问ID

添加字段,线索ID(lead_id),类型选择关联关系,关联到线索表

添加字段,报名时间(enrollment_at),类型选择日期时间


添加字段,实际支付金额(actual_payment),类型选择数字


添加字段,支付状态(payment_status),类型选择枚举

添加字段,剩余课时数(total_hours_left),类型选择数字

1.4 提成表 (Commissions)

该表用于记录每一笔报名产生的提成,并支持退费扣减的追溯。通过不覆盖历史记录,确保了财务核算的完整性。

字段名
数据类型
长度/约束
描述
备注
id INT

 / UUID
PK, AI
提成记录ID
主键
enrollment_id INT

 / UUID
FK to Enrollments.id
报名记录ID
外键
adviser_id INT

 / UUID
FK to SalesAdvisers.id
课程顾问ID
外键
commission_amount FLOAT

提成金额
可能是正数(提成)或负数(扣减)
adjustment_type ENUM
(‘initial’, ‘refund_deduction’)
调整类型
用于区分是初始提成还是退费扣减
related_commission_id INT

 / UUID
NULLABLE
关联提成ID
退费扣减记录时,指向原始提成ID
created_at DATETIME

记录时间
自动记录

添加字段,报名记录ID(enrollment_id),类型选择关联关系,关联到报名表

添加字段,课程顾问ID(adviser_id),类型选择关联关系,关联到销售顾问表

添加字段,提成金额(commission_amount),类型选择数字


添加字段,调整类型(adjustment_type),类型选择枚举

添加字段,关联提成ID(related_commission_id),类型选择关联关系,关联到提成表


第二部分:课程顾问端——转报名功能实现

在课程顾问的“我的线索”页面,当线索试听完成后,我们将提供一个“转报名”按钮。

2.1 “转报名”弹窗表单与核心逻辑

在数据表格的操作列添加一个报名按钮

选择页面组件,添加弹窗组件

弹窗内容下添加表单容器,数据模型选择报名表

给课程顾问ID绑定全局变量的数据标识

给线索ID绑定数据,绑定为弹窗组件的入参

给报名按钮配置点击事件,打开弹窗,传入数据表格所在行的数据标识

2.2 核心功能实现:转报名云函数

云函数代码示例
创建一个名为 enrollStudent 的云函数。

添加入参

添加如下代码

module.exports=asyncfunction(params, context){
try{
const{ leadId, adviserId, courseId, actualPayment, paymentStatus }= params;

// 参数校验...

手把手教你搭建一套“免费又好用”的招生CRM系统(七):报名管理——从CRM到教务的完美衔接
// 1. 查询线索信息,用于创建学生记录
const leadRecord =await context.callModel({
dataSourceName:'Leads_jw',
methodName:'wedaGetItemV2',// 获取单条数据
params:{
select:{"$master":true},
filter:{// 使用 filter 包裹 where
where:{
_id:{
$eq: leadId // 使用 $eq 比较运算符
}
}
}
}
});
if(!leadRecord || Object.keys(leadRecord).length==0)thrownewError('Lead not found.');

// 2. 查询课程信息,用于计算提成和课时
const courseRecord =await context.callModel({
dataSourceName:'Courses_jw',
methodName:'wedaGetItemV2',// 获取单条数据
params:{
select:{"$master":true},
filter:{// 使用 filter 包裹 where
where:{
_id:{
$eq: courseId // 使用 $eq 比较运算符
}
}
}
}
});
if(!courseRecord || Object.keys(courseRecord).length==0)thrownewError('Course not found.');

// 3. 创建学生记录(如果不存在)
const studentData ={
full_name: leadRecord.full_name,
phone_number: leadRecord.phone_number,
source_channel: leadRecord.source_channel,
};
const studentResult =await context.callModel({
dataSourceName:'Students_jw',
methodName:'wedaCreateV2',
params:{data: studentData },
});
const studentId = studentResult.id;

// 4. 创建报名记录
const enrollmentData ={
student_id:{_id:studentId},
course_id:{_id:courseId},
adviser_id:{_id:adviserId},
lead_id:{_id:leadId},
actual_payment: actualPayment,
payment_status: paymentStatus,
enrollment_at:Date.now(),
total_hours_left: courseRecord.total_hours,// 剩余课时等于课程总课时
};
const enrollmentResult =await context.callModel({
dataSourceName:'Enrollments',
methodName:'wedaCreateV2',
params:{data: enrollmentData },
});
const enrollmentId = enrollmentResult.id;

// 5. 计算并创建初始提成记录
const commissionAmount = actualPayment * courseRecord.commission_rate;
const commissionData ={
enrollment_id:{_id:enrollmentId},
adviser_id:{_id:adviserId},
commission_amount: commissionAmount,
adjustment_type:'1',// 标记为初始提成
};
await context.callModel({
dataSourceName:'Commissions',
methodName:'wedaCreateV2',
params:{data: commissionData },
});

// 6. 更新线索状态为已报名
await context.callModel({
dataSourceName:'Leads_jw',
methodName:'wedaUpdateV2',
params:{
data:{status:'5'},
filter:{where:{_id:{$eq: leadId }}}
},
});

return{success:true,data:{ studentId, enrollmentId }};
}catch(error){
return{success:false,message: error.message };
}
};

2.3 调用云函数

选中表单容器,设置提交事件

讲原理的方法改为我们的API的报名方法

修改入参,改为如下的结构

({leadId:$w.form3.submitParams.data.lead_id._id,
adviserId:$w.form3.submitParams.data.adviser_id?._id,
courseId:$w.form3.submitParams.data.course_id._id,
actualPayment:$w.form3.submitParams.data.actual_payment,
paymentStatus:$w.form3.submitParams.data.actual_payment
})

总结:从CRM到教务的呈上启下

恭喜!通过本篇教程,我们为招生CRM系统构建了最后的堡垒——报名管理。我们不仅实现了从线索到正式学员的转化,更重要的是,我们通过多张数据表之间的联动和更严谨的提成追溯设计,为后续的教务管理和财务核算打下了坚实的数据基础。

  • 数据闭环
    :线索、跟进、试听、报名、提成,所有业务环节的数据都已贯通。
  • 财务严谨性
    :通过“新增记录不覆盖”的设计,确保了提成数据的完整可追溯。
  • 功能扩展
    报名表中的total_hours_left字段,为后续的排课、消课、课时统计等教务功能预留了关键数据接口。

至此,我们的“免费又好用”的招生CRM系统系列教程圆满结束。希望本套教程能够帮助您快速掌握低代码平台的应用搭建技巧,并理解如何将复杂的业务逻辑分解并实现。