From 916b501c6715747383ac86b67f7aaa09aba55890 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 10 Jul 2026 12:15:31 +0800 Subject: [PATCH] feat: add admin + reset admin password toolbar for sub_distributors - api/add_sub_distributor_admin.dspy: create org+user+reseller.admin role, show password - api/reset_sub_distributor_admin_pwd.dspy: reset admin password, show new password - sub_distributors_list: add toolbar buttons with popup binds --- wwwroot/api/add_sub_distributor_admin.dspy | 76 +++++++++++++++++++ .../api/reset_sub_distributor_admin_pwd.dspy | 35 +++++++++ wwwroot/sub_distributors_list/index.ui | 50 ++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 wwwroot/api/add_sub_distributor_admin.dspy create mode 100644 wwwroot/api/reset_sub_distributor_admin_pwd.dspy diff --git a/wwwroot/api/add_sub_distributor_admin.dspy b/wwwroot/api/add_sub_distributor_admin.dspy new file mode 100644 index 0000000..a20747d --- /dev/null +++ b/wwwroot/api/add_sub_distributor_admin.dspy @@ -0,0 +1,76 @@ +"""Create an admin user for the selected sub_distributor. Show the initial password.""" +sub_distributor_id = params_kw.get('sub_distributor_id') or params_kw.get('id') +if not sub_distributor_id: + return {'widgettype': 'Error', 'options': {'title': 'Error', 'message': 'Missing sub_distributor_id', 'timeout': 3}} + +userorgid = await get_userorgid() +if not userorgid: + return {'widgettype': 'Error', 'options': {'title': 'Auth Error', 'message': 'Please login', 'timeout': 3}} + +dbname = get_module_dbname('supplychain') +async with DBPools().sqlorContext(dbname) as sor: + recs = await sor.sqlExe( + 'SELECT id, sub_dist_name FROM sub_distributors WHERE id = ${sid}$', + {'sid': sub_distributor_id} + ) + if not recs: + return {'widgettype': 'Error', 'options': {'title': 'Error', 'message': '二级分销商不存在', 'timeout': 3}} + sd = recs[0] + +org_id = sd.id +admin_username = f'admin_{sub_distributor_id[:8]}' + +async with DBPools().sqlorContext('sage') as sor_sage: + # Check/ensure org exists + org_recs = await sor_sage.sqlExe( + 'SELECT id FROM organization WHERE id = ${oid}$', {'oid': org_id} + ) + if not org_recs: + now = timestampstr() + await sor_sage.C('organization', { + 'id': org_id, 'orgname': sd.sub_dist_name, 'create_at': now, + }) + + # Check if admin already exists + user_recs = await sor_sage.sqlExe( + 'SELECT id FROM users WHERE username = ${un}$', {'un': admin_username} + ) + if user_recs: + return { + 'widgettype': 'Message', + 'options': { + 'title': '管理员已存在', + 'message': f'管理员 {admin_username} 已存在,请用重置密码功能', + 'type': 'warning', 'timeout': 5 + } + } + + # Generate password from uuid + pwd = getID().replace('-','')[:12] + now = timestampstr() + user_id = getID() + + await sor_sage.C('users', { + 'id': user_id, 'username': admin_username, + 'password': password_encode(pwd), + 'orgid': org_id, 'nick_name': sd.sub_dist_name + '管理员', + 'user_status': '1', 'created_at': now, + }) + + # Assign reseller.admin role + role_recs = await sor_sage.sqlExe( + "SELECT id FROM role WHERE role = ${r}$", {'r': 'reseller.admin'} + ) + if role_recs: + await sor_sage.C('userrole', { + 'userid': user_id, 'roleid': role_recs[0].id, + }) + + return { + 'widgettype': 'Message', + 'options': { + 'title': '管理员创建成功', + 'message': f'用户名: {admin_username}\n初始密码: {pwd}\n请妥善保管,登录后建议修改', + 'type': 'success', 'timeout': 0 + } + } diff --git a/wwwroot/api/reset_sub_distributor_admin_pwd.dspy b/wwwroot/api/reset_sub_distributor_admin_pwd.dspy new file mode 100644 index 0000000..634ed7c --- /dev/null +++ b/wwwroot/api/reset_sub_distributor_admin_pwd.dspy @@ -0,0 +1,35 @@ +"""Reset the admin password for the selected sub_distributor.""" +sub_distributor_id = params_kw.get('sub_distributor_id') or params_kw.get('id') +if not sub_distributor_id: + return {'widgettype': 'Error', 'options': {'title': 'Error', 'message': 'Missing sub_distributor_id', 'timeout': 3}} + +admin_username = f'admin_{sub_distributor_id[:8]}' +new_pwd = getID().replace('-','')[:12] + +async with DBPools().sqlorContext('sage') as sor: + recs = await sor.sqlExe( + 'SELECT id FROM users WHERE username = ${un}$', {'un': admin_username} + ) + if not recs: + return { + 'widgettype': 'Message', + 'options': { + 'title': '管理员不存在', + 'message': f'管理员 {admin_username} 不存在,请先创建', + 'type': 'warning', 'timeout': 5 + } + } + + await sor.U('users', { + 'id': recs[0].id, + 'password': password_encode(new_pwd), + }) + + return { + 'widgettype': 'Message', + 'options': { + 'title': '密码重置成功', + 'message': f'用户名: {admin_username}\n新密码: {new_pwd}', + 'type': 'success', 'timeout': 0 + } + } diff --git a/wwwroot/sub_distributors_list/index.ui b/wwwroot/sub_distributors_list/index.ui index 48d1a54..0cb6e49 100644 --- a/wwwroot/sub_distributors_list/index.ui +++ b/wwwroot/sub_distributors_list/index.ui @@ -21,6 +21,18 @@ "name": "distribution_agreements", "icon": "{{entire_url('/imgs/distribution_agreement_items.svg')}}", "label": "分销协议" + }, + { + "selected_row": true, + "name": "add_admin", + "icon": "{{entire_url('/imgs/userrole.svg')}}", + "label": "添加管理员" + }, + { + "selected_row": true, + "name": "reset_admin_pwd", + "icon": "{{entire_url('/imgs/key.svg')}}", + "label": "重置管理员密码" } ] }, @@ -336,6 +348,44 @@ "method": "POST", "url": "{{entire_url('../api/ensure_distribution_agreement.dspy')}}" } + }, + { + "wid": "self", + "event": "add_admin", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "添加管理员", + "height": "250px", + "width": "400px" + }, + "params_mapping": { + "mapping": {"id": "sub_distributor_id"}, + "need_other": false + }, + "options": { + "method": "POST", + "url": "{{entire_url('../api/add_sub_distributor_admin.dspy')}}" + } + }, + { + "wid": "self", + "event": "reset_admin_pwd", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "重置管理员密码", + "height": "250px", + "width": "400px" + }, + "params_mapping": { + "mapping": {"id": "sub_distributor_id"}, + "need_other": false + }, + "options": { + "method": "POST", + "url": "{{entire_url('../api/reset_sub_distributor_admin_pwd.dspy')}}" + } } ]