已收录

经验:行之有效的用户自注册和自解绑的流程和代码

办公助手
办公助手 Lv.1 新人创作者

Lv.1 新人创作者

多维表格用得多了,就会发现一个现实存在的问题,就是如果用户很多,把这些人加到高级权限中简直就是一个噩梦,如果某个人退出了,把他删了更是麻烦,而且我还不喜欢加陌生人为自己的联系人,所以识别协作者全靠看头像和名称。我一直在想,能不能做一个全自动无人值守的用户自己注册和解绑的流程。在前几个月,我确实摸索出来了,也很好用,本来想自己藏着,后来又想,多维表格用得人多了,才能进步更快,生态才能更加完整。建议在应用模式下使用本套流程:

  1. 高级权限中设置一个默认角色并设置打开链接进入自动进入默认角色;

  1. 默认角色只有一个页面的访问权限,就是一个查询视图,依靠管理员给的密码(也可以设置手机号短信验证进入),进入后,点击按钮,就会自动把触发者绑定成为指定的角色,也就是在高级权限中的指定角色中加入这个触发者,这就完成了用户的自绑定;

  1. 如果想解绑或者更换别人绑定,可以输入密码后再次点击按钮,就可以完成解绑了,也就是在高级权限中的指定角色中删除掉这个协作者;

  1. 由于担心接口的不稳定性,我考虑了很多情况,当然非常期待官方可以在高级权限中增加功能,就是固定数据表的一个联系人字段为该角色的协作者,并且随字段中联系人的变化而变化。也希望大家一起分享自己的收获,共同交流,共同成长。

// ---------- 工具函数 ----------
// Id 归一化:统一转字符串去空格,避免 number/string 严格比较恒不等
function toId(v) {
  return String(v ?? "").trim()
}

// 安全延时:环境无内置 sleep 时静默跳过
function safeSleep(ms) {
  try {
    sleep(ms)
  } catch (e) { }
}

// 查找用户在哪个角色中,返回角色名(空字符串 = 不存在)
function findUserRoles(user_id) {
  let hit = ""
  const roles = Application.Permission2.Roles
  for (let i = 1; i <= roles.Count; i++) {
    let signal = 0
    const role = roles.Item(i)
    try {
      const users = role.Users
      for (let j = 1; j <= users.Count; j++) {
        if (toId(users.Item(j).Id) === toId(user_id)) {
          hit = role.Name
          signal = 1
          break
        }
      }
    } catch (e) {
      console.log(role.Name + '读取失败;' + e.message)
    }
    if (signal) {
      break
    }
  }
  return hit
}

// 从所有角色中删除用户
function removeFromAllRoles(user_id) {
  const user = Application.User(user_id)
  const roles = Application.Permission2.Roles
  const ok = [], fail = []
  for (let i = 1; i <= roles.Count; i++) {
    const role = roles.Item(i)
    try {
      const result = role.DeleteUser(user)
      if (result && result.Code === 0) {
        ok.push(role.Name)
      } else {
        fail.push({ "角色": role.Name, "失败原因": result ? result.Message : "接口无返回" })
      }
    } catch (e) {
      fail.push({ "角色": role.Name, "失败原因": e.message })
    }
  }
  return { ok, fail }
}

// 添加协作者
function addUserToRole(user_id, roleName) {
  const role = Application.Permission2.Roles(roleName)
  if (!role) {
    console.log(roleName + "不存在")
    return { "ok_or_fail": false, "output": roleName + "不存在" }
  }
  const user = Application.User(user_id)
  try {
    // 单次 1 个,不触及 20 上限
    const r = role.AddUser([user])
    console.log(r)
    return { "ok_or_fail": true}
  } catch (e) {
    console.log(e.message)
    return { "ok_or_fail": false, "output": e.message }
  }
}

// ---------- 主流程 ----------
const rid = Context.argv.rid
const sid = Context.argv.sid[0]
const user_id_chufa = Context.argv.user[0].id   // 触发者的 userid
const sheet = Application.Sheets.ItemById(sid)
const user_id = sheet.RecordRange(rid, "@账号ID").Value
const role = sheet.RecordRange(rid, "@角色").Value
if (toId(user_id)) {
  // 校验:只有本人能解绑自己
  if (toId(user_id_chufa) !== toId(user_id)) {
    console.log("准备解绑的账号和触发者不同")
    console.log("触发者ID:", user_id_chufa, "删除目标ID:", user_id)
    return { "state": "准备解绑的账号和触发者不同" }
  }
  // 执行删除并收集结果
  const rm = removeFromAllRoles(user_id)
  if (rm.ok.length) console.log("已从角色移除:" + JSON.stringify(rm.ok))
  if (rm.fail.length) console.log("移除失败信息:" + JSON.stringify(rm.fail))
  // 延时复查验证
  let remain = findUserRoles(user_id)
  if (remain) {
    safeSleep(1000)
    remain = findUserRoles(user_id)
  }
  if (!remain) {
    // 清字段加容错:避免"权限已删、字段还在"的不一致
    try {
      sheet.RecordRange(rid, "@账号").Value = null
      sheet.RecordRange(rid, "@账号ID").Value = null
    } catch (e) {
      console.log(e.message)
      console.log("【解绑成功】:权限已移除,但清空字段失败")
      return { "state": "【解绑成功】:权限已移除,但清空字段失败" }
    }
    return { "state": "【解绑成功】" }
  }
  // 失败必须显式返回,下游节点和点击者才能感知
  console.log("【解绑失败】:用户仍在角色" + remain + "中")
  return { "state": "【解绑失败】:用户仍在角色" + remain + "中" }
} else {
  let remain = findUserRoles(user_id_chufa)
  if (remain) {
    console.log("【绑定失败】:用户已经存在于" + remain + "角色中,请不要重复绑定")
    return { "state": "【绑定失败】:用户已经存在于" + remain + "角色中,请不要重复绑定" }
  }
  if (!String(role ?? "").trim()) {
    console.log("【绑定失败】:未提供需要绑定的角色")
    return { "state": "【绑定失败】:未提供需要绑定的角色" }
  }
  const add = addUserToRole(user_id_chufa, String(role).trim())
  remain = findUserRoles(user_id_chufa)
  if (!remain) {
    safeSleep(1000)
    remain = findUserRoles(user_id_chufa)
  }
  if (remain) {
    try {
      sheet.RecordRange(rid, "@账号").Value = user_id_chufa
      sheet.RecordRange(rid, "@账号ID").Value = user_id_chufa
    } catch (e) {
      console.log(e.message)
      return { "state": "【绑定成功】:已绑定,但写入字段失败"}
    }
    return { "state": "【绑定成功】" }
  }
  if (!add.ok_or_fail) {
    console.log("【绑定失败】:" + add.output)
    return { "state": "【绑定失败】:" + add.output }
  }
  console.log("【绑定失败】:但是原因不明")
  return { "state": "【绑定失败】:但是原因不明" }
}

江苏省
浏览 187
1
7
分享
7 +1
8
1 +1
全部评论 8
 
宝奎
期待官方采纳此条建议
   河北省
举报
0
0
 
鱼竿要生锈
虽然一堆代码没看明白,但你这个建议是个好建议。
   四川省
举报
0
1
测试账号
其实这个还是很需要的
· 江苏省
举报
0
0
 
懿喵
不错,为你点赞
   福建省
举报
0
0
 
孔杰威
最近我也摸索出来了,核心就是让操作者首次点击链接进入一个默认数据表,完成“加入协作”这一步,自动分配到默认权限角色;再让他自己点击按钮,通过按钮触发脚本,把“触发人“分配到对应的权限角色。可玩性是比较高,但目前担心多人同时点击按钮触发脚本时的挤兑问题。
   广东省
举报
0
2
孔杰威
而且10月更新了自动化流程中的”全局提示“功能,点击按钮后角色分配的成功/失败、具体角色等信息可以通过全局提示提醒(脚本输出值关联到全局提示)。整体交互体验很好。
· 广东省
举报
0
0
 
测试员
我自己的测试号来顶一下,返回的state主要是提供按钮的“全局提示”使用的,管理员如果也想点击按钮删除,需要把“校验:只有本人能解绑自己”这个去掉
   江苏省
举报
0
0