mirror of
https://github.com/veops/cmdb.git
synced 2025-08-08 14:50:56 +08:00
前后端全面升级
This commit is contained in:
435
cmdb-api/api/lib/common_setting/department.py
Normal file
435
cmdb-api/api/lib/common_setting/department.py
Normal file
@@ -0,0 +1,435 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import abort
|
||||
from treelib import Tree
|
||||
from wtforms import Form
|
||||
from wtforms import IntegerField
|
||||
from wtforms import StringField
|
||||
from wtforms import validators
|
||||
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
from api.lib.common_setting.utils import get_df_from_read_sql
|
||||
from api.lib.perm.acl.role import RoleCRUD
|
||||
from api.models.common_setting import Department, Employee
|
||||
|
||||
sub_departments_column_name = 'sub_departments'
|
||||
|
||||
|
||||
def drop_ts_column(df):
|
||||
columns = list(df.columns)
|
||||
remove_columns = []
|
||||
for column in ['created_at', 'updated_at', 'deleted_at', 'last_login']:
|
||||
targets = list(filter(lambda c: c.startswith(column), columns))
|
||||
if targets:
|
||||
remove_columns.extend(targets)
|
||||
|
||||
remove_columns = list(set(remove_columns))
|
||||
|
||||
return df.drop(remove_columns, axis=1) if len(remove_columns) > 0 else df
|
||||
|
||||
|
||||
def get_department_df():
|
||||
criterion = [
|
||||
Department.deleted == 0,
|
||||
]
|
||||
query = Department.query.filter(
|
||||
*criterion
|
||||
)
|
||||
df = get_df_from_read_sql(query)
|
||||
if df.empty:
|
||||
return
|
||||
return drop_ts_column(df)
|
||||
|
||||
|
||||
def get_all_employee_df(block=0):
|
||||
criterion = [
|
||||
Employee.deleted == 0,
|
||||
]
|
||||
if block >= 0:
|
||||
criterion.append(
|
||||
Employee.block == block
|
||||
)
|
||||
|
||||
entities = [getattr(Employee, c) for c in Employee.get_columns(
|
||||
).keys() if c not in ['deleted', 'deleted_at']]
|
||||
query = Employee.query.with_entities(
|
||||
*entities
|
||||
).filter(
|
||||
*criterion
|
||||
)
|
||||
df = get_df_from_read_sql(query)
|
||||
if df.empty:
|
||||
return df
|
||||
return drop_ts_column(df)
|
||||
|
||||
|
||||
class DepartmentTree(object):
|
||||
def __init__(self, append_employee=False, block=-1):
|
||||
self.append_employee = append_employee
|
||||
self.block = block
|
||||
self.d_df = get_department_df()
|
||||
self.employee_df = get_all_employee_df(
|
||||
block) if append_employee else None
|
||||
|
||||
def prepare(self):
|
||||
pass
|
||||
|
||||
def get_employees_by_d_id(self, d_id):
|
||||
_df = self.employee_df[
|
||||
self.employee_df['department_id'].eq(d_id)
|
||||
].sort_values(by=['direct_supervisor_id'], ascending=True)
|
||||
if _df.empty:
|
||||
return []
|
||||
|
||||
if self.block != -1:
|
||||
_df = _df[
|
||||
_df['block'].eq(self.block)
|
||||
]
|
||||
|
||||
return _df.to_dict('records')
|
||||
|
||||
def get_tree_departments(self):
|
||||
# 一级部门
|
||||
top_df = self.d_df[self.d_df['department_parent_id'].eq(-1)]
|
||||
if top_df.empty:
|
||||
return []
|
||||
|
||||
d_list = []
|
||||
|
||||
for index in top_df.index:
|
||||
top_d = top_df.loc[index].to_dict()
|
||||
|
||||
department_id = top_d['department_id']
|
||||
|
||||
# 检查 department_id 是否作为其他部门的 parent
|
||||
sub_df = self.d_df[
|
||||
self.d_df['department_parent_id'].eq(department_id)
|
||||
].sort_values(by=['sort_value'], ascending=True)
|
||||
|
||||
employees = []
|
||||
|
||||
if self.append_employee:
|
||||
# 要包含员工
|
||||
employees = self.get_employees_by_d_id(department_id)
|
||||
|
||||
top_d['employees'] = employees
|
||||
|
||||
if sub_df.empty:
|
||||
top_d[sub_departments_column_name] = []
|
||||
d_list.append(top_d)
|
||||
continue
|
||||
|
||||
self.parse_sub_department(sub_df, top_d)
|
||||
d_list.append(top_d)
|
||||
|
||||
return d_list
|
||||
|
||||
def get_all_departments(self, is_tree=1):
|
||||
if self.d_df.empty:
|
||||
return []
|
||||
|
||||
if is_tree != 1:
|
||||
return self.d_df.to_dict('records')
|
||||
|
||||
return self.get_tree_departments()
|
||||
|
||||
def parse_sub_department(self, df, top_d):
|
||||
sub_departments = []
|
||||
for s_index in df.index:
|
||||
d = df.loc[s_index].to_dict()
|
||||
sub_df = self.d_df[
|
||||
self.d_df['department_parent_id'].eq(
|
||||
df.at[s_index, 'department_id'])
|
||||
].sort_values(by=['sort_value'], ascending=True)
|
||||
employees = []
|
||||
|
||||
if self.append_employee:
|
||||
# 要包含员工
|
||||
employees = self.get_employees_by_d_id(
|
||||
df.at[s_index, 'department_id'])
|
||||
|
||||
d['employees'] = employees
|
||||
|
||||
if sub_df.empty:
|
||||
d[sub_departments_column_name] = []
|
||||
sub_departments.append(d)
|
||||
continue
|
||||
|
||||
self.parse_sub_department(sub_df, d)
|
||||
sub_departments.append(d)
|
||||
|
||||
top_d[sub_departments_column_name] = sub_departments
|
||||
|
||||
|
||||
class DepartmentForm(Form):
|
||||
department_name = StringField(validators=[
|
||||
validators.DataRequired(message="部门名称不能为空"),
|
||||
validators.Length(max=255),
|
||||
])
|
||||
|
||||
department_director_id = IntegerField(validators=[], default=0)
|
||||
department_parent_id = IntegerField(validators=[], default=1)
|
||||
|
||||
|
||||
class DepartmentCRUD(object):
|
||||
|
||||
@staticmethod
|
||||
def add(**kwargs):
|
||||
DepartmentCRUD.check_department_name_unique(kwargs['department_name'])
|
||||
department_parent_id = kwargs.get('department_parent_id', 0)
|
||||
DepartmentCRUD.check_department_parent_id(department_parent_id)
|
||||
|
||||
DepartmentCRUD.check_department_parent_id_allow(
|
||||
-1, department_parent_id)
|
||||
|
||||
try:
|
||||
role = RoleCRUD.add_role(name=kwargs['department_name'])
|
||||
except Exception as e:
|
||||
return abort(400, ErrFormat.acl_add_role_failed.format(str(e)))
|
||||
|
||||
kwargs['acl_rid'] = role.id
|
||||
try:
|
||||
db_department = Department.create(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return abort(400, str(e))
|
||||
|
||||
return db_department
|
||||
|
||||
@staticmethod
|
||||
def check_department_parent_id_allow(d_id, department_parent_id):
|
||||
if department_parent_id == 0:
|
||||
return
|
||||
# 检查 department_parent_id 是否在许可范围内
|
||||
allow_p_d_id_list = DepartmentCRUD.get_allow_parent_d_id_by(d_id)
|
||||
target = list(
|
||||
filter(lambda d: d['department_id'] == department_parent_id, allow_p_d_id_list))
|
||||
if len(target) == 0:
|
||||
try:
|
||||
d = Department.get_by(
|
||||
first=True, to_dict=False, department_id=department_parent_id)
|
||||
name = d.department_name if d else ErrFormat.department_id_not_found.format(department_parent_id)
|
||||
except Exception as e:
|
||||
name = ErrFormat.department_id_not_found.format(department_parent_id)
|
||||
abort(400, ErrFormat.cannot_to_be_parent_department.format(name))
|
||||
|
||||
@staticmethod
|
||||
def check_department_parent_id(department_parent_id):
|
||||
if int(department_parent_id) < 0:
|
||||
abort(400, ErrFormat.parent_department_id_must_more_than_zero)
|
||||
|
||||
@staticmethod
|
||||
def check_department_name_unique(name, _id=0):
|
||||
criterion = [
|
||||
Department.department_name == name,
|
||||
Department.deleted == 0,
|
||||
]
|
||||
if _id > 0:
|
||||
criterion.append(
|
||||
Department.department_id != _id
|
||||
)
|
||||
|
||||
res = Department.query.filter(
|
||||
*criterion
|
||||
).all()
|
||||
|
||||
res and abort(
|
||||
400, ErrFormat.department_name_already_exists.format(name)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def edit(_id, **kwargs):
|
||||
DepartmentCRUD.check_department_name_unique(
|
||||
kwargs['department_name'], _id)
|
||||
kwargs.pop('department_id', None)
|
||||
existed = Department.get_by(
|
||||
first=True, department_id=_id, to_dict=False)
|
||||
if not existed:
|
||||
abort(404, ErrFormat.department_id_not_found.format(_id))
|
||||
|
||||
department_parent_id = kwargs.get('department_parent_id', 0)
|
||||
DepartmentCRUD.check_department_parent_id(department_parent_id)
|
||||
if department_parent_id > 0:
|
||||
DepartmentCRUD.check_department_parent_id_allow(
|
||||
_id, department_parent_id)
|
||||
|
||||
try:
|
||||
RoleCRUD.update_role(
|
||||
existed.acl_rid, name=kwargs['department_name'])
|
||||
except Exception as e:
|
||||
return abort(400, ErrFormat.acl_update_role_failed.format(str(e)))
|
||||
|
||||
try:
|
||||
existed.update(**kwargs)
|
||||
except Exception as e:
|
||||
return abort(400, str(e))
|
||||
|
||||
@staticmethod
|
||||
def delete(_id):
|
||||
existed = Department.get_by(
|
||||
first=True, department_id=_id, to_dict=False)
|
||||
if not existed:
|
||||
abort(404, ErrFormat.department_id_not_found.format(_id))
|
||||
try:
|
||||
RoleCRUD.delete_role(existed.acl_rid)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
return existed.soft_delete()
|
||||
|
||||
@staticmethod
|
||||
def get_allow_parent_d_id_by(department_id):
|
||||
"""
|
||||
获取可以成为 department_id 的 department_parent_id 的 list
|
||||
"""
|
||||
tree_list = DepartmentCRUD.get_department_tree_list()
|
||||
|
||||
allow_d_id_list = []
|
||||
|
||||
for tree in tree_list:
|
||||
if department_id > 0:
|
||||
try:
|
||||
tree.remove_subtree(department_id)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
[allow_d_id_list.append({'department_id': int(n.identifier), 'department_name': n.tag}) for n in
|
||||
tree.all_nodes()]
|
||||
|
||||
return allow_d_id_list
|
||||
|
||||
@staticmethod
|
||||
def update_department_sort(department_list):
|
||||
d_map = {d['id']: d['sort_value'] for d in department_list}
|
||||
d_id = [d['id'] for d in department_list]
|
||||
|
||||
db_list = Department.query.filter(
|
||||
Department.department_id.in_(d_id),
|
||||
Department.deleted == 0
|
||||
).all()
|
||||
|
||||
for existed in db_list:
|
||||
existed.update(sort_value=d_map[existed.department_id])
|
||||
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_all_departments_with_employee(block):
|
||||
return DepartmentTree(True, block).get_all_departments(1)
|
||||
|
||||
@staticmethod
|
||||
def get_department_tree_list():
|
||||
df = get_department_df()
|
||||
if df.empty:
|
||||
return []
|
||||
|
||||
# 一级部门
|
||||
top_df = df[df['department_parent_id'].eq(-1)]
|
||||
if top_df.empty:
|
||||
return []
|
||||
|
||||
tree_list = []
|
||||
|
||||
for index in top_df.index:
|
||||
tree = Tree()
|
||||
identifier_root = top_df.at[index, 'department_id']
|
||||
tree.create_node(
|
||||
top_df.at[index, 'department_name'],
|
||||
identifier_root
|
||||
)
|
||||
|
||||
# 检查 department_id 是否作为其他部门的 parent
|
||||
sub_df = df[
|
||||
df['department_parent_id'].eq(identifier_root)
|
||||
]
|
||||
if sub_df.empty:
|
||||
tree_list.append(tree)
|
||||
continue
|
||||
|
||||
DepartmentCRUD.parse_sub_department_node(
|
||||
sub_df, df, tree, identifier_root)
|
||||
|
||||
tree_list.append(tree)
|
||||
|
||||
return tree_list
|
||||
|
||||
@staticmethod
|
||||
def parse_sub_department_node(df, all_df, tree, parent_id):
|
||||
for s_index in df.index:
|
||||
tree.create_node(
|
||||
df.at[s_index, 'department_name'],
|
||||
df.at[s_index, 'department_id'],
|
||||
parent=parent_id
|
||||
)
|
||||
|
||||
sub_df = all_df[
|
||||
all_df['department_parent_id'].eq(
|
||||
df.at[s_index, 'department_id'])
|
||||
]
|
||||
if sub_df.empty:
|
||||
continue
|
||||
|
||||
DepartmentCRUD.parse_sub_department_node(
|
||||
sub_df, all_df, tree, df.at[s_index, 'department_id'])
|
||||
|
||||
@staticmethod
|
||||
def get_departments_and_ids(department_parent_id, block):
|
||||
query = Department.query.filter(
|
||||
Department.department_parent_id == department_parent_id,
|
||||
Department.deleted == 0,
|
||||
).order_by(Department.sort_value.asc())
|
||||
df = get_df_from_read_sql(query)
|
||||
if df.empty:
|
||||
return [], []
|
||||
|
||||
tree_list = DepartmentCRUD.get_department_tree_list()
|
||||
employee_df = get_all_employee_df(block)
|
||||
|
||||
department_id_list = list(df['department_id'].values)
|
||||
query = Department.query.filter(
|
||||
Department.department_parent_id.in_(department_id_list),
|
||||
Department.deleted == 0,
|
||||
).order_by(Department.sort_value.asc()).group_by(Department.department_id)
|
||||
sub_df = get_df_from_read_sql(query)
|
||||
if sub_df.empty:
|
||||
df['has_sub'] = 0
|
||||
|
||||
def handle_row_employee_count(row):
|
||||
return len(employee_df[employee_df['department_id'] == row['department_id']])
|
||||
|
||||
df['employee_count'] = df.apply(
|
||||
lambda row: handle_row_employee_count(row), axis=1)
|
||||
|
||||
else:
|
||||
sub_map = {d['department_parent_id']: 1 for d in sub_df.to_dict('records')}
|
||||
|
||||
def handle_row(row):
|
||||
d_ids = DepartmentCRUD.get_department_id_list_by_root(
|
||||
row['department_id'], tree_list)
|
||||
row['employee_count'] = len(
|
||||
employee_df[employee_df['department_id'].isin(d_ids)])
|
||||
|
||||
row['has_sub'] = sub_map.get(row['department_id'], 0)
|
||||
|
||||
return row
|
||||
|
||||
df = df.apply(lambda row: handle_row(row), axis=1)
|
||||
|
||||
return df.to_dict('records'), department_id_list
|
||||
|
||||
@staticmethod
|
||||
def get_department_id_list_by_root(root_department_id, tree_list=None):
|
||||
if tree_list is None:
|
||||
tree_list = DepartmentCRUD.get_department_tree_list()
|
||||
id_list = []
|
||||
for tree in tree_list:
|
||||
try:
|
||||
tmp_tree = tree.subtree(root_department_id)
|
||||
[id_list.append(int(n.identifier))
|
||||
for n in tmp_tree.all_nodes()]
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
return id_list
|
Reference in New Issue
Block a user