提交bug修复 后台权限管理

This commit is contained in:
super
2026-04-11 22:25:14 +08:00
parent b400b494ad
commit 7589b4e418
36 changed files with 956 additions and 359 deletions

View File

@@ -70,6 +70,26 @@ def _proxy_backend_java(method, path, *, params=None, json_data=None, files=None
return data, None, 200
def _format_permission_item(item):
return {
'id': item.get('id'),
'name': item.get('name') or '',
'column_key': item.get('columnKey') or '',
'menu_type': item.get('menuType') or 'app',
'route_path': item.get('routePath') or '',
'created_at': (item.get('createdAt') or '').replace('T', ' ')[:16],
}
def _sync_user_column_permissions(user_id, column_ids):
_, error_response, status = _proxy_backend_java(
'PUT',
f'/api/admin/permission-users/{user_id}/columns',
json_data={'columnIds': [int(x) for x in (column_ids or []) if str(x).isdigit()]},
)
return error_response, status
# ---------- 用户管理 ----------
@admin_api.route('/users')
@@ -155,6 +175,7 @@ def list_users():
'total': total,
'page': page,
'page_size': page_size,
'current_user_id': session.get('user_id'),
'current_user_role': role,
'current_user_username': current_row.get('username') or '',
'admins': admins,
@@ -203,9 +224,7 @@ def create_user():
want_created_by = current_row['id']
is_admin = 1 if want_role in ('super_admin', 'admin') else 0
pwd_hash = generate_password_hash(password, method='pbkdf2:sha256')
column_ids = data.get('column_ids')
if column_ids is None:
column_ids = []
column_ids = data.get('column_ids') or []
try:
conn = get_db()
with conn.cursor() as cur:
@@ -214,9 +233,11 @@ def create_user():
(username, pwd_hash, is_admin, want_role, want_created_by),
)
new_uid = cur.lastrowid
_set_user_column_permissions(cur, new_uid, column_ids)
conn.commit()
conn.close()
error_response, status = _sync_user_column_permissions(new_uid, column_ids)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'msg': '用户创建成功'})
except pymysql.IntegrityError:
return jsonify({'success': False, 'error': '用户名已存在'})
@@ -270,10 +291,12 @@ def update_user(uid):
(is_admin, want_role, uid),
)
column_ids = data.get('column_ids')
if column_ids is not None:
_set_user_column_permissions(cur, uid, column_ids)
conn.commit()
conn.close()
if column_ids is not None:
error_response, status = _sync_user_column_permissions(uid, column_ids)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'msg': '更新成功'})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
@@ -388,156 +411,124 @@ def history():
@admin_api.route('/columns')
@admin_required
def list_columns():
"""获取栏目列表(用于栏目配置与用户权限选择)"""
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute(
"SELECT id, name, column_key, created_at FROM columns ORDER BY id"
)
rows = cur.fetchall()
conn.close()
items = [
{
'id': r['id'],
'name': r['name'] or '',
'column_key': r['column_key'] or '',
'created_at': r['created_at'].strftime('%Y-%m-%d %H:%M') if r.get('created_at') else '',
}
for r in rows
]
return jsonify({'success': True, 'items': items})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
params = {}
menu_type = (request.args.get('menu_type') or '').strip()
if menu_type:
params['menuType'] = menu_type
result, error_response, status = _proxy_backend_java(
'GET',
'/api/admin/permission-menus',
params=params or None,
)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'items': [_format_permission_item(item) for item in (result.get('data') or [])]})
@admin_api.route('/column', methods=['POST'])
@admin_required
def create_column():
"""新增栏目"""
data = request.get_json() or {}
name = (data.get('name') or '').strip()
column_key = (data.get('column_key') or '').strip()
menu_type = (data.get('menu_type') or 'app').strip() or 'app'
route_path = (data.get('route_path') or '').strip()
if not name:
return jsonify({'success': False, 'error': '栏目名不能为空'})
if not column_key:
return jsonify({'success': False, 'error': '栏目标识不能为空'})
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute(
"INSERT INTO columns (name, column_key) VALUES (%s, %s)",
(name, column_key),
)
cid = cur.lastrowid
conn.commit()
conn.close()
return jsonify({'success': True, 'msg': '创建成功', 'id': cid})
except pymysql.IntegrityError:
return jsonify({'success': False, 'error': '栏目标识已存在'})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
if not route_path:
return jsonify({'success': False, 'error': '菜单路由不能为空'})
result, error_response, status = _proxy_backend_java(
'POST',
'/api/admin/permission-menus',
json_data={
'name': name,
'columnKey': column_key,
'menuType': menu_type,
'routePath': route_path,
},
)
if error_response is not None:
return error_response, status
item = result.get('data') or {}
return jsonify({'success': True, 'msg': result.get('message') or '创建成功', 'id': item.get('id')})
@admin_api.route('/column/<int:cid>', methods=['PUT'])
@admin_required
def update_column(cid):
"""更新栏目"""
data = request.get_json() or {}
name = (data.get('name') or '').strip()
column_key = (data.get('column_key') or '').strip()
menu_type = (data.get('menu_type') or 'app').strip() or 'app'
route_path = (data.get('route_path') or '').strip()
if not name:
return jsonify({'success': False, 'error': '栏目名不能为空'})
if not column_key:
return jsonify({'success': False, 'error': '栏目标识不能为空'})
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute(
"UPDATE columns SET name = %s, column_key = %s WHERE id = %s",
(name, column_key, cid),
)
if cur.rowcount == 0:
conn.close()
return jsonify({'success': False, 'error': '栏目不存在'})
conn.commit()
conn.close()
return jsonify({'success': True, 'msg': '更新成功'})
except pymysql.IntegrityError:
return jsonify({'success': False, 'error': '栏目标识已存在'})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
if not route_path:
return jsonify({'success': False, 'error': '菜单路由不能为空'})
result, error_response, status = _proxy_backend_java(
'PUT',
f'/api/admin/permission-menus/{cid}',
json_data={
'name': name,
'columnKey': column_key,
'menuType': menu_type,
'routePath': route_path,
},
)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'msg': result.get('message') or '更新成功'})
@admin_api.route('/column/<int:cid>', methods=['DELETE'])
@admin_required
def delete_column(cid):
"""删除栏目(会同步删除用户栏目权限关联)"""
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute("DELETE FROM user_column_permission WHERE column_id = %s", (cid,))
cur.execute("DELETE FROM columns WHERE id = %s", (cid,))
if cur.rowcount == 0:
conn.close()
return jsonify({'success': False, 'error': '栏目不存在'})
conn.commit()
conn.close()
return jsonify({'success': True, 'msg': '删除成功'})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
result, error_response, status = _proxy_backend_java(
'DELETE',
f'/api/admin/permission-menus/{cid}',
)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'msg': result.get('message') or '删除成功'})
@admin_api.route('/user/<int:uid>/columns')
@admin_required
def get_user_columns(uid):
"""获取某用户的栏目权限 ID 列表(编辑用户时回显)"""
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute(
"SELECT column_id FROM user_column_permission WHERE user_id = %s",
(uid,),
)
rows = cur.fetchall()
conn.close()
column_ids = [r['column_id'] for r in rows]
return jsonify({'success': True, 'column_ids': column_ids})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
params = {}
menu_type = (request.args.get('menu_type') or '').strip()
if menu_type:
params['menuType'] = menu_type
result, error_response, status = _proxy_backend_java(
'GET',
f'/api/admin/permission-users/{uid}/columns',
params=params or None,
)
if error_response is not None:
return error_response, status
payload = result.get('data') or {}
return jsonify({'success': True, 'column_ids': payload.get('columnIds') or []})
@admin_api.route('/user/<int:uid>/column-permissions')
@admin_required
def get_user_column_permissions(uid):
"""根据用户获取其拥有的栏目权限详细信息"""
try:
conn = get_db()
with conn.cursor() as cur:
cur.execute(
"""
SELECT c.id, c.name, c.column_key, c.created_at
FROM user_column_permission ucp
JOIN columns c ON c.id = ucp.column_id
WHERE ucp.user_id = %s
ORDER BY c.id
""",
(uid,),
)
rows = cur.fetchall()
conn.close()
items = [
{
'id': r['id'],
'name': r['name'] or '',
'column_key': r['column_key'] or '',
'created_at': r['created_at'].strftime('%Y-%m-%d %H:%M') if r.get('created_at') else '',
}
for r in rows
]
return jsonify({'success': True, 'items': items})
except Exception as e:
return jsonify({'success': False, 'error': str(e)})
params = {}
menu_type = (request.args.get('menu_type') or '').strip()
if menu_type:
params['menuType'] = menu_type
result, error_response, status = _proxy_backend_java(
'GET',
f'/api/admin/permission-users/{uid}/column-permissions',
params=params or None,
)
if error_response is not None:
return error_response, status
return jsonify({'success': True, 'items': [_format_permission_item(item) for item in (result.get('data') or [])]})
def _set_user_column_permissions(cur, user_id, column_ids):

View File

@@ -23,8 +23,8 @@ bucket_path = "nanri-image/"
file_url_pre = f"https://{bucket}.oss-cn-hangzhou.aliyuncs.com/"
import os
backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://127.0.0.1:18080').rstrip('/')
# backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://8.136.19.173:18080').rstrip('/')
# backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://127.0.0.1:18080').rstrip('/')
backend_java_base_url = os.environ.get('BACKEND_JAVA_BASE_URL', 'http://8.136.19.173:18080').rstrip('/')
os.environ['OSS_ACCESS_KEY_ID'] = accessKeyId
os.environ['OSS_ACCESS_KEY_SECRET'] = accessKeySecret
os.environ['SECRET_KEY'] = "ddffc7c1d02121d9554d7b080b2511b6"

View File

@@ -114,6 +114,18 @@ def init_db():
UNIQUE KEY uk_column_key (column_key)
)
""")
try:
cur.execute("ALTER TABLE columns ADD COLUMN menu_type VARCHAR(20) NOT NULL DEFAULT 'app' COMMENT '菜单类型: app/admin' AFTER column_key")
except Exception:
pass
try:
cur.execute("ALTER TABLE columns ADD COLUMN route_path VARCHAR(255) NOT NULL DEFAULT '' COMMENT '菜单路由或页面标识' AFTER menu_type")
except Exception:
pass
try:
cur.execute("UPDATE columns SET menu_type = 'app' WHERE menu_type IS NULL OR menu_type = ''")
except Exception:
pass
cur.execute("""
CREATE TABLE IF NOT EXISTS user_column_permission (
user_id INT NOT NULL,

View File

@@ -637,8 +637,11 @@
// ========== 用户管理 ==========
var userPage = 1, userPageSize = 15;
var currentUserId = null;
var currentUserRole = 'admin';
var currentUserUsername = '';
var currentUserAdminPermissionKeys = {};
var currentUserAdminPermissionRoutes = {};
var adminsList = [];
function roleLabel(role) {
if (role === 'super_admin') return '超级管理员';
@@ -663,6 +666,7 @@
tbody.innerHTML = '<tr><td colspan="6" class="empty-tip">加载失败: ' + (res.error || '') + '</td></tr>';
return;
}
currentUserId = res.current_user_id || null;
currentUserRole = res.current_user_role || 'admin';
currentUserUsername = (res.current_user_username || '').trim();
adminsList = res.admins || [];
@@ -683,6 +687,7 @@
updateCreateFormByRole();
updateUserFilterByRole();
updateDedupeTotalDataAccess();
loadCurrentUserAdminPermissions();
})
.catch(function() {
document.getElementById('userListBody').innerHTML = '<tr><td colspan="6" class="empty-tip">请求失败</td></tr>';
@@ -722,6 +727,50 @@
if (usersPanel) usersPanel.classList.add('active');
}
}
function updateShopManageAccess() {
var tab = document.querySelector('.tab[data-tab="shop-manage"]');
var panel = document.getElementById('panel-shop-manage');
if (!tab || !panel) return;
var canUse = currentUserRole === 'super_admin' ||
!!currentUserAdminPermissionKeys['admin_shop_manage'] ||
!!currentUserAdminPermissionRoutes['shop-manage'];
tab.style.display = canUse ? '' : 'none';
panel.style.display = canUse ? '' : 'none';
if (!canUse && tab.classList.contains('active')) {
tab.classList.remove('active');
panel.classList.remove('active');
var usersTab = document.querySelector('.tab[data-tab="users"]');
var usersPanel = document.getElementById('panel-users');
if (usersTab) usersTab.classList.add('active');
if (usersPanel) usersPanel.classList.add('active');
}
}
function loadCurrentUserAdminPermissions() {
currentUserAdminPermissionKeys = {};
currentUserAdminPermissionRoutes = {};
if (!currentUserId || currentUserRole === 'super_admin') {
updateShopManageAccess();
return;
}
fetch('/api/admin/user/' + currentUserId + '/column-permissions?menu_type=admin')
.then(function(r) { return r.json(); })
.then(function(res) {
if (!res.success) {
updateShopManageAccess();
return;
}
(res.items || []).forEach(function(item) {
var columnKey = (item.column_key || '').trim();
var routePath = (item.route_path || '').trim();
if (columnKey) currentUserAdminPermissionKeys[columnKey] = true;
if (routePath) currentUserAdminPermissionRoutes[routePath] = true;
});
updateShopManageAccess();
})
.catch(function() {
updateShopManageAccess();
});
}
var allColumnsList = [];
function loadColumnsForPermission() {
fetch('/api/admin/columns')