少儿舞蹈小脚本(20):手机号登录与多角色注册

对于许多初涉小程序开发的伙伴来说,用户系统(登录与注册)往往是第一个需要攻克的难关。传统的用户名密码模式看似简单,但在低代码平台和现代移动应用的生态中,却显得有些“格格不入”。

本文将打破惯性思维,为你详细解析一套更符合低代码平台逻辑、以手机号为核心的用户认证与多角色管理方案。这套方案不仅能优化用户首次登录的体验,还能清晰地管理应用内的不同角色(如“家长”和“老师”),为后续的功能权限管理打下坚实的基础。

在这里插入图片描述

一、核心逻辑:告别传统,拥抱手机号验证码登录

在低代码平台中,用户体系通常被划分为 “内部用户”“外部用户”

  • 内部用户:通常指应用的管理者、运营人员等。他们拥有后台操作权限,可以手动在后台添加账号、设置密码。这类账号数量有限(例如,平台可能只提供100个授权名额),成本较高,不适合作为面向大众的用户体系。
  • 外部用户:即我们应用的主要使用者,如学生家长、老师等。他们的数量庞大,增长迅速。对于外部用户,最佳实践是采用 手机号验证码登录

在这里插入图片描述

为什么选择手机号验证码登录?

  1. 低门槛,高转化:用户无需记忆复杂的账号密码,只需输入手机号和验证码即可完成登录/注册,流程简单快捷,能有效降低新用户的注册门槛。
  2. 自动注册:用户首次通过手机号验证后,系统会自动在“外部用户”体系中为其创建一个账号,省去了繁琐的注册步骤。
  3. 身份唯一性:手机号是天然的、唯一的身份标识(UID),便于我们进行用户身份的识别与关联。
  4. 符合平台规范:无论是微信小程序还是其他主流平台,都对手机号授权登录提供了成熟的官方接口,安全可靠。

我们的核心登录/注册流程如下:

  1. 用户打开我的页面,系统强制做产品级别的认证,此时只是登录到了云开发
  2. 用户通过手机号验证码验证后,进入“我的”页面,检测到用户在用户表里是否存在记录。
  3. 记录不存在则是新用户,引导到角色选择页面
  4. 跳转到对应的注册页面完成注册

关键点:应用拿着这个手机号去系统的 用户表 (User) 中查询。
* 如果查询到数据:说明该用户已注册过,登录成功,直接进入“我的”页面。
* 如果未查询到数据:说明是新用户,此时 不应 立即创建用户信息,而是应该先引导用户选择自己的角色。

二、角色分离:家长与老师的精准区分

在少儿舞蹈小程序中,“家长”和“老师”是两个核心但权限、属性截然不同的角色。家长需要查看孩子的课表、缴费、接收通知;而老师则需要进行点名、发布课堂反馈、管理学员等。

因此,在用户首次登录时,我们必须让其明确自己的角色。

角色选择与注册流程:

  1. 当系统检测到新用户(在用户表中无记录)时,自动跳转到一个 “角色选择页面”
  2. 页面上提供两个清晰的选项:“我是家长”、“我是老师”。
  3. 用户根据自己的身份点击相应的按钮。
  4. 点击后,页面分别跳转到 “家长信息注册页”“老师信息注册页”
  5. 用户在各自的注册页中填写必要的补充信息(例如,家长的宝宝姓名、老师的教龄等)。
  6. 提交信息后,系统会完成以下操作:
    • 用户表 中创建一条新记录,核心字段就是用户的手机号。
    • 同时,在对应的 家长表老师表 中创建一条记录,并与用户表中的记录进行关联。

通过这个流程,我们就完美地将一个通过手机号登录的用户,与具体的业务角色身份(家长或老师)绑定了起来。

三、数据模型:三张表奠定用户系统基石

清晰的数据结构是系统稳定运行的保障。我们需要设计三张核心数据表:用户表、家长表、老师表。

1 用户表 (Users)

这张表是整个用户系统的基础,用于存储所有通过手机验证的用户,作为身份认证的唯一凭证。

字段名 (Field Name)数据类型 (Data Type)描述 (Description)主键/外键 (Key)备注 (Notes)
idString / Integer唯一标识符Primary Key建议使用平台自动生成的唯一ID
phone_numberString用户手机号核心字段,唯一且不能为空
roleString用户角色‘parent’, ‘teacher’
created_atDatetime创建时间记录用户首次注册时间
last_login_atDatetime最后登录时间可用于分析用户活跃度

在这里插入图片描述

2 家长表 (Parents)

用于存储家长的详细业务信息。

字段名 (Field Name)数据类型 (Data Type)描述 (Description)主键/外键 (Key)备注 (Notes)
idString / Integer唯一标识符Primary Key
user_idString / Integer关联的用户IDForeign Key (Users.id)与用户表一一对应
child_nameString宝宝姓名核心业务字段
child_genderString宝宝性别‘男’, ‘女’
child_birthdayDate宝宝生日
contact_nameString家长称谓例如:王女士,李爸爸
created_atDatetime记录创建时间

在这里插入图片描述

3 老师表 (Teachers)

用于存储老师的详细业务信息。

字段名 (Field Name)数据类型 (Data Type)描述 (Description)主键/外键 (Key)备注 (Notes)
idString / Integer唯一标识符Primary Key
user_idString / Integer关联的用户IDForeign Key (Users.id)与用户表一一对应
teacher_nameString老师姓名核心业务字段
genderString性别‘男’, ‘女’
teaching_sinceInteger教龄(年)例如:5
introductionText老师简介
profile_pictureImage URL老师头像
created_atDatetime记录创建时间

在这里插入图片描述

四、API 设计:连接前后端的桥梁

前后端分离的开发模式下,我们需要定义清晰的API接口来处理数据交互。

1 登录/检查用户状态 API

API参照过往教程

2 家长注册 API

/**
* 家长角色注册
* @param {object} params - 传入参数
* @param {string} params.phoneNumber - 用户的手机号
* @param {string} params.childName - 宝宝姓名
* @param {string} params.childGender - 宝宝性别
* @param {string} params.childBirthday - 宝宝生日
* @param {string} params.contactName - 家长称谓
* @param {object} context - 调用上下文
* @returns {Promise<object>} - 返回操作结果
  */
  module.exports = async function (params, context) {
  // --- 1. 参数校验 ---
  const requiredFields = ['phoneNumber', 'childName', 'contactName'];
  for (const field of requiredFields) {
  if (!params[field]) {
  return {
  success: false,
  code: 'INVALID_PARAMS',
  message: `缺少必要的注册信息: ${field}`,
  };
  }
  }
  const { phoneNumber, childName, childGender, childBirthday, contactName } = params;
  // --- 2. 准备数据 ---
  const userData = {
  phone: phoneNumber,
  role: "1", // 明确角色为家长
  };
  // --- 3. 写入数据库(伪事务处理) ---
  // 理想情况下应使用事务,确保两张表要么都成功,要么都失败。
  // 在无事务支持的低代码平台,需要做好失败时的补偿(回滚)操作。
  let createdUser = null;
  try {
  // 3.1 首先创建用户主表记录
  const createUserResult = await context.callModel({
  dataSourceName: 'dance_users',
  methodName: 'wedaCreateV2',
  params: {
  data: userData,
  },
  });
  if (!createUserResult || !createUserResult.id) {
  throw new Error('创建用户主表记录失败');
  }
  createdUser = createUserResult; // 保存创建的用户信息
  // 3.2 准备家长表数据,并关联刚创建的用户ID
  const parentData = {
  user_id: { _id: createdUser.id }, // 关键步骤:建立关联
  child_name: childName,
  child_gender: childGender,
  child_birthday: childBirthday,
  contact_name: contactName,
  };
  // 3.3 创建家长详情表记录
  const createParentResult = await context.callModel({
  dataSourceName: 'dance_parent',
  methodName: 'wedaCreateV2',
  params: {
  data: parentData,
  },
  });
  if (!createParentResult || !createParentResult.id) {
  throw new Error('创建家长详情记录失败');
  }
  // --- 4. 返回成功结果 ---
  return {
  success: true,
  message: '家长注册成功',
  data: {
  userId: createdUser.id,
  parentId: createParentResult.id,
  role: '1',
  },
  };
  } catch (error) {
  console.error('家长注册时发生错误:', error);
  // 回滚逻辑:如果用户主表创建成功,但家长表失败了,则删除已创建的用户主表记录
  if (createdUser && createdUser.id) {
  await context.callModel({
  dataSourceName: 'dance_users',
  methodName: 'wedaDeleteV2', // 假设有删除方法
  params: {
  filter:{
  where:{
  _id:{$eq:createdUser.id}
  }
  }
  },
  });
  console.log(`已回滚删除用户: ${createdUser.id}`);
  }
  return {
  success: false,
  code: 'INTERNAL_ERROR',
  message: '注册失败,请稍后重试',
  };
  }
  };

3 老师注册 API

/**
* 老师角色注册
* @param {object} params - 传入参数
* @param {string} params.phoneNumber - 用户的手机号
* @param {string} params.teacherName - 老师姓名
* @param {string} params.gender - 性别
* @param {number} params.teachingSince - 教龄
* @param {string} params.introduction - 老师简介
* @param {object} context - 调用上下文
* @returns {Promise<object>} - 返回操作结果
  */
  module.exports = async function (params, context) {
  // --- 1. 参数校验 ---
  if (!params.phoneNumber || !params.teacherName) {
  return {
  success: false,
  code: 'INVALID_PARAMS',
  message: '缺少手机号或老师姓名',
  };
  }
  const { phoneNumber, teacherName, gender, teachingSince, introduction } = params;
  // --- 2. 准备数据 ---
  const userData = {
  phone: phoneNumber,
  role: "teacher", // 明确角色为老师
  };
  // --- 3. 写入数据库(伪事务处理) ---
  let createdUser = null;
  try {
  // 3.1 创建用户主表记录
  const createUserResult = await context.callModel({
  dataSourceName: 'dance_users',
  methodName: 'wedaCreateV2',
  params: {
  data: userData,
  },
  });
  if (!createUserResult || !createUserResult.id) {
  throw new Error('创建用户主表记录失败');
  }
  createdUser = createUserResult;
  // 3.2 准备老师表数据,并关联用户ID
  const teacherData = {
  user_id: { _id: createdUser.id }, // 建立关联
  teacher_name: teacherName,
  gender: gender,
  teaching_since: teachingSince,
  introduction: introduction,
  };
  // 3.3 创建老师详情表记录
  const createTeacherResult = await context.callModel({
  dataSourceName: 'dance_teacher',
  methodName: 'wedaCreateV2',
  params: {
  data: teacherData,
  },
  });
  if (!createTeacherResult || !createTeacherResult.id) {
  throw new Error('创建老师详情记录失败');
  }
  // --- 4. 返回成功结果 ---
  return {
  success: true,
  message: '老师注册成功',
  data: {
  userId: createdUser.id,
  teacherId: createTeacherResult.id,
  role: '2',
  },
  };
  } catch (error) {
  console.error('老师注册时发生错误:', error);
  // 回滚逻辑
  if (createdUser && createdUser.id) {
  await context.callModel({
  dataSourceName: 'dance_users',
  methodName: 'wedaDeleteV2', // 假设有删除方法
  params: {
  filter:{
  where:{
  _id:{$eq:createdUser.id}
  }
  }
  },
  });
  console.log(`已回滚删除用户: ${createdUser.id}`);
  }
  return {
  success: false,
  code: 'INTERNAL_ERROR',
  message: '注册失败,请稍后重试',
  };
  }
  };

五、页面搭建:从空白到功能完善

现在,我们将以上逻辑和API串联起来,搭建出实际的前端页面。

1 “我的”页面

  • 核心逻辑:页面加载时,首先检查本地存储中是否存在用户信息(如tokenrole)。
    • 存在:说明用户已登录。直接调用一个 login 接口获取最新的用户信息(例如家长可以看到宝宝信息,老师可以看到自己的排课),并渲染页面。
    • 不存在:显示一个“点击登录/注册”的按钮。
  • UI组件:用户信息展示区(头像、昵称)、功能列表(我的课表、我的订单等)、登录按钮。

具体的逻辑我们其实在购物车下单的时候已经实现,可以翻看过往的教程

2 角色选择页面

点击创建页面的图标,创建角色选择页面
在这里插入图片描述
在页面中添加普通容器,里边添加两个按钮,设置纵向排列,水平垂直居中
在这里插入图片描述
给按钮设置点击事件,打开对应的页面
在这里插入图片描述

3 家长/老师信息填写页面

  • UI:标准的表单页面,包含输入框(宝宝姓名、老师姓名)、选择器(性别、生日)等。
  • 核心逻辑
    1. 页面加载时,绑定系统登录对象的用户手机号。
    2. 用户填写完所有必填项后,点击“提交”按钮。
    3. 前端对表单数据进行校验(如姓名不能为空)。
    4. 校验通过后,调用对应的注册API。
    5. 注册成功后,自动跳转回“我的”页面,此时“我的”页面会显示已登录状态。

界面我们使用表单容器搭建,选择对应的数据模型
在这里插入图片描述
我们无需显示的在页面上放一个手机号的组件,只需要在表单提交的时候传入入参即可
在这里插入图片描述

总结

通过“手机号验证 + 角色选择 + 信息补充”这一套组合拳,我们不仅为少儿舞蹈小程序构建了一个安全、高效且用户体验友好的登录注册系统,更重要的是,我们从一开始就建立了清晰的数据模型和角色划分,为未来实现复杂的业务功能(如排课、点名、家校沟通、权限控制等)铺平了道路。

这种思路充分利用了低代码平台的优势,将复杂的后端逻辑封装在简单的API调用中,让开发者能更专注于前端页面的搭建和业务功能的实现。希望这篇博客能为你提供有价值的参考!

posted @ 2025-10-01 09:22  wzzkaifa  阅读(22)  评论(0)    收藏  举报