This commit is contained in:
2023-11-07 17:15:33 +08:00
commit fe37e2e1df
418 changed files with 53012 additions and 0 deletions
@@ -0,0 +1,25 @@
module.exports = {
/**
* 用户接受邀请
* @url user/kh/acceptInvite 前端调用的url参数地址
* @description 此接口用于在注册之后再填写邀请码的场景,多数情况下不会用到此接口而是在注册时填写邀请码
* data 请求参数 说明
* @param {String} inviteCode 邀请人的邀请码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID } = util;
let { uid, inviteCode = "" } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
res = await uniID.acceptInvite({
uid:uid,
inviteCode:inviteCode
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,75 @@
module.exports = {
/**
* 添加文件上传记录
* @url user/kh/addUploadRecord 前端调用的url参数地址
* data 请求参数 说明
* @param {String} url 文件外网访问url
* @param {String} name 文件名
* @param {Number} size 文件大小
* @param {String} file_id 文件id
* @param {String} provider 供应商
* @param {String} category_id 分类ID
* @param {Number} width 图片\视频的宽度
* @param {Number} height 图片\视频的高度
* @param {Number} orientation 图片\视频的旋转方向
* @param {Number} duration 视频的总时长
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, filterResponse, originalParam } = event;
let { customUtil, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: "" };
// 业务逻辑开始-----------------------------------------------------------
let {
url,
name: original_name,
size,
provider,
file_id,
category_id,
width,
height,
orientation,
duration
} = data;
let type = "other";
if (vk.pubfn.test(url, "image")) {
type = "image";
} else if (vk.pubfn.test(url, "video")) {
type = "video";
} else if (vk.pubfn.test(url, "audio")) {
type = "video"; // 这里audio也分类为video
}
if (vk.pubfn.isNull(original_name)) original_name = url;
let dataJson = {
user_id: uid,
sort: 0,
status: 0,
type,
url,
display_name: original_name,
original_name,
size,
file_id,
provider,
width,
height,
orientation,
duration
};
if (vk.pubfn.isNotNull(category_id)) {
dataJson["category_id"] = category_id;
}
await vk.baseDao.add({
dbName: "vk-files",
dataJson
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 绑定支付宝
* @url user/kh/bindAlipay 前端调用的url参数地址
* @description 将当前登录用户绑定支付宝
* data 请求参数 说明
* @param {String} code 支付宝登录返回的code
* @param {String} platform 客户端类型:mp-weixin、app-plus,默认uni-id会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 绑定
res = await uniID.bindAlipay(event.data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,23 @@
module.exports = {
/**
* 绑定邮箱
* @url user/kh/bindEmail 前端调用的url参数地址
* @description 将当前登录用户绑定邮箱
* data 请求参数 说明
* @param {String} email 用户邮箱
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
if (!data.code) return { code: -1, msg: "验证码不能为空!" }
// 绑定邮箱
res = await uniID.bindEmail(event.data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,23 @@
module.exports = {
/**
* 绑定手机号
* @url user/kh/bindMobile 前端调用的url参数地址
* @description 将当前登录用户绑定手机号
* data 请求参数 说明
* @param {String} mobile 用户手机号
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
if (!data.code) return { code: -1, msg: "验证码不能为空!" }
// 绑定手机号
res = await uniID.bindMobile(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,72 @@
module.exports = {
/**
* 换绑新的邮箱
* @url user/kh/bindNewEmail 前端调用的url参数地址
* @description 将当前登录用户换绑新的邮箱
* data 请求参数 说明
* @param {String} oldEmail 旧邮箱码
* @param {String} oldEmailCode 旧邮箱收到的验证码
* @param {String} email 新邮箱码
* @param {String} code 新邮箱收到的验证码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
oldEmail, // 旧邮箱码
oldEmailCode, // 旧邮箱收到的验证码
email, // 新邮箱码
code, // 新邮箱收到的验证码
} = data;
if (!code || !oldEmailCode) return { code: -1, msg: "验证码不能为空!" };
if (!email || !oldEmail) return { code: -1, msg: "邮箱不能为空!" };
// 先检查下旧邮箱码和验证码是否正确
let verifyCodeRes;
verifyCodeRes = await uniID.verifyCode({
email: oldEmail,
code: oldEmailCode,
type: 'unbind'
});
if (verifyCodeRes.code !== 0) {
verifyCodeRes.msg = `旧邮箱${verifyCodeRes.msg}`
return verifyCodeRes;
}
// 先检查下新邮箱码和验证码是否正确
verifyCodeRes = await uniID.verifyCode({
email,
code: code,
type: 'bind'
});
if (verifyCodeRes.code !== 0) {
verifyCodeRes.msg = `新邮箱${verifyCodeRes.msg}`
return verifyCodeRes;
}
// 解绑邮箱
let unbindEmailRes = await uniID.unbindEmail({
uid,
email: oldEmail
});
if (unbindEmailRes.code !== 0) {
return unbindEmailRes;
}
await uniID.setVerifyCode({ code, email, type: "bind" });
// 绑定新邮箱
res = await uniID.bindEmail({
uid,
email,
code
});
if (res.code !== 0) {
return res;
}
res.msg = "换绑邮箱成功!";
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,70 @@
module.exports = {
/**
* 换绑新的手机号
* @url user/kh/bindNewMobile 前端调用的url参数地址
* @description 将当前登录用户换绑新的手机号
* data 请求参数 说明
* @param {String} oldMobile 旧手机号码
* @param {String} oldMobileCode 旧手机收到的验证码
* @param {String} mobile 新手机号码
* @param {String} code 新手机收到的验证码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
oldMobile, // 旧手机号码
oldMobileCode, // 旧手机收到的验证码
mobile, // 新手机号码
code, // 新手机收到的验证码
} = data;
if (!code || !oldMobileCode) return { code: -1, msg: "验证码不能为空!" };
if (!mobile || !oldMobile) return { code: -1, msg: "手机号不能为空!" };
// 先检查下旧手机号码和验证码是否正确
let verifyCodeRes;
verifyCodeRes = await uniID.verifyCode({
mobile: oldMobile,
code: oldMobileCode,
type: 'unbind'
});
if (verifyCodeRes.code !== 0) {
verifyCodeRes.msg = `旧手机${verifyCodeRes.msg}`
return verifyCodeRes;
}
// 先检查下新手机号码和验证码是否正确
verifyCodeRes = await uniID.verifyCode({
mobile,
code: code,
type: 'bind'
});
if (verifyCodeRes.code !== 0) {
verifyCodeRes.msg = `新手机${verifyCodeRes.msg}`
return verifyCodeRes;
}
// 解绑手机号
let unbindMobileRes = await uniID.unbindMobile({
uid,
mobile: oldMobile
});
if (unbindMobileRes.code !== 0) {
return unbindMobileRes;
}
// 绑定新手机号
res = await uniID.bindMobile({
uid,
mobile
});
if (res.code !== 0) {
return res;
}
res.msg = "换绑手机号成功!";
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,23 @@
module.exports = {
/**
* 绑定QQ
* @url user/kh/bindQQ 前端调用的url参数地址
* data 请求参数 说明
* @param {String} code QQ登录返回的code
* @param {String} platform 客户端类型:mp-weixin、app-plus,默认uni-id会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 绑定
res = await uniID.bindQQ(event.data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 绑定微信
* @url user/kh/bindWeixin 前端调用的url参数地址
* @description 将当前登录用户绑定微信
* data 请求参数 说明
* @param {String} code 微信登录返回的code
* @param {String} platform 客户端类型:mp-weixin、app-plus,默认uni-id会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 绑定
res = await uniID.bindWeixin(event.data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,25 @@
'use strict';
module.exports = {
/**
* 获取支付宝小程序码
* @url user/kh/getAlipayMiniCode 前端调用的url参数地址
* data 请求参数
* @param {String} page 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
* @param {String} scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
*/
main: async (event) => {
let { data = {}, userInfo, util, filterResponse, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: "" };
// 业务逻辑开始-----------------------------------------------------------
let {
page,
scene
} = data;
res = await vk.openapi.alipay.acode.getMiniCode(data);
res.base64 = res.qr_code_url_circle_blue; // 兼容其他小程序的返回字段
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,34 @@
module.exports = {
/**
* 获取接受邀请的用户清单
* @url user/kh/getInvitedUser 前端调用的url参数地址
* data 请求参数 说明
* @param {Number} pageIndex 当前页码
* @param {Number} pageSize 每页显示数量
* @param {Array<Object>} sortRule 排序规则
* @param {object} formData 查询条件数据源
* @param {Array<Object>} columns 查询条件规则
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, filterResponse, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk , db, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
// 可写与数据库的交互逻辑等等
let dbName = "uni-id-users";
let index = 0;
res = await vk.baseDao.getTableData({
dbName,
data,
fieldJson:{ token:false, password:false },
whereJson:{
['inviter_uid.' + index]:uid
}
});
return res;
}
}
@@ -0,0 +1,30 @@
'use strict';
module.exports = {
/**
* 获取我拥有的菜单列表
* @url user/kh/getMenu 前端调用的url参数地址
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, filterResponse, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk , db, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
let {
role = [],
} = userInfo;
// 根据角色获取菜单
res = await vk.system.sysDao.listMenuByRole({
role,
treeProps:{
level: 3
}
});
res.userInfo = userInfo;
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,16 @@
module.exports = {
/**
* 获取我的用户信息
* @url user/kh/getMyUserInfo 前端调用的url参数地址
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
res.userInfo = userInfo;
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,45 @@
'use strict';
module.exports = {
/**
* 获取qq小程序码
* @url user/kh/getQQMiniCode 前端调用的url参数地址
* data 请求参数
* @param {String} path 小程序页面路径
*/
main: async (event) => {
let { data = {}, userInfo, util, filterResponse, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: "" };
// 业务逻辑开始-----------------------------------------------------------
let {
path
} = data;
res = await vk.openapi.qq.acode.getMiniCode(data);
if (typeof res === "object" && res.code) {
return res;
}
try {
// 二进制转base64
let base64 = Buffer.from(res, 'binary').toString('base64');
return {
code: 0,
base64: `data:image/png;base64,${base64}`
};
} catch (err) {
// 转base64失败
return {
code: -1,
msg: "生成小程序码失败",
err: {
message: err.message,
stack: err.stack
}
};
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,57 @@
'use strict';
module.exports = {
/**
* 获取小程序码
* @url user/kh/getWeixinMPqrcode 前端调用的url参数地址
* data 请求参数 说明
* @param {String} scene 自定义参数最大32个可见字符 只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~
* @param {String} page 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
* @param {boolean} check_path 默认是true,检查page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错);为 false 时允许小程序未发布或者 page 不存在, 但page 有数量上限(60000个)请勿滥用。
* @param {String} env_version 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。
* @param {number} width 二维码的宽度,单位 px,最小 280px,最大 1280px
* @param {boolean} auto_color 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false
* @param {Object} line_color auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
* @param {boolean} is_hyaline 是否需要透明底色,为 true 时,生成透明底色的小程序
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let { my_invite_code } = userInfo;
if (vk.pubfn.isNull(data.scene) && my_invite_code) {
// 若scene为空,则自动带上用户邀请码
data.scene = `code=${my_invite_code}`;
}
if (!userInfo.role || userInfo.role.indexOf("admin") === -1) {
data.check_path = true; // 防止超限, 为 false 时允许小程序未发布或者 page 不存在, 但page 有数量上限(60000个)请勿滥用。
}
res = await vk.openapi.weixin.wxacode.getUnlimited(data);
if (typeof res === "object" && res.code) {
return res;
}
try {
// 二进制转base64
let base64 = Buffer.from(res, 'binary').toString('base64')
return {
code: 0,
base64: `data:image/png;base64,${base64}`
};
} catch (err) {
// 转base64失败
return {
code: -1,
msg: "生成小程序码失败",
err: {
message: err.message,
stack: err.stack
}
};
}
// 业务逻辑结束-----------------------------------------------------------
}
}
@@ -0,0 +1,41 @@
'use strict';
module.exports = {
/**
* 获取小程序scheme码,适用于短信、邮件、外部网页等拉起小程序的业务场景。通过该接口,可以选择生成到期失效和永久有效的小程序码,目前仅针对国内非个人主体的小程序开放
* @url user/kh/getWeixinMPscheme 前端调用的url参数地址
* data 请求参数 说明
* @param {String} path 小程序页面路径
* @param {String} query 小程序页面参数
* @param {String} env_version 默认值"release"。要打开的小程序版本。正式版为 "release",体验版为"trial",开发版为"develop",仅在微信外打开时生效
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
path,
query,
env_version
} = data;
// 超过180天有10万个限制,故这里强制设为90天
res = await vk.openapi.weixin.urlscheme.generate({
...data,
// 跳转到的目标小程序信息。
jump_wxa: {
path, // 小程序页面路径
query, // 小程序页面参数
env_version
},
is_expire: true,
expire_type: 1,
expire_interval: 90, // 90天后失效
});
return res;
// 业务逻辑结束-----------------------------------------------------------
}
}
@@ -0,0 +1,38 @@
'use strict';
module.exports = {
/**
* 获取小程序 URL Link,适用于短信、邮件、网页、微信内等拉起小程序的业务场景。通过该接口,可以选择生成到期失效和永久有效的小程序链接,有数量限制,目前仅针对国内非个人主体的小程序开放
* @url user/kh/getWeixinMPurl 前端调用的url参数地址
* data 请求参数 说明
* @param {String} path 小程序页面路径
* @param {String} query 小程序页面参数
* @param {String} env_version 默认值"release"。要打开的小程序版本。正式版为 "release",体验版为"trial",开发版为"develop",仅在微信外打开时生效
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
path,
query,
env_version
} = data;
// 超过180天有10万个限制,故这里强制设为90天
res = await vk.openapi.weixin.urllink.generate({
...data,
path,
query,
env_version,
is_expire: true,
expire_type: 1,
expire_interval: 90, // 90天后失效
});
return res;
// 业务逻辑结束-----------------------------------------------------------
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 重置密码
* @url user/kh/resetPwd 前端调用的url参数地址
* @description 重置当前登录用户的密码为123456
* data 请求参数 说明
* @param {String} uid 当前登录用户id(可信任,仅kh目录下的函数有此值)
* @param {String} password 重置后的密码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { util, data={} } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
// 注意:虽然uid是从data中获取的,但无论前端uid传什么,最终uid都是当前登录用户的_id,因此不会出现A把B的密码改了的情况。
let { uid, password= '123456' } = data;
res = await uniID.resetPwd({ uid, password });
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,37 @@
module.exports = {
/**
* 设置头像
* @url user/kh/setAvatar 前端调用的url参数地址
* @description 设置当前登录用户的头像
* data 请求参数 说明
* @param {String} avatar 用户头像URL
* @param {Boolean} deleteOldFile 是否同时删除云储存内的头像文件
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk , db, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
let { avatar, deleteOldFile } = data;
// 设置头像
res = await uniID.setAvatar(event.data);
// 判断是否需要删除旧头像的云储存文件
if(res.code ===0 && deleteOldFile && userInfo.avatar){
try {
await uniCloud.deleteFile({
fileList: [
userInfo.avatar
]
});
}catch(err){
res.deleteFileErr = err;
}
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,32 @@
module.exports = {
/**
* 设置用户邀请码(自动生成)
* @url user/kh/setUserInviteCode 前端调用的url参数地址
* @description 针对未生成邀请码的用户使用此方法生成邀请码(自动生成)
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} myInviteCode 最终设置的邀请码
*/
main: async (event) => {
let { data = {},userInfo, util, originalParam } = event;
let { uniID } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
// 判断下如果已经生成,则不再重新生成
if(userInfo.my_invite_code){
res = {
"code": 0,
"msg": "无需重复设置",
"myInviteCode": userInfo.my_invite_code
}
}else{
res = await uniID.setUserInviteCode({
uid:uid
});
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 解绑支付宝
* @url user/kh/unbindAlipay 前端调用的url参数地址
* @description 当前登录用户解绑支付宝
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
if(vk.pubfn.isNullAll(userInfo.mobile, userInfo.username)){
return { code : -1, msg : '为了您的账号安全,请绑定手机号后再解绑支付宝!' };
}
// 解绑
res = await uniID.unbindAlipay(uid);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,25 @@
module.exports = {
/**
* 解绑邮箱
* @url user/kh/unbindEmail 前端调用的url参数地址
* @description 当前登录用户解绑邮箱
* data 请求参数 说明
* @param {String} email 邮箱
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util } = event;
let { uniID, vk } = util;
let res = { code : 0, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
if(vk.pubfn.isNullAll(userInfo.mobile, userInfo.username, userInfo.wx_openid, userInfo.ali_openid, userInfo.qq_openid)){
return { code : -1, msg : '为了您的账号安全,请绑定手机号后再解绑邮箱!' };
}
// 解绑
res = await uniID.unbindEmail(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,25 @@
module.exports = {
/**
* 解绑邮箱
* @url user/kh/unbindMobile 前端调用的url参数地址
* @description 当前登录用户解绑手机号
* data 请求参数 说明
* @param {String} mobile 手机号
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util } = event;
let { uniID, vk } = util;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
if (vk.pubfn.isNullAll(userInfo.username, userInfo.wx_openid, userInfo.ali_openid, userInfo.qq_openid)) {
return { code: -1, msg: '为了您的账号安全,请绑定微信或设置用户名后再解绑手机号!' };
}
// 解绑
res = await uniID.unbindMobile(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,23 @@
module.exports = {
/**
* 解绑QQ
* @url user/kh/unbindQQ 前端调用的url参数地址
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
if(vk.pubfn.isNullAll(userInfo.mobile, userInfo.username)){
return { code : -1, msg : '为了您的账号安全,请绑定手机号后再解绑QQ!' };
}
// 解绑
res = await uniID.unbindQQ(uid);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 解绑微信
* @url user/kh/unbindWeixin 前端调用的url参数地址
* @description 当前登录用户解绑微信
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
// 业务逻辑开始-----------------------------------------------------------
let { data = {}, userInfo, util } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
if(vk.pubfn.isNullAll(userInfo.mobile, userInfo.username)){
return { code : -1, msg : '为了您的账号安全,请绑定手机号后再解绑微信!' };
}
// 解绑
res = await uniID.unbindWeixin(uid);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,26 @@
module.exports = {
/**
* 修改登录密码
* @url user/kh/updatePwd 前端调用的url参数地址
* @description 修改当前登录用户的登录密码
* data 请求参数 说明
* @param {String} oldPassword 旧密码
* @param {String} newPassword 新密码
* @param {String} passwordConfirmation 确认新密码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, userInfo, util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
if (userInfo.disable_update_password) {
return { code: -1, msg: "该账号不支持修改密码。" };
}
res = await uniID.updatePwd(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,32 @@
module.exports = {
/**
* 修改用户信息
* @url user/kh/updateUser 前端调用的url参数地址
* @description 设置当前登录用户的昵称等用户信息
* data 请求参数 说明
* @param {String} nickname 用户的昵称
* @param {String} avatar 用户的头像
* @param {Number} gender 用户的性别
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
// 允许修改的字段列表
let updateKeys = ["nickname", "avatar", "gender", "social_info"];
let dataJson = {};
for (let i in updateKeys) {
let key = updateKeys[i];
if (vk.pubfn.isNotNull(data[key])) dataJson[key] = data[key];
}
dataJson.uid = uid;
res = await uniID.updateUser(dataJson);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,27 @@
module.exports = {
/**
* 验证token是否有效
* @url user/pub/checkToken 前端调用的url参数地址
* @description 验证token是否有效(会返回uid)
* @param {String} uniIdToken 用户token
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} uid 当前token对应的用户uid
* @param {Object} userInfo 当前用户信息
* @param {Array} role 当前用户角色
* @param {Array} permission 当前用户权限
*/
main: async (event) => {
let { util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
res = await uniID.checkToken(event.uniIdToken,{
needPermission:true,
needUserInfo:true
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,152 @@
const platform_Android = 'Android';
const platform_iOS = 'iOS';
const package_app = 'native_app';
const package_wgt = 'wgt';
const app_version_db_name = 'opendb-app-versions'
module.exports = {
/**
* App升级中心 - 检测是当前版本是否需要升级
* @url user/pub/checkVersion 前端调用的url参数地址
* @description 验证token是否有效(会返回uid)
* @param {String} uniIdToken 用户token
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} uid 当前token对应的用户uid
* @param {Object} userInfo 当前用户信息
* @param {Array} role 当前用户角色
* @param {Array} permission 当前用户权限
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { customUtil, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: "" };
let { context } = originalParam;
// 业务逻辑开始-----------------------------------------------------------
/**
* 检测升级 使用说明
* 上传包:
* 1. 根据传参,先检测传参是否完整,appid appVersion wgtVersion 必传
* 2. 先从数据库取出所有该平台(从上下文读取平台信息,默认 Andriod)的所有线上发行更新
* 3. 再从所有线上发行更新中取出版本最大的一版。如果可以,尽量先检测wgt的线上发行版更新
* 4. 使用上步取出的版本包的版本号 和传参 appVersion、wgtVersion 来检测是否有更新,必须同时大于这两项,否则返回暂无更新
* 5. 如果库中 wgt包 版本大于传参 appVersion,但是不满足 min_uni_version < appVersion,则不会使用wgt更新,会接着判断库中 app包version 是否大于 appVersion
*/
let {
appid,
appVersion,
wgtVersion,
} = data;
let platform = context.OS === 'ios' ? platform_iOS : platform_Android;
// 参数非空检测
let nullKey = vk.pubfn.isNullOneByObject({ appid, appVersion, wgtVersion, platform });
if (nullKey) return { code: -102, msg: '参数 ' + nullKey + ' 不能为空' };
let versionList = await vk.baseDao.select({
dbName: app_version_db_name,
getCount: false,
pageIndex: 1,
pageSize: 500,
getMain: true,
whereJson: {
appid,
platform,
stable_publish: true
},
sortArr: [{ name: "create_date", type: "desc" }],
});
if (vk.pubfn.isNull(versionList)) return { code: -101, msg: "暂无更新或检查appid是否填写正确" };
const appVersionInDb = versionList.find(item => item.type === package_app) || {};
const wgtVersionInDb = versionList.find(item => item.type === package_wgt) || {};
const hasAppPackage = !!Object.keys(appVersionInDb).length;
const hasWgtPackage = !!Object.keys(wgtVersionInDb).length;
// 取两个版本中版本号最大的包,版本一样,使用wgt包
let stablePublishDb = hasAppPackage ?
hasWgtPackage ?
compare(wgtVersionInDb.version, appVersionInDb.version) >= 0 ?
wgtVersionInDb :
appVersionInDb :
appVersionInDb :
wgtVersionInDb;
const {
version,
min_uni_version
} = stablePublishDb;
// 库中的version必须满足同时大于appVersion和wgtVersion才行,因为上次更新可能是wgt更新
const appUpdate = compare(version, appVersion) === 1; // app包可用更新
const wgtUpdate = compare(version, wgtVersion) === 1; // wgt包可用更新
if (Object.keys(stablePublishDb).length && appUpdate && wgtUpdate) {
// 判断是否可用wgt更新
if (min_uni_version && compare(min_uni_version, appVersion) < 1) {
return {
code: 101,
msg: 'wgt更新',
...stablePublishDb
};
} else if (hasAppPackage && compare(appVersionInDb.version, appVersion) === 1) {
return {
code: 102,
msg: '整包更新',
...appVersionInDb
};
}
} else {
return { code: 0, msg: '当前版本已经是最新的,不需要更新' };
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
/**
* 对比版本号,如需要,请自行修改判断规则
* 支持比对 ("3.0.0.0.0.1.0.1", "3.0.0.0.0.1") ("3.0.0.1", "3.0") ("3.1.1", "3.1.1.1") 之类的
* @param {Object} v1
* @param {Object} v2
* v1 > v2 return 1
* v1 < v2 return -1
* v1 == v2 return 0
*/
function compare(v1 = '0', v2 = '0') {
v1 = String(v1).split('.')
v2 = String(v2).split('.')
const minVersionLens = Math.min(v1.length, v2.length);
let result = 0;
for (let i = 0; i < minVersionLens; i++) {
const curV1 = Number(v1[i])
const curV2 = Number(v2[i])
if (curV1 > curV2) {
result = 1
break;
} else if (curV1 < curV2) {
result = -1
break;
}
}
if (result === 0 && (v1.length !== v2.length)) {
const v1BiggerThenv2 = v1.length > v2.length;
const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
for (let i = minVersionLens; i < maxLensVersion.length; i++) {
const curVersion = Number(maxLensVersion[i])
if (curVersion > 0) {
v1BiggerThenv2 ? result = 1 : result = -1
break;
}
}
}
return result;
}
@@ -0,0 +1,37 @@
module.exports = {
/**
* 获取支付宝openid
* @url user/pub/code2SessionAlipay 前端调用的url参数地址
* data 请求参数 说明
* @param {String} code 支付宝登录返回的code
* @param {String} platform 客户端类型:mp-weixin、app-plus,默认uni-id会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} openid 用户openid
* @param {String} accessToken 客户端为APP时返回
* @param {String} expiresIn 客户端为APP时返回,accessToken 接口调用凭证超时时间,单位(秒)
* @param {String} refreshToken 客户端为APP时返回,用于刷新accessToken
* @param {String} reExpiresIn refreshToken超时时间,单位(秒)
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
// 用户登录(账号+密码)
res = await uniID.code2SessionAlipay(data);
if (res.code === 0) {
let encrypted = vk.crypto.aes.encrypt({
data: res
});
delete res.sessionKey; // 删除明文sessionKey
delete res.accessToken; // 删除明文accessToken
delete res.refreshToken; // 删除明文refreshToken
res.encryptedKey = encrypted; // 返回前端加密内容
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,41 @@
module.exports = {
/**
* 获取微信openid
* @url user/pub/code2SessionWeixin 前端调用的url参数地址
* @description 支持APP、微信小程序、微信公众号
* data 请求参数 说明
* @param {String} code 微信登录返回的code
* @param {String} platform 客户端类型:mp-weixin、app-plus、h5-weixin,默认会自动取客户端类型,但是在云函数url化等场景无法取到客户端类型,可以使用此参数指定
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} openid 用户openid
* @param {String} unionid 用户unionid,可以取到此值时返回
* @param {String} sessionKey 客户端为微信小程序时返回
* @param {String} accessToken 客户端为APP时返回
* @param {String} expiresIn 客户端为APP时返回,accessToken 接口调用凭证超时时间,单位(秒)
* @param {String} refreshToken 客户端为APP时返回,用于刷新accessToken
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, vk } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
code,
platform,
appid,
needCache
} = data;
if (!platform) platform = originalParam.context.PLATFORM;
res = await vk.openapi.weixin.auth.code2Session({
context: originalParam.context,
appid,
code,
needKey: false, // 是否需要返回明文的sessionKey或accessToken(为了安全期间,建议设置false)
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,23 @@
'use strict';
module.exports = {
/**
* 获取用户微信绑定的手机号
* 微信小程序API更新了,现在只需传code可以代替encryptedData和iv
* @url user/pub/getPhoneNumber 前端调用的url参数地址
* data 请求参数
* @param {String} code 通过 getphonenumber 事件获取到的code(code和encryptedData+iv二选一)
* @param {String} encryptedData(code和encryptedData+iv二选一)
* @param {String} iv(code和encryptedData+iv二选一)
* @param {String} encryptedKey code2SessionWeixin 接口返回的encryptedKey
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { customUtil, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
res = await vk.openapi.weixin.decrypt.getPhoneNumber(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,36 @@
'use strict';
module.exports = {
/**
* 根据邀请码获取用户社交信息
* @url user/pub/getUserInfoByInviteCode 前端调用的url参数地址
* data 请求参数
* @param {String} code
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { customUtil, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code : 0, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
let { code } = data;
// 参数非空检测
let nullKey = vk.pubfn.isNullOneByObject({ code });
if(nullKey) return { code:-1, msg:'参数 '+nullKey+' 不能为空' };
res.userInfo = await vk.baseDao.findByWhereJson({
dbName:"uni-id-users",
fieldJson:{
_id:true,
nickname:true,
gender:true,
avatar:true,
my_invite_code:true,
social_info:true
},
whereJson:{
my_invite_code : code
}
});
return res;
// 业务逻辑结束-----------------------------------------------------------
}
}
@@ -0,0 +1,55 @@
module.exports = {
/**
* 用户登录(账号+密码)
* @url user/pub/login 前端调用的url参数地址
* @description 用户登录(账号+密码)
* data 请求参数 说明
* @param {String} username 用户名
* @param {String} password 密码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
// 用户登录(账号+密码)
if (typeof data.username === "number") data.username = String(data.username).trim();
if (typeof data.password === "number") data.password = String(data.password).trim();
let {
needPermission,
username,
password,
queryField = ['username', 'email', 'mobile'], // 不指定queryField的情况下只会查询username
} = data;
res = await uniID.login({
username,
password,
needPermission,
queryField
});
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "password",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,55 @@
module.exports = {
/**
* 用户登录(支付宝授权)
* @url user/pub/loginByAlipay 前端调用的url参数地址
* @description 用户登录(支付宝授权)
* data 请求参数 说明
* @param {String} code 支付宝登录返回的code
* @param {String} type login:登录 register:注册
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
code,
type,
myInviteCode,
needPermission
} = data;
res = await uniID.loginByAlipay({
code,
type,
myInviteCode,
needPermission
});
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
let encrypted = vk.crypto.aes.encrypt({
data: res
});
delete res.sessionKey; // 删除明文sessionKey
delete res.accessToken; // 删除明文accessToken
res.encryptedKey = encrypted; // 返回前端加密内容
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "alipay",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,56 @@
module.exports = {
/**
* 用户登录(邮箱+验证码登录)
* @url user/pub/loginByEmail 前端调用的url参数地址
* data 请求参数 说明
* @param {String} email 邮箱
* @param {String} code 邮箱收到的验证码
* @param {String} type 指定操作类型,不传:存在则登录不存在则注册 login:只能登录 register:只能注册
* @param {String} password 密码,当前用户为新注册时生效
* @param {String} myInviteCode 设置当前注册用户自己的邀请码,当前用户为新注册时生效(不传会自动生成)
* @param {Boolean} needPermission 设置为true时会在checkToken时返回用户权限(permission),如果是在admin端,需传true
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
email,
code,
type,
password,
needPermission,
myInviteCode
} = data;
res = await uniID.loginByEmail({
email,
code,
type,
password,
myInviteCode,
needPermission
});
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "email",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,58 @@
module.exports = {
/**
* QQ授权登录
* @url user/pub/loginByQQ 前端调用的url参数地址
* data 请求参数 说明
* @param {String} code QQ小程序登录返回的code
* @param {String} accessToken QQ APP登录返回的access_token
* @param {String} type login:登录 register:注册
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
code,
accessToken,
type,
myInviteCode,
needPermission
} = data;
res = await uniID.loginByQQ({
code,
accessToken,
type,
myInviteCode,
needPermission
});
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
let encrypted = vk.crypto.aes.encrypt({
data: res
});
delete res.sessionKey; // 删除明文sessionKey
delete res.accessToken; // 删除明文accessToken
delete res.refreshToken; // 删除明文refreshToken
res.encryptedKey = encrypted; // 返回前端加密内容
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "qq",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,72 @@
module.exports = {
/**
* 手机号登陆(手机号+手机验证码)
* @url user/pub/loginBySms 前端调用的url参数地址
* @description 手机号登陆(手机号+手机验证码)
* data 请求参数 说明
* @param {String} mobile 手机号
* @param {String} code 验证码
* @param {String} type 指定操作类型,不传:存在则登录不存在则注册 login:只能登录 register:只能注册
* @param {String} password 密码,当前用户为新注册时生效
* @param {String} inviteCode 邀请人的邀请码,当前用户为新注册时生效
* @param {String} myInviteCode 设置当前注册用户自己的邀请码,当前用户为新注册时生效(不传会自动生成)
* @param {Boolean} needPermission 设置为true时会在checkToken时返回用户权限(permission),如果是在admin端,需传true
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
mobile,
code,
type,
password,
inviteCode,
myInviteCode,
needPermission
} = data;
res = await uniID.loginBySms({
mobile,
code,
type,
password,
inviteCode,
myInviteCode,
needPermission
});
// 修改用户昵称为:手机尾号xxxx用户
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
let mobileStr = mobile.substring(7);
await vk.baseDao.update({
dbName: "uni-id-users",
whereJson: {
_id: res.uid,
nickname: null
},
dataJson: {
nickname: `手机尾号${mobileStr}用户`
}
});
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "sms",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,74 @@
module.exports = {
/**
* APP端 手机一键登录
* @url user/pub/loginByUniverify 前端调用的url参数地址
* @param {String} access_token uni.login登录成功后,返回的access_token参数
* @param {String} openid uni.login登录成功后,返回的openid参数
* @param {String} type 指定操作类型,可选值为login、register,不传此参数时表现为手机号已注册则登录,手机号未注册则进行注册
* @param {String} password 密码,type为register时生效
* @param {String} inviteCode 邀请人的邀请码,type为register时生效
* @param {String} myInviteCode 设置当前注册用户自己的邀请码,type为register时生效
* @param {Boolean} needPermission 设置为true时会在checkToken时返回用户权限(permission),建议在管理控制台中使用
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} uid 当前token对应的用户uid
* @param {String} type 操作类型,login为登录、register为注册
* @param {String} mobile 登录者手机号
* @param {String} userInfo 用户全部信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { customUtil, uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
access_token,
openid,
type,
password,
inviteCode,
myInviteCode,
needPermission
} = data;
res = await uniID.loginByUniverify({
access_token,
openid,
type,
password,
inviteCode,
myInviteCode,
needPermission
});
if (res.token) {
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
if (res.type === "register") {
let mobileStr = res.userInfo.mobile.substring(7);
res.userInfo = await vk.baseDao.updateAndReturn({
dbName: "uni-id-users",
whereJson: {
_id: res.uid
},
dataJson: {
nickname: data.nickname || `手机尾号${mobileStr}用户`,
},
});
}
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "univerify",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,58 @@
module.exports = {
/**
* 用户登录(微信授权)
* @url user/pub/loginByWeixin 前端调用的url参数地址
* @description 支持APP、微信小程序、微信公众号
* data 请求参数 说明
* @param {String} code 微信登录返回的code
* @param {String} type login:登录 register:注册
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
* @param {Object} userInfo 用户信息
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
// 额外写入的自定义字段数据(可以自己增加,建议只传一些显示的信息,不要传余额字段等,因为前端不可信任)
// 注意:app的微信登录已经支持自动获取昵称和头像,前端无需再传昵称和头像。
let custom = {
nickname: data.nickname,
avatar: data.avatar
};
/**
* 支持多小程序登录
* 此处data可以额外接收appid和appsecret参数(appid可以从前端传,而appsecret可以配置在全局配置中(common/uni-config-center/vk-unicloud/index.js),也可以自己从数据库获取)
* 如果不传appid,则默认使用uni-id的配置信息
* 特别注意:如果使用多小程序登录,则同一用户(同一个微信号)在不同小程序登录时,会分别创建不同的用户(除非小程序绑定在同一个开放平台下)。
*/
// 打开下方注释,动态赋值appid和appsecret即可支持多小程序登录
// data.appid = "xxxx";
// data.appsecret = "xxxx";
res = await vk.openapi.weixin.loginByWeixin({
data,
context: originalParam.context,
custom,
needKey: false, // 是否需要返回明文的sessionKey或accessToken(为了安全期间,建议设置false)
});
if (res.token) {
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: "login",
login_type: "weixin",
user_id: res.uid,
context: originalParam.context
}, util);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,99 @@
module.exports = {
/**
* 用户登录(微信手机号授权)
* @url user/pub/loginByWeixinPhoneNumber 前端调用的url参数地址
* @description 用户登录(微信授权)
* data 请求参数 说明
* @param {String} code 通过 getphonenumber 事件获取到的code(code和encryptedData+iv二选一)
* @param {String} encryptedData(code和encryptedData+iv二选一)
* @param {String} iv(code和encryptedData+iv二选一)
* @param {String} encryptedKey code2SessionWeixin 接口返回的encryptedKey
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 登录成功之后返回的token信息
* @param {String} tokenExpired token过期时间
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
let {
nickname,
avatar,
gender,
inviteCode,
encryptedKey,
code,
encryptedData,
iv
} = data;
// 获取微信绑定的手机号
res = await vk.openapi.weixin.decrypt.getPhoneNumber({
encryptedKey,
code,
encryptedData,
iv
});
if (res.code !== 0) return res;
let {
mobile
} = res;
let mobileCode = vk.pubfn.random(6);
// 指定操作类型,可选值为login、register,不传此参数时表现为手机号已注册则登录,手机号未注册则进行注册
let type;
// 通过手机号登录
await uniID.setVerifyCode({ mobile, code: mobileCode, expiresIn: 60, type: "login" });
res = await uniID.loginBySms({ mobile, code: mobileCode, type, inviteCode });
if (!res.token) return res;
if (!res.msg) {
res.msg = res.type === "register" ? "注册成功" : "登录成功";
}
if (res.type === "register") {
// 解密获得openid和unionid
let decryptedRes = vk.crypto.aes.decrypt({
data: encryptedKey, // 待解密的原文
});
let wx_openid = {};
let wx_unionid = decryptedRes.unionid;
wx_openid["mp-weixin"] = decryptedRes.openid;
// 查询openid或unionid是否已经存在,如果存在,则不绑定微信.
let queryUserInfo = await vk.daoCenter.userDao.findByUserInfo({
"wx_openid.mp-weixin": wx_openid["mp-weixin"],
"wx_unionid": wx_unionid
});
res.bindWeixin = vk.pubfn.isNull(queryUserInfo) ? true : false;
// 需要额外添加的字段
let mobileStr = mobile.substring(7);
let dataJson = {
nickname: nickname || `手机尾号${mobileStr}用户`,
avatar,
gender
};
if (res.bindWeixin) {
dataJson["wx_openid"] = wx_openid;
dataJson["wx_unionid"] = wx_unionid;
}
res.userInfo = await vk.baseDao.updateAndReturn({
dbName: "uni-id-users",
whereJson: {
_id: res.uid
},
dataJson
});
}
// 日志服务
const loginLogService = vk.require("service/user/util/login_log");
await loginLogService.add({
type: res.type,
login_type: "weixinPhoneNumber",
user_id: res.uid,
context: originalParam.context
}, util);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,24 @@
module.exports = {
/**
* 用户退出登录
* @url user/pub/logout 前端调用的url参数地址
* @description 用户退出登录
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
if(event.uniIdToken){
res = await uniID.logout(event.uniIdToken);
}else{
res.code = 0;
res.msg = "未登录";
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,61 @@
module.exports = {
/**
* 用户注册(账号+密码)
* @url user/pub/register 前端调用的url参数地址
* @description 用户注册(账号+密码)
* data 请求参数 说明
* @param {String} username 用户名,唯一
* @param {String} password 密码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} token 注册完成自动登录之后返回的token信息
* @param {String} tokenExpired token过期时间
* @param {Object} userInfo 用户信息
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: -1, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
if (typeof data.username === "number") data.username = String(data.username).trim();
if (typeof data.password === "number") data.password = String(data.password).trim();
let {
username,
password,
needPermission,
myInviteCode
} = data;
// 验证规则开始 -----------------------------------------------------------
let rules = {
username: [
{ required: true, validator: vk.pubfn.validator("username"), message: '用户名以字母开头,长度在3~32之间,只能包含字母、数字和下划线', trigger: ['blur', 'change'] }
],
password: [
{ validator: vk.pubfn.validator("password"), message: '密码长度在6~18之间,只能包含字母、数字和下划线', trigger: 'blur' }
]
};
// 验证规则结束 -----------------------------------------------------------
// 开始进行验证
let formRulesRes = vk.pubfn.formValidate({
data: data,
rules: rules
});
if (formRulesRes.code !== 0) {
// 表单验证未通过
return formRulesRes;
}
// 表单验证通过,下面写自己的业务逻辑
res = await uniID.register({
username,
password,
needPermission,
myInviteCode
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,52 @@
module.exports = {
/**
* 根据邮箱+验证码重置密码
* @url user/pub/resetPasswordByEmail 前端调用的url参数地址
* data 请求参数 说明
* @param {String} password 重置后的密码
* @param {String} code 验证码
* @param {String} email 邮箱号码
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk , db, _ } = util;
let {
_id,
password,
code,
email
} = data;
let res = { code : -1, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
// 可写与数据库的交互逻辑等等
// 检查验证码是否正确
res = await uniID.verifyCode({
email,
code,
type: 'reset-pwd', // 自2022-08-13起,以从reset改为reset-pwd,与官方uni-id对齐
});
if(res.code !== 0){
return res;
}
// 根据email查询用户是否存在
let userInfo = await vk.baseDao.findByWhereJson({
dbName:"uni-id-users",
whereJson:{
email
}
});
if(!userInfo){
return { code : -1, msg : '邮箱号未注册!' };
}
// 重置密码
res = await uniID.resetPwd({
uid: userInfo._id,
password
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,52 @@
module.exports = {
/**
* 根据手机+验证码重置密码
* @url user/pub/resetPasswordByMobile 前端调用的url参数地址
* data 请求参数 说明
* @param {String} password 重置后的密码
* @param {String} code 验证码
* @param {String} mobile 手机号
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util, originalParam } = event;
let { uniID, config, pubFun, vk , db, _ } = util;
let {
_id,
password,
code,
mobile
} = data;
let res = { code : -1, msg : '' };
// 业务逻辑开始-----------------------------------------------------------
// 可写与数据库的交互逻辑等等
// 检查验证码是否正确
res = await uniID.verifyCode({
mobile,
code,
type: 'reset-pwd', // 自2022-08-13起,以从reset改为reset-pwd,与官方uni-id对齐
});
if(res.code !== 0){
return res;
}
// 根据mobile查询用户是否存在
let userInfo = await vk.baseDao.findByWhereJson({
dbName:"uni-id-users",
whereJson:{
mobile
}
});
if(!userInfo){
return { code : -1, msg : '手机号未注册!' };
}
// 重置密码
res = await uniID.resetPwd({
uid: userInfo._id,
password
});
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,112 @@
var vkmail;
try {
vkmail = require('vk-mail');
} catch (err) {
console.error("请先添加公共模块:vk-mail(右键对应的云函数,点击管理公共模块或扩展库依赖,勾选vk-mail依赖)");
}
var everyDaylimit = 30; // 限制同一个邮箱每天次数
module.exports = {
/**
* 发送邮箱验证码
* @url user/pub/sendEmailCode 前端调用的url参数地址
* @description 发送邮箱验证码
* data 请求参数 说明
* @param {String} email 邮箱
* @param {String} type 验证码类型
* @param {String} serviceType 邮件服务类型,默认为qq
* @param {Boolean|String} checkUserExist 是否需要检测邮箱对应的账号是否存在或不存在,默认不检测,若为exists代表邮箱必须已注册,若为!exists代表邮箱必须未注册
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID, config } = util;
let {
email,
type,
serviceType = "qq",
checkUserExist = false
} = data;
let res = { code: 0, msg: 'ok' };
// 业务逻辑开始-----------------------------------------------------------
if (!vkmail) {
return { code: -1, msg: "请先添加公共模块:vk-mail(右键对应的云函数,点击管理公共模块或扩展库依赖,勾选vk-mail依赖)" };
}
// 参数非空检测
let nullKey = vk.pubfn.isNullOneByObject({ email, type });
if (nullKey) return { code: -1, msg: '参数 ' + nullKey + ' 不能为空' };
if (!vk.pubfn.test(email, "email")) return { code: -1, msg: "邮箱格式错误" };
if (vk.pubfn.isNull(type)) return { code: -1, msg: "type不能为空" };
if (checkUserExist) {
let dcloud_appid = originalParam.context.appId;
// 检测邮箱对应的账号是否存在
let num = await vk.daoCenter.userDao.count({
email,
dcloud_appid: _.or(_.eq(dcloud_appid), _.exists(false))
});
if (checkUserExist === true || checkUserExist === "exists") {
// 邮箱必须已注册用户,如果邮箱未注册,则不发短信
if (num === 0) {
return { code: -1, msg: `邮箱:${email} 未注册`, notExists: true };
}
} else if (checkUserExist === "!exists") {
// 邮箱必须未注册用户,如果邮箱已注册,则不发短信
if (num > 0) {
return { code: -1, msg: `邮箱:${email} 已注册`, exists: true };
}
}
}
// 针对同一个邮箱,发送验证码每天有次数限制
const cacheKey = `sys.sendEmailCode.everyDaylimit-${email}`;
let currNum = await vk.globalDataCache.get(cacheKey);
if (!currNum) currNum = 0;
if (currNum >= everyDaylimit) {
return { code: -1, msg: "此邮箱今日已超最大次数限制,请明日再试" };
}
let code = vk.pubfn.random(6, "0123456789");
let param = {
code,
type,
email
};
// 发送验证码开始
let emailConfig = config.vk.service.email;
// 如果配置设置了过期时间,则使用配置的过期时间,否则默认180秒
param.expiresIn = emailConfig.codeExpiresIn || 180;
let emailService = vkmail.createTransport({
"host": emailConfig[serviceType].host,
"port": emailConfig[serviceType].port,
"secure": emailConfig[serviceType].secure, // use SSL
"auth": emailConfig[serviceType].auth
});
try {
// 发送邮件
await emailService.sendMail({
"from": emailConfig[serviceType].auth.user,
"to": data.email,
"cc": emailConfig[serviceType].auth.user, // 由于邮件可能会被当成垃圾邮件,但只要把邮件抄送给自己一份,就不会被当成垃圾邮件。
"subject": data.subject || `验证码是${code}`, // 邮件的标题
"text": `您的验证码是${code},打死也不要告诉别人哦!`, // 邮件的内容
});
// 发送验证码成功后,设置验证码
await uniID.setVerifyCode(param);
currNum++;
let { startTime } = vk.pubfn.getDayOffsetStartAndEnd(1, new Date());
let second = vk.pubfn.toDecimal((startTime - Date.now()) / 1000, 0)
await vk.globalDataCache.set(cacheKey, currNum, second);
} catch (err) {
console.error(err);
return { code: -1, msg: "邮件发送失败", err };
}
// 发送验证码结束
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,86 @@
var everyDaylimit = 12; // 限制同一个手机号每天次数
module.exports = {
/**
* 发送短信的验证码
* @url user/pub/sendSmsCode 前端调用的url参数地址
* @description 发送验证码
* data 请求参数 说明
* @param {String} mobile 手机号
* @param {String} type 验证码类型
* @param {Boolean|String} checkUserExist 是否需要检测手机号对应的账号是否存在或不存在,默认不检测,若为exists代表手机号必须已注册,若为!exists代表手机号必须未注册
*
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {Object} requestRes 原始返回数据
* @param {Object} requestParam 包含服务供应商和发送的手机号
*/
main: async (event) => {
let { data = {}, userInfo, util, originalParam } = event;
let { uniID, config, pubFun, vk, db, _ } = util;
let { uid } = data;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
mobile,
type,
checkUserExist = false
} = data;
if (vk.pubfn.isNull(mobile)) {
return { code: -1, msg: '手机号不能为空' };
}
if (!vk.pubfn.test(mobile, 'mobile')) {
return { code: -1, msg: "手机号格式错误" };
}
if (checkUserExist) {
let dcloud_appid = originalParam.context.appId;
// 检测手机号对应的账号是否存在
let num = await vk.daoCenter.userDao.count({
mobile,
dcloud_appid: _.or(_.eq(dcloud_appid), _.exists(false))
});
if (checkUserExist === true || checkUserExist === "exists") {
// 手机号必须已注册用户,如果手机号未注册,则不发短信
if (num === 0) {
return { code: -1, msg: `手机号:${mobile} 未注册`, notExists: true };
}
} else if (checkUserExist === "!exists") {
// 手机号必须未注册用户,如果手机号已注册,则不发短信
if (num > 0) {
return { code: -1, msg: `手机号:${mobile} 已注册`, exists: true };
}
}
}
// 针对同一个手机号,发送验证码每天有次数限制
const cacheKey = `sys.sendSmsCode.everyDaylimit-${mobile}`;
let currNum = await vk.globalDataCache.get(cacheKey);
if (!currNum) currNum = 0;
if (currNum >= everyDaylimit) {
return { code: -1, msg: "此手机号今日已超最大次数限制,请明日再试" };
}
let code = vk.pubfn.random(6, "0123456789");
let provider = "unicloud";
if (vk.pubfn.getData(config, "vk.service.sms.aliyun.enable")) {
provider = "aliyun";
}
res = await vk.system.smsUtil.sendSmsVerifyCode({
provider,
code,
type,
phone: mobile,
expiresIn: 180, // 验证码实际有效时间,必须是60的倍数
});
if (res.code == 0) {
currNum++;
let { startTime } = vk.pubfn.getDayOffsetStartAndEnd(1, new Date());
let second = vk.pubfn.toDecimal((startTime - Date.now()) / 1000, 0)
await vk.globalDataCache.set(cacheKey, currNum, second);
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,28 @@
module.exports = {
/**
* 密码加密测试
* @url user/sys/encryptPwd 前端调用的url参数地址
* @description 密码加密测试
* @param {Object} data 请求参数
* @param {String} uniIdToken 用户token
* @param {Object} util 公共工具包
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} password 加密后的字符串
*/
main: async (event) => {
let { util } = event;
let { uniID } = util;
let res = {};
// 业务逻辑开始-----------------------------------------------------------
const password = await uniID.encryptPwd('123456');
res = {
code: 0,
msg: '密码加密完成',
password
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,27 @@
module.exports = {
/**
* 设置验证码
* @url user/sys/setVerifyCode 前端调用的url参数地址
* @description 设置验证码(此接口正式环境不要暴露,故写在了sys目录下)
* data 请求参数 说明
* @param {String} email 邮箱
* @param {String} mobile 手机号
* @param {String} type 验证码类型
* res 返回参数说明
* @param {Number} code 错误码,0表示成功
* @param {String} msg 详细信息
* @param {String} email 邮箱
* @param {String} mobile 手机号
* @param {String} verifyCode 验证码(uni 1.1.2开始不再返回verifyCode)
*/
main: async (event) => {
let { data = {}, util } = event;
let { uniID } = util;
let { email, mobile, code, type } = data;
let res = {code : -1, msg : ''};
// 业务逻辑开始-----------------------------------------------------------
res = await uniID.setVerifyCode(data);
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,72 @@
module.exports = {
/**
* 添加登录日志
* @param {Object} type 日志类型 login logout
* @param {String} login_type 登录类型 phone password weixin alipay
* @param {Object} user_id 用户ID
* @param {String} ip ip地址
* @param {String} ua userAgent
* @param {String} os 操作系统
* @param {String} platform 平台信息
* @param {Object} context 请求上下文
* @param {Number} state 1:登录成功 0 登录失败
*/
add: async (event, util) => {
let { uniID, config, pubFun, vk, db, _ } = util;
let res = { code: 0, msg: '' };
// 业务逻辑开始-----------------------------------------------------------
let {
type,
login_type,
user_id,
ip,
ua,
os,
platform,
context = {},
state = 1,
dcloud_appid
} = event;
if (context) {
ip = context.CLIENTIP;
ua = context.CLIENTUA;
os = context.OS;
platform = context.PLATFORM;
dcloud_appid = context.APPID;
}
try {
if (vk.pubfn.getData(config, "vk.service.log.login.status")) {
let nowDate = new Date();
let {
now
} = vk.pubfn.getCommonTime(nowDate);
// 增加登录日志
await vk.baseDao.add({
dbName: "uni-id-log",
dataJson: {
type,
login_type,
user_id,
ip,
ua,
os,
platform,
state,
dcloud_appid,
date:{
...now,
date_day_str: vk.pubfn.timeFormat(nowDate, "yyyy-MM-dd"),
date_month_str: vk.pubfn.timeFormat(nowDate, "yyyy-MM")
}
}
});
} else {
console.log("已关闭登录日志");
}
} catch (err) {
console.log("日志写入错误");
}
// 业务逻辑结束-----------------------------------------------------------
return res;
}
}
@@ -0,0 +1,12 @@
## 统一用户中心
#### `router/service/user`
#### 此目录为集成`uni-id`账户系统的实现(开发者自己系统的逻辑尽量不要写在此处,应写在`service/admin`或`service/client`或`其他`目录下
```
.
├── user──────────────────────# 统一用户中心服务(已集成uniID)
│ ── └── kh───────────────────# kh函数为必须登录后才能访问的函数(客户端用户)
│ ── └── pub──────────────────# pub函数为所有人都可以访问,不限制
│ ── └── sys──────────────────# sys函数为需要授权才能访问的函数(如商家工作人员等)
└─────────────────────────────────
```