# -*- 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))