少儿舞蹈小程序(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)
id String / Integer 唯一标识符 Primary Key 建议使用平台自动生成的唯一ID
phone_number String 用户手机号 核心字段,唯一且不能为空
role String 用户角色 'parent', 'teacher'
created_at Datetime 创建时间 记录用户首次注册时间
last_login_at Datetime 最后登录时间 可用于分析用户活跃度

2 家长表 (Parents)

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

字段名 (Field Name) 数据类型 (Data Type) 描述 (Description) 主键/外键 (Key) 备注 (Notes)
id String / Integer 唯一标识符 Primary Key
user_id String / Integer 关联的用户ID Foreign Key (Users.id) 与用户表一一对应
child_name String 宝宝姓名 核心业务字段
child_gender String 宝宝性别 '男', '女'
child_birthday Date 宝宝生日
contact_name String 家长称谓 例如:王女士,李爸爸
created_at Datetime 记录创建时间

3 老师表 (Teachers)

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

字段名 (Field Name) 数据类型 (Data Type) 描述 (Description) 主键/外键 (Key) 备注 (Notes)
id String / Integer 唯一标识符 Primary Key
user_id String / Integer 关联的用户ID Foreign Key (Users.id) 与用户表一一对应
teacher_name String 老师姓名 核心业务字段
gender String 性别 '男', '女'
teaching_since Integer 教龄(年) 例如:5
introduction Text 老师简介
profile_picture Image URL 老师头像
created_at Datetime 记录创建时间

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

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

1 登录/检查用户状态 API

API参照过往教程

2 家长注册 API

bash 复制代码
/**
 * 家长角色注册
 * @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

bash 复制代码
/**
 * 老师角色注册
 * @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调用中,让开发者能更专注于前端页面的搭建和业务功能的实现。希望这篇博客能为你提供有价值的参考!

相关推荐
一枚前端小姐姐2 天前
低代码平台表单设计系统技术分析(实战三)
前端·vue.js·低代码
一枚前端小姐姐2 天前
低代码平台表单设计系统技术分析(实战二)
低代码·架构·前端框架
一枚前端小姐姐2 天前
低代码平台表单设计系统架构分析(实战一)
前端·低代码·架构
麦聪聊数据3 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
吴声子夜歌3 天前
小程序——布局示例
小程序
luffy54593 天前
微信小程序页面使用类似filter函数的wxs语法
微信小程序·小程序
Slow菜鸟3 天前
微信小程序开发(二)目录结构完全指南
微信小程序·小程序
小小王app小程序开发3 天前
海外盲盒小程序抽赏玩法分析(附跨境技术落地要点)
小程序
一叶星殇3 天前
微信小程序请求拦截器踩坑:避免重复刷新 token
微信小程序·小程序