246 lines
13 KiB
Python
246 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""hr-system 权限与数据范围底座(F11 / T08)"""
|
||
import json, time, uuid
|
||
from apppublic import log
|
||
from sqlor import Db
|
||
|
||
SCOPE_TYPE_ORG = "org"
|
||
SCOPE_TYPE_FIELD = "field"
|
||
ROLE_EMPLOYEE = "employee"
|
||
ROLE_MANAGER = "manager"
|
||
ROLE_HR_ADMIN = "hr_admin"
|
||
ROLE_SYS_ADMIN = "sys_admin"
|
||
ROLE_ADMIN = "admin"
|
||
BUILTIN_ROLES = (ROLE_EMPLOYEE, ROLE_MANAGER, ROLE_HR_ADMIN, ROLE_SYS_ADMIN)
|
||
_db = None
|
||
|
||
def _get_db():
|
||
global _db
|
||
if _db is None:
|
||
_db = Db("hrs")
|
||
return _db
|
||
|
||
def _now():
|
||
return time.strftime("%Y-%m-%d %H:%M:%S")
|
||
|
||
def _uuid():
|
||
return uuid.uuid4().hex
|
||
|
||
# ---- 角色 ----
|
||
def role_list(req):
|
||
db = _get_db()
|
||
rows = db.query("SELECT r.id, r.role_code, r.role_name, r.remark, r.created_at, (SELECT COUNT(*) FROM sys_role_permission rp WHERE rp.role_id = r.id) AS perm_count FROM sys_role r ORDER BY r.created_at")
|
||
return {"code": 0, "data": rows}
|
||
|
||
def role_detail(req):
|
||
role_id = req.get("id") or req.get("role_id")
|
||
if not role_id: return {"code": 1, "msg": "缺少角色 id"}
|
||
db = _get_db()
|
||
role = db.query_one("SELECT * FROM sys_role WHERE id = %s", (role_id,))
|
||
if not role: return {"code": 1, "msg": "角色不存在"}
|
||
perms = db.query("SELECT perm_id FROM sys_role_permission WHERE role_id = %s", (role_id,))
|
||
role["perm_ids"] = [p["perm_id"] for p in perms]
|
||
return {"code": 0, "data": role}
|
||
|
||
def role_create(req):
|
||
db = _get_db()
|
||
role_code = (req.get("role_code") or "").strip()
|
||
role_name = (req.get("role_name") or "").strip()
|
||
if not role_code or not role_name: return {"code": 1, "msg": "角色编码与名称必填"}
|
||
if db.query_one("SELECT id FROM sys_role WHERE role_code = %s", (role_code,)):
|
||
return {"code": 1, "msg": "角色编码已存在"}
|
||
rid = _uuid()
|
||
db.execute("INSERT INTO sys_role (id, role_code, role_name, remark, created_by, created_at, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s)", (rid, role_code, role_name, req.get("remark",""), req.get("operator_id",""), _now(), _now()))
|
||
return {"code": 0, "data": {"id": rid}}
|
||
|
||
def role_update(req):
|
||
role_id = req.get("id") or req.get("role_id")
|
||
if not role_id: return {"code": 1, "msg": "缺少角色 id"}
|
||
db = _get_db()
|
||
db.execute("UPDATE sys_role SET role_name=%s, remark=%s, updated_by=%s, updated_at=%s WHERE id=%s", (req.get("role_name",""), req.get("remark",""), req.get("operator_id",""), _now(), role_id))
|
||
return {"code": 0}
|
||
|
||
def role_delete(req):
|
||
role_id = req.get("id") or req.get("role_id")
|
||
if not role_id: return {"code": 1, "msg": "缺少角色 id"}
|
||
db = _get_db()
|
||
role = db.query_one("SELECT role_code FROM sys_role WHERE id = %s", (role_id,))
|
||
if not role: return {"code": 1, "msg": "角色不存在"}
|
||
if role["role_code"] in BUILTIN_ROLES or role["role_code"] == ROLE_ADMIN:
|
||
return {"code": 1, "msg": "内置角色禁止删除"}
|
||
db.execute("DELETE FROM sys_role_permission WHERE role_id = %s", (role_id,))
|
||
db.execute("DELETE FROM sys_user_role WHERE role_id = %s", (role_id,))
|
||
db.execute("DELETE FROM sys_role WHERE id = %s", (role_id,))
|
||
return {"code": 0}
|
||
|
||
def role_assign_perms(req):
|
||
role_id = req.get("role_id") or req.get("id")
|
||
if not role_id: return {"code": 1, "msg": "缺少角色 id"}
|
||
perm_ids = req.get("perm_ids") or []
|
||
db = _get_db()
|
||
if not db.query_one("SELECT id FROM sys_role WHERE id = %s", (role_id,)):
|
||
return {"code": 1, "msg": "角色不存在"}
|
||
db.execute("DELETE FROM sys_role_permission WHERE role_id = %s", (role_id,))
|
||
if perm_ids:
|
||
db.executemany("INSERT INTO sys_role_permission (role_id, perm_id) VALUES (%s, %s)", [(role_id, p) for p in perm_ids])
|
||
return {"code": 0, "data": {"count": len(perm_ids)}}
|
||
|
||
# ---- 管理员 ----
|
||
def admin_list(req):
|
||
db = _get_db()
|
||
rows = db.query("SELECT u.id, u.username, u.real_name, u.status, u.created_at, (SELECT GROUP_CONCAT(r.role_code) FROM sys_user_role ur JOIN sys_role r ON r.id=ur.role_id WHERE ur.user_id=u.id) AS role_codes FROM sys_user u ORDER BY u.created_at")
|
||
return {"code": 0, "data": rows}
|
||
|
||
def admin_create(req):
|
||
db = _get_db()
|
||
username = (req.get("username") or "").strip()
|
||
if not username: return {"code": 1, "msg": "用户名必填"}
|
||
if db.query_one("SELECT id FROM sys_user WHERE username = %s", (username,)):
|
||
return {"code": 1, "msg": "用户名已存在"}
|
||
uid = _uuid()
|
||
db.execute("INSERT INTO sys_user (id, username, real_name, password, status, created_at, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s)", (uid, username, req.get("real_name",""), req.get("password",""), req.get("status",1), _now(), _now()))
|
||
role_ids = req.get("role_ids") or []
|
||
if role_ids:
|
||
db.executemany("INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)", [(uid, r) for r in role_ids])
|
||
return {"code": 0, "data": {"id": uid}}
|
||
|
||
def admin_bind_roles(req):
|
||
user_id = req.get("user_id") or req.get("id")
|
||
if not user_id: return {"code": 1, "msg": "缺少用户 id"}
|
||
role_ids = req.get("role_ids") or []
|
||
db = _get_db()
|
||
db.execute("DELETE FROM sys_user_role WHERE user_id = %s", (user_id,))
|
||
if role_ids:
|
||
db.executemany("INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)", [(user_id, r) for r in role_ids])
|
||
return {"code": 0}
|
||
|
||
def admin_disable(req):
|
||
user_id = req.get("user_id") or req.get("id")
|
||
if not user_id: return {"code": 1, "msg": "缺少用户 id"}
|
||
db = _get_db()
|
||
db.execute("UPDATE sys_user SET status=%s, updated_at=%s WHERE id=%s", (req.get("status",0), _now(), user_id))
|
||
return {"code": 0}
|
||
|
||
# ---- 数据范围 ----
|
||
def _json_loads(raw, default):
|
||
if not raw: return default
|
||
try: return json.loads(raw)
|
||
except (TypeError, ValueError): return default
|
||
|
||
def scope_list(req):
|
||
db = _get_db()
|
||
rows = db.query("SELECT s.*, u.username, u.real_name FROM sys_data_scope s LEFT JOIN sys_user u ON u.id = s.admin_user_id ORDER BY s.created_at")
|
||
for r in rows:
|
||
r["org_ids"] = _json_loads(r.get("org_ids_json"), [])
|
||
r["field_ids"] = _json_loads(r.get("field_ids_json"), [])
|
||
return {"code": 0, "data": rows}
|
||
|
||
def scope_save(req):
|
||
db = _get_db()
|
||
admin_user_id = req.get("admin_user_id") or req.get("user_id")
|
||
if not admin_user_id: return {"code": 1, "msg": "缺少管理员用户"}
|
||
scope_type = req.get("scope_type") or SCOPE_TYPE_ORG
|
||
org_json = json.dumps(req.get("org_ids") or [], ensure_ascii=False)
|
||
field_json = json.dumps(req.get("field_ids") or [], ensure_ascii=False)
|
||
exists = db.query_one("SELECT id FROM sys_data_scope WHERE admin_user_id = %s", (admin_user_id,))
|
||
if exists:
|
||
db.execute("UPDATE sys_data_scope SET scope_type=%s, org_ids_json=%s, field_ids_json=%s, remark=%s, updated_by=%s, updated_at=%s WHERE id=%s", (scope_type, org_json, field_json, req.get("remark",""), req.get("operator_id",""), _now(), exists["id"]))
|
||
return {"code": 0, "data": {"id": exists["id"]}}
|
||
sid = _uuid()
|
||
db.execute("INSERT INTO sys_data_scope (id, admin_user_id, scope_type, org_ids_json, field_ids_json, remark, created_by, updated_by, created_at, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", (sid, admin_user_id, scope_type, org_json, field_json, req.get("remark",""), req.get("operator_id",""), req.get("operator_id",""), _now(), _now()))
|
||
return {"code": 0, "data": {"id": sid}}
|
||
|
||
def scope_delete(req):
|
||
scope_id = req.get("id")
|
||
if not scope_id: return {"code": 1, "msg": "缺少配置 id"}
|
||
db = _get_db()
|
||
db.execute("DELETE FROM sys_data_scope WHERE id = %s", (scope_id,))
|
||
return {"code": 0}
|
||
|
||
# ---- 叠加机制 ----
|
||
def _expand_org_tree(db, org_ids):
|
||
if not org_ids: return []
|
||
result = set(org_ids); frontier = list(org_ids); seen = set(org_ids)
|
||
while frontier:
|
||
ph = ",".join(["%s"] * len(frontier))
|
||
children = db.query("SELECT id FROM org_unit WHERE parent_id IN (%s)" % ph, frontier)
|
||
frontier = []
|
||
for c in children:
|
||
if c["id"] not in seen:
|
||
seen.add(c["id"]); result.add(c["id"]); frontier.append(c["id"])
|
||
return sorted(result)
|
||
|
||
def get_data_scope(user_id):
|
||
db = _get_db()
|
||
roles = db.query("SELECT r.role_code FROM sys_user_role ur JOIN sys_role r ON r.id = ur.role_id WHERE ur.user_id = %s", (user_id,))
|
||
role_codes = [r["role_code"] for r in roles]
|
||
result = {"org_all": False, "org_ids": [], "field_all": False, "field_ids": [], "role_codes": role_codes}
|
||
if ROLE_SYS_ADMIN in role_codes or ROLE_ADMIN in role_codes:
|
||
result["org_all"] = True; result["field_all"] = True
|
||
return result
|
||
scope_rows = db.query("SELECT scope_type, org_ids_json, field_ids_json FROM sys_data_scope WHERE admin_user_id = %s", (user_id,))
|
||
if not scope_rows: return result
|
||
org_union = set(); field_union = set(); field_restricted = False
|
||
for row in scope_rows:
|
||
st = row.get("scope_type") or ""
|
||
oids = _json_loads(row.get("org_ids_json"), [])
|
||
fids = _json_loads(row.get("field_ids_json"), [])
|
||
if st in (SCOPE_TYPE_ORG, "all") and oids: org_union.update(oids)
|
||
if st in (SCOPE_TYPE_FIELD, "all"):
|
||
field_restricted = True; field_union.update(fids)
|
||
result["org_ids"] = _expand_org_tree(db, sorted(org_union))
|
||
if field_restricted: result["field_ids"] = sorted(field_union)
|
||
else: result["field_all"] = True
|
||
return result
|
||
|
||
def scope_query(req):
|
||
user_id = req.get("user_id") or req.get("operator_id") or req.get("uid")
|
||
if not user_id: return {"code": 1, "msg": "缺少 user_id"}
|
||
return {"code": 0, "data": get_data_scope(user_id)}
|
||
|
||
# ---- 初始化 ----
|
||
def init_permission(db=None):
|
||
db = db or _get_db()
|
||
now = _now()
|
||
counts = {"roles": 0, "users": 0, "perms": 0}
|
||
roles = [(ROLE_EMPLOYEE,"员工自助","员工本人查看/修改个人档案、发起自助申请"),(ROLE_MANAGER,"经理","管理本部门员工、发起并审批本部门人事异动"),(ROLE_HR_ADMIN,"HR管理员","管理全公司人事业务,受数据范围约束"),(ROLE_SYS_ADMIN,"系统管理员","系统配置、角色/管理员/数据范围/审计管理")]
|
||
role_id_map = {}
|
||
for code, name, remark in roles:
|
||
row = db.query_one("SELECT id FROM sys_role WHERE role_code = %s", (code,))
|
||
if not row:
|
||
rid = _uuid()
|
||
db.execute("INSERT INTO sys_role (id, role_code, role_name, remark, created_by, created_at, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s)", (rid, code, name, remark, "system", now, now))
|
||
counts["roles"] += 1
|
||
else: rid = row["id"]
|
||
role_id_map[code] = rid
|
||
admin_row = db.query_one("SELECT id FROM sys_user WHERE username = 'admin'")
|
||
if not admin_row:
|
||
uid = _uuid()
|
||
db.execute("INSERT INTO sys_user (id, username, real_name, password, status, created_at, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s)", (uid, "admin", "系统管理员", "", 1, now, now))
|
||
counts["users"] += 1
|
||
else: uid = admin_row["id"]
|
||
for code, rid in role_id_map.items():
|
||
if not db.query_one("SELECT 1 AS x FROM sys_user_role WHERE user_id = %s AND role_id = %s", (uid, rid)):
|
||
db.execute("INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)", (uid, rid))
|
||
base_perms = [("hr_system:role_admin","角色权限配置","/hr_system/role_admin"),("hr_system:data_scope","数据范围配置","/hr_system/data_scope"),("hr_system:audit_log","操作日志查询","/hr_system/audit_log")]
|
||
perm_id_map = {}
|
||
for code, name, path in base_perms:
|
||
row = db.query_one("SELECT id FROM sys_permission WHERE perm_code = %s", (code,))
|
||
if not row:
|
||
pid = _uuid()
|
||
db.execute("INSERT INTO sys_permission (id, perm_code, perm_name, perm_path, created_at) VALUES (%s,%s,%s,%s,%s)", (pid, code, name, path, now))
|
||
counts["perms"] += 1
|
||
else: pid = row["id"]
|
||
perm_id_map[code] = pid
|
||
for pid in perm_id_map.values():
|
||
if not db.query_one("SELECT 1 AS x FROM sys_role_permission WHERE role_id = %s AND perm_id = %s", (role_id_map[ROLE_SYS_ADMIN], pid)):
|
||
db.execute("INSERT INTO sys_role_permission (role_id, perm_id) VALUES (%s, %s)", (role_id_map[ROLE_SYS_ADMIN], pid))
|
||
return counts
|
||
|
||
ENDPOINTS = {"role_list":role_list,"role_detail":role_detail,"role_create":role_create,"role_update":role_update,"role_delete":role_delete,"role_assign_perms":role_assign_perms,"admin_list":admin_list,"admin_create":admin_create,"admin_bind_roles":admin_bind_roles,"admin_disable":admin_disable,"scope_list":scope_list,"scope_save":scope_save,"scope_delete":scope_delete,"scope_query":scope_query}
|
||
|
||
def register(server_env):
|
||
server_env.register_public("get_data_scope", get_data_scope)
|
||
server_env.register_api(ENDPOINTS)
|
||
log.info("hr-system 权限底座已注册:get_data_scope + %d 个端点", len(ENDPOINTS))
|