feat: domain-based reseller binding on registration + test SMS bypass

This commit is contained in:
yumoqing 2026-07-10 11:24:25 +08:00
parent 35750d5938
commit 18913e7064
4 changed files with 301 additions and 260 deletions

View File

@ -118,6 +118,8 @@ async def register_user(sor, ns):
ns.created_at = timestampstr() ns.created_at = timestampstr()
ns.login_fail_count = 0 ns.login_fail_count = 0
ns1 = DictObject(id=id, orgname=ns.username) ns1 = DictObject(id=id, orgname=ns.username)
if ns.get('parentid'):
ns1.parentid = ns.parentid
await create_org(sor, ns1) await create_org(sor, ns1)
roles = [ roles = [
{ {

View File

@ -1,36 +1,40 @@
# TEST MODE: 不发送真实短信,返回固定验证码 123456
phone = params_kw.cellphone phone = params_kw.cellphone
if phone is None: if phone is None:
return { return {
"status": "error", "status": "error",
"data":{ "data": {
"message": "没有收到手机号" "message": "没有收到手机号"
} }
} }
# 使用短信模块发布的sms_engine实例生成验证码参数手机号
try: try:
xx = await sms_engine.generate_sms_code(phone) code = '123456'
code_id = getID()
from datetime import datetime, timedelta
expire_time = datetime.now() + timedelta(minutes=5)
env = request._run_ns
async with get_sor_context(env, 'smssend') as sor:
await sor.C('validatecode', {
'id': code_id,
'vcode': code,
'expire_time': expire_time,
'del_flg': '0',
'create_at': datetime.now()
})
debug(f'TEST MODE: code_id={code_id}, code={code}, phone={phone}')
return {
"status": "ok",
"data": {
"message": f"测试模式: 验证码 {code}",
"key": code_id
}
}
except Exception as e: except Exception as e:
debug(f'gen_sms_code error: {e}') debug(f'gen_sms_code error: {e}')
exception(f'gen_sms_code error for {phone}: {e}') exception(f'gen_sms_code error for {phone}: {e}')
return { return {
"status": "error", "status": "error",
"data": { "data": {
"message": f"发送验证码出错: {e}" "message": f"发送验证码出错: {e}"
} }
} }
if xx is None:
return {
"status": "error",
"data": {
"message": "发送验证码出错请检查短信模板配置和百度API连接"
}
}
id, code = xx
debug(f'{id=},{code=}')
return {
"status": "ok",
"data": {
"message": "短信码已生成",
"key": id
}
}

View File

@ -1,78 +1,96 @@
debug(f'register.dspy: {params_kw=}') debug(f'register.dspy: {params_kw=}')
db = DBPools() db = DBPools()
dbname = get_module_dbname('rbac') dbname = get_module_dbname('rbac')
# 根据注册域名查找所属分销商
parentid = None
try:
host = request.host
env = request._run_ns
domain_info = await env.get_domain_by_host(request, host)
if domain_info:
parentid = domain_info.resellerid
debug(f'register: domain={host} -> resellerid={parentid}')
else:
debug(f'register: domain={host} not found in tenant_domain')
except Exception as e:
exception(f'register: domain lookup error: {e}')
if parentid:
params_kw.parentid = parentid
async with db.sqlorContext(dbname) as sor: async with db.sqlorContext(dbname) as sor:
data = await register_user(sor, params_kw) data = await register_user(sor, params_kw)
data = DictObject(**data) data = DictObject(**data)
if data.status == 'error': if data.status == 'error':
debug(f"register error: {data.data.message}") debug(f"register error: {data.data.message}")
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "注册失败", "title": "注册失败",
"message": data.data.message "message": data.data.message
} }
} }
user = data.data.user user = data.data.user
orgid = user.orgid orgid = user.orgid
try: try:
await openCustomerAccounts(sor, '0', orgid) await openCustomerAccounts(sor, '0', orgid)
debug(f'{orgid} accounts opened') debug(f'{orgid} accounts opened')
except Exception as e: except Exception as e:
exception(f'{e},{orgid=}') exception(f'{e},{orgid=}')
# 检查是否有促销码,如果有则绑定客户归属关系 # 检查是否有促销码,如果有则绑定客户归属关系
promo_code_id = params_kw.get('promo_code_id') promo_code_id = params_kw.get('promo_code_id')
if promo_code_id: if promo_code_id:
try: try:
await bind_customer(request, DictObject(promo_code_id=promo_code_id, customerid=orgid)) await bind_customer(request, DictObject(promo_code_id=promo_code_id, customerid=orgid))
debug(f'register: customer {orgid} bound via promo_code {promo_code_id}') debug(f'register: customer {orgid} bound via promo_code {promo_code_id}')
except Exception as e: except Exception as e:
exception(f'register: bind_customer failed for {orgid} via {promo_code_id}: {e}') exception(f'register: bind_customer failed for {orgid} via {promo_code_id}: {e}')
# 注册成功后自动登录 # 注册成功后自动登录
await remember_user(user.id, username=user.username, userorgid=user.orgid) await remember_user(user.id, username=user.username, userorgid=user.orgid)
return { return {
"widgettype": "Message", "widgettype": "Message",
"options": { "options": {
"timeout": 3, "timeout": 3,
"auto_open": True, "auto_open": True,
"title": "注册成功", "title": "注册成功",
"message": f"{user.username} 注册成功,已自动登录" "message": f"{user.username} 注册成功,已自动登录"
}, },
"binds": [ "binds": [
{ {
"wid": "self", "wid": "self",
"event": "dismissed", "event": "dismissed",
"actiontype": "urlwidget", "actiontype": "urlwidget",
"target": "window.user_container", "target": "window.user_container",
"options": { "options": {
"url": entire_url('/rbac/user/userinfo.ui') "url": entire_url('/rbac/user/userinfo.ui')
} }
}, },
{ {
"wid": "self", "wid": "self",
"event": "dismissed", "event": "dismissed",
"actiontype": "script", "actiontype": "script",
"target": "body.login_window", "target": "body.login_window",
"script": "if(this.destroy) this.destroy()" "script": "if(this.destroy) this.destroy()"
}, },
{ {
"wid": "self", "wid": "self",
"event": "dismissed", "event": "dismissed",
"actiontype": "script", "actiontype": "script",
"target": "self", "target": "self",
"script": "if(bricks.app && bricks.app.dispatch) bricks.app.dispatch('user_logined')" "script": "if(bricks.app && bricks.app.dispatch) bricks.app.dispatch('user_logined')"
} }
] ]
} }
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "注册失败", "title": "注册失败",
"message": "系统错误,请稍后重试" "message": "系统错误,请稍后重试"
} }
} }

View File

@ -11,186 +11,203 @@ cfm_password = params_kw.cfm_password
# 基本参数校验 # 基本参数校验
if not username: if not username:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "注册失败", "title": "注册失败",
"message": "请输入用户名" "message": "请输入用户名"
} }
} }
if not mobile: if not mobile:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "注册失败", "title": "注册失败",
"message": "请输入手机号" "message": "请输入手机号"
} }
} }
if not password: if not password:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "注册失败", "title": "注册失败",
"message": "请输入密码" "message": "请输入密码"
} }
} }
if password != cfm_password: if password != cfm_password:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "注册失败", "title": "注册失败",
"message": "两次输入的密码不一致" "message": "两次输入的密码不一致"
} }
} }
if not key or not sms_code: if not key or not sms_code:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "注册失败", "title": "注册失败",
"message": "请先发送并输入短信验证码" "message": "请先发送并输入短信验证码"
} }
} }
# 验证短信码 # 验证短信码
try: try:
ok = await sms_engine.check_sms_code(key, sms_code) ok = await sms_engine.check_sms_code(key, sms_code)
except Exception as e: except Exception as e:
exception(f'sms_register sms check error: {e}') exception(f'sms_register sms check error: {e}')
ok = False ok = False
if not ok: if not ok:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 3, "timeout": 3,
"title": "验证失败", "title": "验证失败",
"message": "短信验证码错误或已过期,请重新获取" "message": "短信验证码错误或已过期,请重新获取"
} }
} }
# 根据注册域名查找所属分销商
parentid = None
try:
host = request.host
env = request._run_ns
domain_info = await env.get_domain_by_host(request, host)
if domain_info:
parentid = domain_info.resellerid
debug(f'sms_register: domain={host} -> resellerid={parentid}')
else:
debug(f'sms_register: domain={host} not found in tenant_domain')
except Exception as e:
exception(f'sms_register: domain lookup error: {e}')
# 短信验证通过,注册用户 # 短信验证通过,注册用户
db = DBPools() db = DBPools()
dbname = get_module_dbname('rbac') dbname = get_module_dbname('rbac')
try: try:
async with db.sqlorContext(dbname) as sor: async with db.sqlorContext(dbname) as sor:
# 检查手机号是否已注册 # 检查手机号是否已注册
existing = await sor.R('users', {'mobile': mobile}) existing = await sor.R('users', {'mobile': mobile})
if existing: if existing:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "注册失败", "title": "注册失败",
"message": "该手机号已注册,请直接登录" "message": "该手机号已注册,请直接登录"
} }
} }
# 检查用户名是否已存在 # 检查用户名是否已存在
existing_user = await sor.R('users', {'username': username}) existing_user = await sor.R('users', {'username': username})
if existing_user: if existing_user:
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "注册失败", "title": "注册失败",
"message": "用户名已被占用" "message": "用户名已被占用"
} }
} }
# 调用注册函数 # 调用注册函数
reg_params = DictObject( reg_params = DictObject(
username=username, username=username,
mobile=mobile, mobile=mobile,
password=password, password=password,
cfm_password=cfm_password cfm_password=cfm_password
) )
data = await register_user(sor, reg_params) if parentid:
data = DictObject(**data) reg_params.parentid = parentid
if data.status == 'error':
debug(f"sms_register error: {data.data.message}")
return {
"widgettype": "Error",
"options": {
"timeout": 5,
"title": "注册失败",
"message": data.data.message
}
}
user = data.data.user data = await register_user(sor, reg_params)
orgid = user.orgid data = DictObject(**data)
try: if data.status == 'error':
await openCustomerAccounts(sor, '0', orgid) debug(f"sms_register error: {data.data.message}")
debug(f'{orgid} accounts opened') return {
except Exception as e: "widgettype": "Error",
exception(f'{e},{orgid=}') "options": {
"timeout": 5,
"title": "注册失败",
"message": data.data.message
}
}
# 检查是否有促销码,如果有则绑定客户归属关系 user = data.data.user
promo_code_id = params_kw.get('promo_code_id') orgid = user.orgid
if promo_code_id: try:
try: await openCustomerAccounts(sor, '0', orgid)
await bind_customer(request, DictObject(promo_code_id=promo_code_id, customerid=orgid)) debug(f'{orgid} accounts opened')
debug(f'sms_register: customer {orgid} bound via promo_code {promo_code_id}') except Exception as e:
except Exception as e: exception(f'{e},{orgid=}')
exception(f'sms_register: bind_customer failed for {orgid} via {promo_code_id}: {e}')
# 注册成功后自动登录 # 检查是否有促销码,如果有则绑定客户归属关系
await remember_user(user.id, username=user.username, userorgid=user.orgid) promo_code_id = params_kw.get('promo_code_id')
return { if promo_code_id:
"widgettype": "Message", try:
"options": { await bind_customer(request, DictObject(promo_code_id=promo_code_id, customerid=orgid))
"timeout": 3, debug(f'sms_register: customer {orgid} bound via promo_code {promo_code_id}')
"auto_open": True, except Exception as e:
"title": "注册成功", exception(f'sms_register: bind_customer failed for {orgid} via {promo_code_id}: {e}')
"message": f"{user.username} 注册成功,已自动登录"
}, # 注册成功后自动登录
"binds": [ await remember_user(user.id, username=user.username, userorgid=user.orgid)
{ return {
"wid": "self", "widgettype": "Message",
"event": "dismissed", "options": {
"actiontype": "urlwidget", "timeout": 3,
"target": "window.user_container", "auto_open": True,
"options": { "title": "注册成功",
"url": entire_url('/rbac/user/userinfo.ui') "message": f"{user.username} 注册成功,已自动登录"
} },
}, "binds": [
{ {
"wid": "self", "wid": "self",
"event": "dismissed", "event": "dismissed",
"actiontype": "script", "actiontype": "urlwidget",
"target": "body.login_window", "target": "window.user_container",
"script": "this.destroy()" "options": {
}, "url": entire_url('/rbac/user/userinfo.ui')
{ }
"wid": "self", },
"event": "dismissed", {
"actiontype": "script", "wid": "self",
"target": "self", "event": "dismissed",
"script": "if(bricks.app && bricks.app.dispatch) bricks.app.dispatch('user_logined')" "actiontype": "script",
} "target": "body.login_window",
] "script": "this.destroy()"
} },
{
"wid": "self",
"event": "dismissed",
"actiontype": "script",
"target": "self",
"script": "if(bricks.app && bricks.app.dispatch) bricks.app.dispatch('user_logined')"
}
]
}
except Exception as e: except Exception as e:
exception(f'sms_register error: {e}') exception(f'sms_register error: {e}')
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "系统错误", "title": "系统错误",
"message": f"注册失败: {e}" "message": f"注册失败: {e}"
} }
} }
return { return {
"widgettype": "Error", "widgettype": "Error",
"options": { "options": {
"timeout": 5, "timeout": 5,
"title": "注册失败", "title": "注册失败",
"message": "系统错误,请稍后重试" "message": "系统错误,请稍后重试"
} }
} }