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

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

在这里插入图片描述

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

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

  • 内部用户:通常指应用的管理者、运营人员等。他们拥有后台操作权限,可以手动在后台添加账号、设置密码。这类账号数量有限(例如,平台可能只提供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

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

Logo

低代码爱好者的网上家园

更多推荐