Merge branch 'master' of github.com:veops/cmdb into dev_ui

This commit is contained in:
wang-liang0615 2023-08-10 19:21:28 +08:00
commit 7272880062
18 changed files with 109 additions and 480 deletions

View File

@ -25,7 +25,7 @@ api:
cd cmdb-api && pipenv run flask run -h 0.0.0.0 cd cmdb-api && pipenv run flask run -h 0.0.0.0
worker: worker:
cd cmdb-api && pipenv run celery worker -A celery_worker.celery -E -Q one_cmdb_async --concurrency=1 -D && pipenv run celery worker -A celery_worker.celery -E -Q acl_async --concurrency=1 -D cd cmdb-api && pipenv run celery -A celery_worker.celery worker -E -Q one_cmdb_async --concurrency=1 -D && pipenv run celery -A celery_worker.celery worker -E -Q acl_async --concurrency=1 -D
ui: ui:
cd cmdb-ui && yarn run serve cd cmdb-ui && yarn run serve

View File

@ -14,7 +14,7 @@ Flask-RESTful = "==0.3.10"
Flask-SQLAlchemy = "==2.5.0" Flask-SQLAlchemy = "==2.5.0"
SQLAlchemy = "==1.4.49" SQLAlchemy = "==1.4.49"
PyMySQL = "==1.1.0" PyMySQL = "==1.1.0"
redis = "==3.2.1" redis = "==4.6.0"
# Migrations # Migrations
Flask-Migrate = "==2.5.2" Flask-Migrate = "==2.5.2"
# Deployment # Deployment
@ -32,10 +32,10 @@ Flask-Caching = ">=1.0.0"
environs = "==4.2.0" environs = "==4.2.0"
marshmallow = "==2.20.2" marshmallow = "==2.20.2"
# async tasks # async tasks
celery = "==4.3.0" celery = "==5.3.1"
celery_once = "==3.0.1" celery_once = "==3.0.1"
more-itertools = "==5.0.0" more-itertools = "==5.0.0"
kombu = "==4.4.0" kombu = "==5.3.1"
# common setting # common setting
timeout-decorator = "==0.5.0" timeout-decorator = "==0.5.0"
WTForms = "==3.0.0" WTForms = "==3.0.0"
@ -50,7 +50,7 @@ toposort = ">=1.5"
requests = ">=2.22.0" requests = ">=2.22.0"
PyJWT = "==2.4.0" PyJWT = "==2.4.0"
elasticsearch = "==7.17.9" elasticsearch = "==7.17.9"
future = "==0.18.2" future = "==0.18.3"
itsdangerous = "==2.1.2" itsdangerous = "==2.1.2"
Jinja2 = "==3.1.2" Jinja2 = "==3.1.2"
jinja2schema = "==0.1.4" jinja2schema = "==0.1.4"

View File

@ -129,6 +129,8 @@ def register_extensions(app):
rd.init_app(app) rd.init_app(app)
if app.config.get('USE_ES'): if app.config.get('USE_ES'):
es.init_app(app) es.init_app(app)
app.config.update(app.config.get("CELERY"))
celery.conf.update(app.config) celery.conf.update(app.config)

View File

@ -4,11 +4,11 @@ COMMON_SETTING_QUEUE = "common_setting_async"
class OperatorType(BaseEnum): class OperatorType(BaseEnum):
EQUAL = 1 # 等于 EQUAL = 1
NOT_EQUAL = 2 # 不等于 NOT_EQUAL = 2
IN = 3 # 包含 IN = 3
NOT_IN = 4 # 不包含 NOT_IN = 4
GREATER_THAN = 5 # 大于 GREATER_THAN = 5
LESS_THAN = 6 # 小于 LESS_THAN = 6
IS_EMPTY = 7 # 为空 IS_EMPTY = 7
IS_NOT_EMPTY = 8 # 不为空 IS_NOT_EMPTY = 8

View File

@ -7,6 +7,7 @@ from wtforms import IntegerField
from wtforms import StringField from wtforms import StringField
from wtforms import validators from wtforms import validators
from api.extensions import db
from api.lib.common_setting.resp_format import ErrFormat from api.lib.common_setting.resp_format import ErrFormat
from api.lib.perm.acl.role import RoleCRUD from api.lib.perm.acl.role import RoleCRUD
from api.models.common_setting import Department, Employee from api.models.common_setting import Department, Employee
@ -44,7 +45,6 @@ def get_all_employee_list(block=0, to_dict=True):
'email', 'email',
'mobile', 'mobile',
'direct_supervisor_id', 'direct_supervisor_id',
'annual_leave',
'block', 'block',
'department_id', 'department_id',
] ]
@ -94,11 +94,9 @@ class DepartmentTree(object):
for top_d in top_departments: for top_d in top_departments:
department_id = top_d['department_id'] department_id = top_d['department_id']
# 检查 department_id 是否作为其他部门的 parent
sub_deps = self.get_department_by_parent_id(department_id) sub_deps = self.get_department_by_parent_id(department_id)
employees = [] employees = []
if self.append_employee: if self.append_employee:
# 要包含员工
employees = self.get_employees_by_d_id(department_id) employees = self.get_employees_by_d_id(department_id)
top_d['employees'] = employees top_d['employees'] = employees
@ -127,7 +125,6 @@ class DepartmentTree(object):
sub_deps = self.get_department_by_parent_id(d['department_id']) sub_deps = self.get_department_by_parent_id(d['department_id'])
employees = [] employees = []
if self.append_employee: if self.append_employee:
# 要包含员工
employees = self.get_employees_by_d_id(d['department_id']) employees = self.get_employees_by_d_id(d['department_id'])
d['employees'] = employees d['employees'] = employees
@ -184,7 +181,6 @@ class DepartmentCRUD(object):
def check_department_parent_id_allow(d_id, department_parent_id): def check_department_parent_id_allow(d_id, department_parent_id):
if department_parent_id == 0: if department_parent_id == 0:
return return
# 检查 department_parent_id 是否在许可范围内
allow_p_d_id_list = DepartmentCRUD.get_allow_parent_d_id_by(d_id) allow_p_d_id_list = DepartmentCRUD.get_allow_parent_d_id_by(d_id)
target = list( target = list(
filter(lambda d: d['department_id'] == department_parent_id, allow_p_d_id_list)) filter(lambda d: d['department_id'] == department_parent_id, allow_p_d_id_list))
@ -263,9 +259,6 @@ class DepartmentCRUD(object):
@staticmethod @staticmethod
def get_allow_parent_d_id_by(department_id): def get_allow_parent_d_id_by(department_id):
"""
获取可以成为 department_id department_parent_id list
"""
tree_list = DepartmentCRUD.get_department_tree_list() tree_list = DepartmentCRUD.get_department_tree_list()
allow_d_id_list = [] allow_d_id_list = []
@ -307,7 +300,6 @@ class DepartmentCRUD(object):
if len(all_deps) == 0: if len(all_deps) == 0:
return [] return []
# 一级部门
top_deps = list(filter(lambda d: d['department_parent_id'] == -1, all_deps)) top_deps = list(filter(lambda d: d['department_parent_id'] == -1, all_deps))
if len(top_deps) == 0: if len(top_deps) == 0:
return [] return []
@ -321,7 +313,6 @@ class DepartmentCRUD(object):
top_d['department_name'], top_d['department_name'],
identifier_root identifier_root
) )
# 检查 department_id 是否作为其他部门的 parent
sub_ds = list(filter(lambda d: d['department_parent_id'] == identifier_root, all_deps)) sub_ds = list(filter(lambda d: d['department_parent_id'] == identifier_root, all_deps))
if len(sub_ds) == 0: if len(sub_ds) == 0:
tree_list.append(tree) tree_list.append(tree)
@ -350,6 +341,13 @@ class DepartmentCRUD(object):
DepartmentCRUD.parse_sub_department_node( DepartmentCRUD.parse_sub_department_node(
next_sub_ds, all_ds, tree, d['department_id']) next_sub_ds, all_ds, tree, d['department_id'])
@staticmethod
def get_department_by_query(query, to_dict=True):
results = query.all()
if not results:
return []
return results if not to_dict else [r.to_dict() for r in results]
@staticmethod @staticmethod
def get_departments_and_ids(department_parent_id, block): def get_departments_and_ids(department_parent_id, block):
query = Department.query.filter( query = Department.query.filter(

View File

@ -18,6 +18,18 @@ from api.lib.common_setting.const import COMMON_SETTING_QUEUE, OperatorType
from api.lib.common_setting.resp_format import ErrFormat from api.lib.common_setting.resp_format import ErrFormat
from api.models.common_setting import Employee, Department from api.models.common_setting import Employee, Department
acl_user_columns = [
'email',
'mobile',
'nickname',
'username',
'password',
'block',
'avatar',
]
employee_pop_columns = ['password']
can_not_edit_columns = ['email']
def edit_acl_user(uid, **kwargs): def edit_acl_user(uid, **kwargs):
user_data = {column: kwargs.get( user_data = {column: kwargs.get(
@ -68,9 +80,6 @@ class EmployeeCRUD(object):
@staticmethod @staticmethod
def get_employee_by_uid_with_create(_uid): def get_employee_by_uid_with_create(_uid):
"""
根据 uid 获取员工信息不存在则创建
"""
try: try:
return EmployeeCRUD.get_employee_by_uid(_uid).to_dict() return EmployeeCRUD.get_employee_by_uid(_uid).to_dict()
except Exception as e: except Exception as e:
@ -100,7 +109,6 @@ class EmployeeCRUD(object):
acl_uid=user_info['uid'], acl_uid=user_info['uid'],
) )
return existed.to_dict() return existed.to_dict()
# 创建员工
if not user_info.get('nickname', None): if not user_info.get('nickname', None):
user_info['nickname'] = user_info['name'] user_info['nickname'] = user_info['name']
@ -143,9 +151,6 @@ class EmployeeCRUD(object):
if len(e_list) > 0: if len(e_list) > 0:
from api.tasks.common_setting import edit_employee_department_in_acl from api.tasks.common_setting import edit_employee_department_in_acl
# fixme: comment next line
# edit_employee_department_in_acl(e_list, new_department_id, current_user.uid)
edit_employee_department_in_acl.apply_async( edit_employee_department_in_acl.apply_async(
args=(e_list, new_department_id, current_user.uid), args=(e_list, new_department_id, current_user.uid),
queue=COMMON_SETTING_QUEUE queue=COMMON_SETTING_QUEUE
@ -207,163 +212,6 @@ class EmployeeCRUD(object):
*criterion *criterion
).count() ).count()
@staticmethod
def import_employee(employee_list):
return CreateEmployee().batch_create(employee_list)
@staticmethod
def get_export_employee_list(block_status):
if block_status >= 0:
employees = Employee.get_by(block=block_status, to_dict=False)
else:
employees = Employee.get_by(to_dict=False)
keep_cols = EmployeeCRUD.get_current_user_view_columns()
all_departments = Department.get_by(to_dict=False)
d_id_map = {d.department_id: d.department_name for d in all_departments}
e_id_map = {e.employee_id: e.nickname for e in employees}
export_columns_map = {
'username': "用户名",
'nickname': '姓名',
'email': '邮箱',
'department_name': '部门',
'sex': '性别',
'mobile': '手机号',
'position_name': '岗位',
'nickname_direct_supervisor': '直属上级',
'last_login': '上次登录时间',
}
data_list = []
for e in employees:
department_name = d_id_map.get(e.department_id, '')
nickname_direct_supervisor = e_id_map.get(e.direct_supervisor_id, '')
try:
last_login = str(e.last_login) if e.last_login else ''
except:
last_login = ''
data = e.to_dict()
data['last_login'] = last_login
data['department_name'] = department_name
data['nickname_direct_supervisor'] = nickname_direct_supervisor
tmp = {export_columns_map[k]: data[k] for k in export_columns_map.keys() if
k in keep_cols or k in sub_columns}
data_list.append(tmp)
return data_list
@staticmethod
def batch_employee(column_name, column_value, employee_id_list):
if not column_value:
abort(400, ErrFormat.value_is_required)
if column_name in ['password', 'block']:
return EmployeeCRUD.batch_edit_password_or_block_column(column_name, employee_id_list, column_value, True)
elif column_name in ['department_id']:
return EmployeeCRUD.batch_edit_employee_department(employee_id_list, column_value)
elif column_name in [
'direct_supervisor_id', 'position_name'
]:
return EmployeeCRUD.batch_edit_column(column_name, employee_id_list, column_value, False)
else:
abort(400, ErrFormat.column_name_not_support)
@staticmethod
def batch_edit_employee_department(employee_id_list, column_value):
err_list = []
employee_list = []
for _id in employee_id_list:
try:
existed = EmployeeCRUD.get_employee_by_id(_id)
employee = dict(
e_acl_rid=existed.acl_rid,
department_id=existed.department_id
)
employee_list.append(employee)
existed.update(department_id=column_value)
except Exception as e:
err_list.append({
'employee_id': _id,
'err': str(e),
})
from api.tasks.common_setting import edit_employee_department_in_acl
edit_employee_department_in_acl.apply_async(
args=(employee_list, column_value, current_user.uid),
queue=COMMON_SETTING_QUEUE
)
return err_list
@staticmethod
def batch_edit_password_or_block_column(column_name, employee_id_list, column_value, is_acl=False):
if column_name == 'block':
err_list = []
success_list = []
for _id in employee_id_list:
try:
employee = EmployeeCRUD.edit_employee_block_column(
_id, is_acl, **{column_name: column_value})
success_list.append(employee)
except Exception as e:
err_list.append({
'employee_id': _id,
'err': str(e),
})
return err_list
else:
return EmployeeCRUD.batch_edit_column(column_name, employee_id_list, column_value, is_acl)
@staticmethod
def batch_edit_column(column_name, employee_id_list, column_value, is_acl=False):
err_list = []
for _id in employee_id_list:
try:
EmployeeCRUD.edit_employee_single_column(
_id, is_acl, **{column_name: column_value})
except Exception as e:
err_list.append({
'employee_id': _id,
'err': str(e),
})
return err_list
@staticmethod
def edit_employee_single_column(_id, is_acl=False, **kwargs):
existed = EmployeeCRUD.get_employee_by_id(_id)
if is_acl:
return edit_acl_user(existed.acl_uid, **kwargs)
try:
for column in employee_pop_columns:
if kwargs.get(column):
kwargs.pop(column)
return existed.update(**kwargs)
except Exception as e:
return abort(400, str(e))
@staticmethod
def edit_employee_block_column(_id, is_acl=False, **kwargs):
existed = EmployeeCRUD.get_employee_by_id(_id)
value = get_block_value(kwargs.get('block'))
if value is True:
# 判断该用户是否为 部门负责人,或者员工的直接上级
check_department_director_id_or_direct_supervisor_id(_id)
if is_acl:
kwargs['block'] = value
edit_acl_user(existed.acl_uid, **kwargs)
data = existed.to_dict()
return data
@staticmethod @staticmethod
def check_email_unique(email, _id=0): def check_email_unique(email, _id=0):
criterion = [ criterion = [
@ -449,7 +297,7 @@ class EmployeeCRUD(object):
@staticmethod @staticmethod
def get_expr_by_condition(column, operator, value, relation): def get_expr_by_condition(column, operator, value, relation):
""" """
根据conditions返回expr: (and_list, or_list) get expr: (and_list, or_list)
""" """
attr = EmployeeCRUD.get_attr_by_column(column) attr = EmployeeCRUD.get_attr_by_column(column)
# 根据operator生成条件表达式 # 根据operator生成条件表达式
@ -483,7 +331,6 @@ class EmployeeCRUD(object):
else: else:
abort(400, ErrFormat.not_support_operator.format(operator)) abort(400, ErrFormat.not_support_operator.format(operator))
# 根据relation生成复合条件
if relation == "&": if relation == "&":
return expr, [] return expr, []
elif relation == "|": elif relation == "|":
@ -493,7 +340,6 @@ class EmployeeCRUD(object):
@staticmethod @staticmethod
def check_condition(column, operator, value, relation): def check_condition(column, operator, value, relation):
# 对于condition中column为空的报错
if column is None or operator is None or relation is None: if column is None or operator is None or relation is None:
return abort(400, ErrFormat.conditions_field_missing) return abort(400, ErrFormat.conditions_field_missing)
@ -642,19 +488,6 @@ def get_user_map(key='uid', acl=None):
return data return data
acl_user_columns = [
'email',
'mobile',
'nickname',
'username',
'password',
'block',
'avatar',
]
employee_pop_columns = ['password']
can_not_edit_columns = ['email']
def format_params(params): def format_params(params):
for k in ['_key', '_secret']: for k in ['_key', '_secret']:
params.pop(k, None) params.pop(k, None)
@ -664,19 +497,22 @@ def format_params(params):
class CreateEmployee(object): class CreateEmployee(object):
def __init__(self): def __init__(self):
self.acl = ACLManager() self.acl = ACLManager()
self.useremail_map = {} self.all_acl_users = self.acl.get_all_users()
def check_acl_user(self, email): def check_acl_user(self, user_data):
user_info = self.useremail_map.get(email, None) target_email = list(filter(lambda x: x['email'] == user_data['email'], self.all_acl_users))
if user_info: if target_email:
return user_info return target_email[0]
return None
target_username = list(filter(lambda x: x['username'] == user_data['username'], self.all_acl_users))
if target_username:
return target_username[0]
def add_acl_user(self, **kwargs): def add_acl_user(self, **kwargs):
user_data = {column: kwargs.get( user_data = {column: kwargs.get(
column, '') for column in acl_user_columns if kwargs.get(column, '')} column, '') for column in acl_user_columns if kwargs.get(column, '')}
try: try:
existed = self.check_acl_user(user_data['email']) existed = self.check_acl_user(user_data)
if not existed: if not existed:
return self.acl.create_user(user_data) return self.acl.create_user(user_data)
return existed return existed
@ -685,8 +521,6 @@ class CreateEmployee(object):
def create_single(self, **kwargs): def create_single(self, **kwargs):
EmployeeCRUD.check_email_unique(kwargs['email']) EmployeeCRUD.check_email_unique(kwargs['email'])
self.useremail_map = self.useremail_map if self.useremail_map else get_user_map(
'email', self.acl)
user = self.add_acl_user(**kwargs) user = self.add_acl_user(**kwargs)
kwargs['acl_uid'] = user['uid'] kwargs['acl_uid'] = user['uid']
kwargs['last_login'] = user['last_login'] kwargs['last_login'] = user['last_login']
@ -699,8 +533,6 @@ class CreateEmployee(object):
) )
def create_single_with_import(self, **kwargs): def create_single_with_import(self, **kwargs):
self.useremail_map = self.useremail_map if self.useremail_map else get_user_map(
'email', self.acl)
user = self.add_acl_user(**kwargs) user = self.add_acl_user(**kwargs)
kwargs['acl_uid'] = user['uid'] kwargs['acl_uid'] = user['uid']
kwargs['last_login'] = user['last_login'] kwargs['last_login'] = user['last_login']
@ -743,9 +575,6 @@ class CreateEmployee(object):
return end_d_id return end_d_id
def format_department_id(self, employee): def format_department_id(self, employee):
"""
部门名称转化为ID不存在则创建
"""
department_name_map = {} department_name_map = {}
try: try:
department_name = employee.get('department_name', '') department_name = employee.get('department_name', '')
@ -762,16 +591,13 @@ class CreateEmployee(object):
def batch_create(self, employee_list): def batch_create(self, employee_list):
err_list = [] err_list = []
self.useremail_map = get_user_map('email', self.acl)
for employee in employee_list: for employee in employee_list:
try: try:
# 获取username
username = employee.get('username', None) username = employee.get('username', None)
if username is None: if username is None:
employee['username'] = employee['email'] employee['username'] = employee['email']
# 校验通过后获取department_id
employee = self.format_department_id(employee) employee = self.format_department_id(employee)
err = employee.get('err', None) err = employee.get('err', None)
if err: if err:
@ -783,7 +609,7 @@ class CreateEmployee(object):
raise Exception( raise Exception(
','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()])) ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()]))
data = self.create_single_with_import(**form.data) self.create_single_with_import(**form.data)
except Exception as e: except Exception as e:
err_list.append({ err_list.append({
'email': employee.get('email', ''), 'email': employee.get('email', ''),
@ -797,12 +623,12 @@ class CreateEmployee(object):
class EmployeeAddForm(Form): class EmployeeAddForm(Form):
username = StringField(validators=[ username = StringField(validators=[
validators.DataRequired(message="username不能为空"), validators.DataRequired(message=ErrFormat.username_is_required),
validators.Length(max=255), validators.Length(max=255),
]) ])
email = StringField(validators=[ email = StringField(validators=[
validators.DataRequired(message="邮箱不能为空"), validators.DataRequired(message=ErrFormat.email_is_required),
validators.Email(message="邮箱格式不正确"), validators.Email(message=ErrFormat.email_format_error),
validators.Length(max=255), validators.Length(max=255),
]) ])
password = StringField(validators=[ password = StringField(validators=[
@ -811,7 +637,7 @@ class EmployeeAddForm(Form):
position_name = StringField(validators=[]) position_name = StringField(validators=[])
nickname = StringField(validators=[ nickname = StringField(validators=[
validators.DataRequired(message="用户名不能为空"), validators.DataRequired(message=ErrFormat.nickname_is_required),
validators.Length(max=255), validators.Length(max=255),
]) ])
sex = StringField(validators=[]) sex = StringField(validators=[])
@ -822,7 +648,7 @@ class EmployeeAddForm(Form):
class EmployeeUpdateByUidForm(Form): class EmployeeUpdateByUidForm(Form):
nickname = StringField(validators=[ nickname = StringField(validators=[
validators.DataRequired(message="用户名不能为空"), validators.DataRequired(message=ErrFormat.nickname_is_required),
validators.Length(max=255), validators.Length(max=255),
]) ])
avatar = StringField(validators=[]) avatar = StringField(validators=[])

View File

@ -49,3 +49,8 @@ class ErrFormat(CommonErrFormat):
acl_add_user_to_role_failed = "ACL 添加用户到角色失败: {}" acl_add_user_to_role_failed = "ACL 添加用户到角色失败: {}"
acl_import_user_failed = "ACL 导入用户[{}]失败: {}" acl_import_user_failed = "ACL 导入用户[{}]失败: {}"
nickname_is_required = "用户名不能为空"
username_is_required = "username不能为空"
email_is_required = "邮箱不能为空"
email_format_error = "邮箱格式错误"

View File

@ -7,10 +7,8 @@ from functools import wraps
import jwt import jwt
from flask import abort from flask import abort
from flask import current_app from flask import current_app
from flask import g
from flask import request from flask import request
from flask import session from flask import session
from flask_login import current_user
from flask_login import login_user from flask_login import login_user
from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import ACLManager
@ -65,12 +63,10 @@ def _auth_with_key():
def _auth_with_session(): def _auth_with_session():
if isinstance(getattr(g, 'user', None), User):
login_user(current_user)
return True
if "acl" in session and "userName" in (session["acl"] or {}): if "acl" in session and "userName" in (session["acl"] or {}):
login_user(UserCache.get(session["acl"]["userName"])) login_user(UserCache.get(session["acl"]["userName"]))
return True return True
return False return False
@ -158,7 +154,7 @@ def _auth_with_acl_token():
def auth_required(func): def auth_required(func):
if request.json is not None: if request.get_json(silent=True) is not None:
setattr(request, 'values', request.json) setattr(request, 'values', request.json)
else: else:
setattr(request, 'values', request.values.to_dict()) setattr(request, 'values', request.values.to_dict())

View File

@ -13,40 +13,39 @@ class Department(ModelWithoutPK):
__tablename__ = 'common_department' __tablename__ = 'common_department'
department_id = db.Column(db.Integer, primary_key=True, autoincrement=True) department_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
department_name = db.Column(db.VARCHAR(255), default='', comment='部门名称') department_name = db.Column(db.VARCHAR(255), default='')
department_director_id = db.Column( department_director_id = db.Column(
db.Integer, default=0, comment='部门负责人ID') db.Integer, default=0)
department_parent_id = db.Column(db.Integer, default=1, comment='上级部门ID') department_parent_id = db.Column(db.Integer, default=1)
sort_value = db.Column(db.Integer, default=0, comment='排序值') sort_value = db.Column(db.Integer, default=0)
acl_rid = db.Column(db.Integer, comment='ACL中rid', default=0) acl_rid = db.Column(db.Integer, default=0)
class Employee(ModelWithoutPK): class Employee(ModelWithoutPK):
__tablename__ = 'common_employee' __tablename__ = 'common_employee'
employee_id = db.Column(db.Integer, primary_key=True, autoincrement=True) employee_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.VARCHAR(255), default='', comment='邮箱') email = db.Column(db.VARCHAR(255), default='')
username = db.Column(db.VARCHAR(255), default='', comment='用户名') username = db.Column(db.VARCHAR(255), default='')
nickname = db.Column(db.VARCHAR(255), default='', comment='姓名') nickname = db.Column(db.VARCHAR(255), default='')
sex = db.Column(db.VARCHAR(64), default='', comment='性别') sex = db.Column(db.VARCHAR(64), default='')
position_name = db.Column(db.VARCHAR(255), default='', comment='职位名称') position_name = db.Column(db.VARCHAR(255), default='')
mobile = db.Column(db.VARCHAR(255), default='', comment='电话号码') mobile = db.Column(db.VARCHAR(255), default='')
avatar = db.Column(db.VARCHAR(255), default='', comment='头像') avatar = db.Column(db.VARCHAR(255), default='')
direct_supervisor_id = db.Column(db.Integer, default=0, comment='直接上级ID') direct_supervisor_id = db.Column(db.Integer, default=0)
department_id = db.Column(db.Integer, department_id = db.Column(db.Integer,
db.ForeignKey('common_department.department_id'), db.ForeignKey('common_department.department_id')
comment='部门ID',
) )
acl_uid = db.Column(db.Integer, comment='ACL中uid', default=0) acl_uid = db.Column(db.Integer, default=0)
acl_rid = db.Column(db.Integer, comment='ACL中rid', default=0) acl_rid = db.Column(db.Integer, default=0)
acl_virtual_rid = db.Column(db.Integer, comment='ACL中虚拟角色rid', default=0) acl_virtual_rid = db.Column(db.Integer, default=0)
last_login = db.Column(db.TIMESTAMP, nullable=True, comment='上次登录时间') last_login = db.Column(db.TIMESTAMP, nullable=True)
block = db.Column(db.Integer, comment='锁定状态', default=0) block = db.Column(db.Integer, default=0)
_department = db.relationship( _department = db.relationship(
'Department', backref='common_employee.department_id', 'Department', backref='common_employee.department_id',
@ -55,14 +54,11 @@ class Employee(ModelWithoutPK):
class EmployeeInfo(Model): class EmployeeInfo(Model):
"""
员工信息
"""
__tablename__ = 'common_employee_info' __tablename__ = 'common_employee_info'
info = db.Column(db.JSON, default={}, comment='员工信息') info = db.Column(db.JSON, default={})
employee_id = db.Column(db.Integer, db.ForeignKey( employee_id = db.Column(db.Integer, db.ForeignKey(
'common_employee.employee_id'), comment='员工ID') 'common_employee.employee_id'))
employee = db.relationship( employee = db.relationship(
'Employee', backref='common_employee.employee_id', lazy='joined') 'Employee', backref='common_employee.employee_id', lazy='joined')
@ -74,16 +70,13 @@ class CompanyInfo(Model):
class InternalMessage(Model): class InternalMessage(Model):
"""
内部消息
"""
__tablename__ = "common_internal_message" __tablename__ = "common_internal_message"
title = db.Column(db.VARCHAR(255), nullable=True, comment='标题') title = db.Column(db.VARCHAR(255), nullable=True)
content = db.Column(db.TEXT, nullable=True, comment='内容') content = db.Column(db.TEXT, nullable=True)
path = db.Column(db.VARCHAR(255), nullable=True, comment='跳转路径') path = db.Column(db.VARCHAR(255), nullable=True)
is_read = db.Column(db.Boolean, default=False, comment='是否已读') is_read = db.Column(db.Boolean, default=False)
app_name = db.Column(db.VARCHAR(128), nullable=False, comment='应用名称') app_name = db.Column(db.VARCHAR(128), nullable=False)
category = db.Column(db.VARCHAR(128), nullable=False, comment='分类') category = db.Column(db.VARCHAR(128), nullable=False)
message_data = db.Column(db.JSON, nullable=True, comment='数据') message_data = db.Column(db.JSON, nullable=True)
employee_id = db.Column(db.Integer, db.ForeignKey('common_employee.employee_id'), comment='ID') employee_id = db.Column(db.Integer, db.ForeignKey('common_employee.employee_id'), comment='ID')

View File

@ -13,13 +13,9 @@ from api.models.common_setting import Department
@celery.task(name="common_setting.edit_employee_department_in_acl", queue=COMMON_SETTING_QUEUE) @celery.task(name="common_setting.edit_employee_department_in_acl", queue=COMMON_SETTING_QUEUE)
def edit_employee_department_in_acl(e_list, new_d_id, op_uid): def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
""" """
ACL 员工更换部门 :param e_list:{acl_rid: 11, department_id: 22}
:param e_list: 员工列表 {acl_rid: 11, department_id: 22} :param new_d_id
:param new_d_id: 新部门 ID :param op_uid
:param op_uid: 操作人 ID
在老部门中删除员工
在新部门中添加员工
""" """
db.session.remove() db.session.remove()
@ -43,7 +39,6 @@ def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
new_department_acl_rid = new_department.acl_rid if new_d_rid_in_acl == new_department.acl_rid else new_d_rid_in_acl new_department_acl_rid = new_department.acl_rid if new_d_rid_in_acl == new_department.acl_rid else new_d_rid_in_acl
for employee in e_list: for employee in e_list:
# 根据 部门ID获取部门 acl_rid
old_department = Department.get_by( old_department = Department.get_by(
first=True, department_id=employee.get('department_id'), to_dict=False) first=True, department_id=employee.get('department_id'), to_dict=False)
if not old_department: if not old_department:
@ -61,7 +56,6 @@ def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
acl_rid=old_d_rid_in_acl acl_rid=old_d_rid_in_acl
) )
d_acl_rid = old_department.acl_rid if old_d_rid_in_acl == old_department.acl_rid else old_d_rid_in_acl d_acl_rid = old_department.acl_rid if old_d_rid_in_acl == old_department.acl_rid else old_d_rid_in_acl
# 在老部门中删除员工
payload = { payload = {
'app_id': 'acl', 'app_id': 'acl',
'parent_id': d_acl_rid, 'parent_id': d_acl_rid,
@ -71,7 +65,6 @@ def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
except Exception as e: except Exception as e:
result.append(ErrFormat.acl_remove_user_from_role_failed.format(str(e))) result.append(ErrFormat.acl_remove_user_from_role_failed.format(str(e)))
# 在新部门中添加员工
payload = { payload = {
'app_id': 'acl', 'app_id': 'acl',
'child_ids': [employee_acl_rid], 'child_ids': [employee_acl_rid],

View File

@ -100,7 +100,7 @@ class DepartmentSortView(APIView):
def put(self): def put(self):
""" """
修改部门排序只能在同一个上级内排序 only can sort in the same parent
""" """
department_list = request.json.get('department_list', None) department_list = request.json.get('department_list', None)
if department_list is None: if department_list is None:

View File

@ -145,31 +145,3 @@ class EmployeePositionView(APIView):
result = EmployeeCRUD.get_all_position() result = EmployeeCRUD.get_all_position()
return self.jsonify(result) return self.jsonify(result)
class EmployeeViewExportExcel(APIView):
url_prefix = (f'{prefix}/export_all',)
def get(self):
# 规定了静态文件的存储位置
excel_filename = 'all_employee_info.xlsx'
excel_path = current_app.config['UPLOAD_DIRECTORY_FULL']
excel_path_with_filename = os.path.join(excel_path, excel_filename)
# 根据parameter查表自连接通过上级id获取上级名字列
block_status = int(request.args.get('block_status', -1))
data_list = EmployeeCRUD.get_export_employee_list(block_status)
headers = data_list[0].keys()
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# insert header
for col_num, col_data in enumerate(headers, start=1):
ws.cell(row=1, column=col_num, value=col_data)
for row_num, row_data in enumerate(data_list, start=2):
for col_num, col_data in enumerate(row_data.values(), start=1):
ws.cell(row=row_num, column=col_num, value=col_data)
wb.save(excel_path_with_filename)
return send_from_directory(excel_path, excel_filename, as_attachment=True)

View File

@ -3,7 +3,7 @@
from api.app import create_app from api.app import create_app
from api.extensions import celery from api.extensions import celery
# celery worker -A celery_worker.celery -l DEBUG -E -Q xxxx # celery -A celery_worker.celery worker -l DEBUG -E -Q xxxx
app = create_app() app = create_app()
app.app_context().push() app.app_context().push()

View File

@ -1,20 +1,9 @@
-i https://mirrors.aliyun.com/pypi/simple -i https://mirrors.aliyun.com/pypi/simple
alembic==1.7.7 alembic==1.7.7
amqp==2.6.1
aniso8601==9.0.1
attrs==23.1.0
backports.zoneinfo==0.2.1
bcrypt==4.0.1
beautifulsoup4==4.12.2
billiard==3.6.4.0
bs4==0.0.1 bs4==0.0.1
cachelib==0.9.0 celery==5.3.1
celery==4.3.0
celery-once==3.0.1 celery-once==3.0.1
certifi==2023.7.22
charset-normalizer==3.1.0
click==8.1.3 click==8.1.3
dnspython==2.3.0
elasticsearch==7.17.9 elasticsearch==7.17.9
email-validator==1.3.1 email-validator==1.3.1
environs==4.2.0 environs==4.2.0
@ -27,51 +16,31 @@ Flask-Login==0.6.2
Flask-Migrate==2.5.2 Flask-Migrate==2.5.2
Flask-RESTful==0.3.10 Flask-RESTful==0.3.10
Flask-SQLAlchemy==2.5.0 Flask-SQLAlchemy==2.5.0
future==0.18.2 future==0.18.3
gunicorn==21.0.1 gunicorn==21.0.1
idna==3.4
importlib-metadata==6.8.0
importlib-resources=
=6.0.0
itsdangerous==2.1.2 itsdangerous==2.1.2
Jinja2==3.1.2 Jinja2==3.1.2
jinja2schema==0.1.4 jinja2schema==0.1.4
jsonschema==4.18.0 jsonschema==4.18.0
jsonschema-specifications==2023.6.1 kombu==5.3.1
kombu==4.4.0
Mako==1.2.4 Mako==1.2.4
MarkupSafe==2.1.3 MarkupSafe==2.1.3
marshmallow==2.20.2 marshmallow==2.20.2
meld3==2.0.1
mistune==3.0.1
more-itertools==5.0.0 more-itertools==5.0.0
msgpack-python==0.5.6 msgpack-python==0.5.6
Pillow==9.3.0 Pillow==9.3.0
pkgutil_resolve_name==1.3.10
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycryptodome==3.12.0 pycryptodome==3.12.0
PyJWT==2.4.0 PyJWT==2.4.0
PyMySQL==1.1.0 PyMySQL==1.1.0
python-dateutil==2.8.2
python-dotenv==1.0.0
python-ldap==3.4.0 python-ldap==3.4.0
pytz==2023.3
PyYAML==6.0 PyYAML==6.0
redis==3.2.1 redis==4.6.0
referencing==0.29.1
requests==2.31.0 requests==2.31.0
rpds-py==0.8.8
six==1.12.0 six==1.12.0
soupsieve==2.4.1
SQLAlchemy==1.4.49 SQLAlchemy==1.4.49
supervisor==4.0.3 supervisor==4.0.3
timeout-decorator==0.5.0 timeout-decorator==0.5.0
toposort==1.10 toposort==1.10
treelib==1.6.1 treelib==1.6.1
tzlocal==5.0.1
urllib3==1.26.16
vine==1.3.0
Werkzeug==2.3.6 Werkzeug==2.3.6
WTForms==3.0.0 WTForms==3.0.0
zipp==3.16.0

View File

@ -53,13 +53,16 @@ MAIL_PASSWORD = ''
DEFAULT_MAIL_SENDER = '' DEFAULT_MAIL_SENDER = ''
# # queue # # queue
CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/2" CELERY = {
BROKER_URL = 'redis://127.0.0.1:6379/2' "broker_url": 'redis://127.0.0.1:6379/2',
BROKER_VHOST = '/' "result_backend": "redis://127.0.0.1:6379/2",
"broker_vhost": "/",
"broker_connection_retry_on_startup": True
}
ONCE = { ONCE = {
'backend': 'celery_once.backends.Redis', 'backend': 'celery_once.backends.Redis',
'settings': { 'settings': {
'url': BROKER_URL, 'url': CELERY['broker_url'],
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -28,8 +28,8 @@ cp cmdb-api/settings.example.py cmdb-api/settings.py
- 后端: 进入**cmdb-api**目录执行 `pipenv run flask run -h 0.0.0.0` - 后端: 进入**cmdb-api**目录执行 `pipenv run flask run -h 0.0.0.0`
- 前端: 进入**cmdb-ui**目录执行`yarn run serve` - 前端: 进入**cmdb-ui**目录执行`yarn run serve`
- worker: - worker:
- 进入**cmdb-api**目录执行 `pipenv run celery worker -A celery_worker.celery -E -Q one_cmdb_async --concurrency=1 -D` - 进入**cmdb-api**目录执行 `pipenv run celery -A celery_worker.celery worker -E -Q one_cmdb_async --concurrency=1 -D`
- 进入**cmdb-api**目录执行 `pipenv run celery worker -A celery_worker.celery -E -Q acl_async --concurrency=1 -D` - 进入**cmdb-api**目录执行 `pipenv run celery -A celery_worker.celery worker -E -Q acl_async --concurrency=1 -D`
- 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000) - 浏览器打开: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- 如果是非本机访问, 要修改**cmdb-ui/.env**里**VUE_APP_API_BASE_URL**里的 IP 地址为后端服务的 ip 地址 - 如果是非本机访问, 要修改**cmdb-ui/.env**里**VUE_APP_API_BASE_URL**里的 IP 地址为后端服务的 ip 地址

View File

@ -26,8 +26,8 @@
- backend: in **cmdb-api** directory: `pipenv run flask run -h 0.0.0.0` - backend: in **cmdb-api** directory: `pipenv run flask run -h 0.0.0.0`
- frontend: in **cmdb-ui** directory: `yarn run serve` - frontend: in **cmdb-ui** directory: `yarn run serve`
- worker: - worker:
- in **cmdb-api** directory: `pipenv run celery worker -A celery_worker.celery -E -Q one_cmdb_async --concurrency=1 -D` - in **cmdb-api** directory: `pipenv run celery -A celery_worker.celery worker -E -Q one_cmdb_async --concurrency=1 -D`
- in **cmdb-api** directory: `pipenv run celery worker -A celery_worker.celery -E -Q acl_async --concurrency=1 -D` - in **cmdb-api** directory: `pipenv run celery -A celery_worker.celery worker -E -Q acl_async --concurrency=1 -D`
- homepage: [http://127.0.0.1:8000](http://127.0.0.1:8000) - homepage: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- if not run localhost: please change ip address(**VUE_APP_API_BASE_URL**) in config file **cmdb-ui/.env** into your backend ip address - if not run localhost: please change ip address(**VUE_APP_API_BASE_URL**) in config file **cmdb-ui/.env** into your backend ip address