增加项目的各个功能

This commit is contained in:
qiaoxinjiu
2026-05-07 19:21:19 +08:00
parent aba1618f89
commit ee6cd4ae66
121 changed files with 9346 additions and 43 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,55 @@
# encoding: UTF-8
from ..dao.bugDao import BugDao
from ..model.bugModel import BugComment
class BugService(object):
"""Bug 管理 Service 层"""
@staticmethod
def create(session, model_cls, add_info):
return BugDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return BugDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return BugDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None, asc=False):
return BugDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column, asc)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return BugDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def get_comments(session, bug_id):
return BugDao.get_comments(session, bug_id)
@staticmethod
def get_history(session, bug_id):
return BugDao.get_history(session, bug_id)
@staticmethod
def add_comment(session, bug_id, content, user_id):
return BugDao.create(session, BugComment, {
'bug_id': bug_id,
'content': content,
'user_id': user_id
})
@staticmethod
def generate_bug_key(session):
return BugDao.generate_bug_key(session)
@staticmethod
def get_stats(session, product_id=None, project_id=None):
return BugDao.get_stats(session, product_id, project_id)
@staticmethod
def add_history(session, bug_id, field_name, old_value, new_value, operator_id):
return BugDao.add_history(session, bug_id, field_name, old_value, new_value, operator_id)

View File

@@ -0,0 +1,38 @@
# encoding: UTF-8
from ..dao.caseDao import CaseDao
class CaseService(object):
"""用例域 Service 层,封装用例编号和快照版本等业务能力。"""
@staticmethod
def create(session, model_cls, add_info):
return CaseDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return CaseDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return CaseDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return CaseDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return CaseDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def next_case_key(session, project_id):
return CaseDao.next_case_key(session, project_id)
@staticmethod
def next_snapshot_version(session, case_id):
return CaseDao.next_snapshot_version(session, case_id)
@staticmethod
def get_module_name_map(session, module_ids):
return CaseDao.get_module_name_map(session, module_ids)

View File

@@ -0,0 +1,63 @@
# encoding: UTF-8
from datetime import datetime
from common.dataBuilderExecutor import DataBuilderExecutor
from ..dao.dataBuilderDao import DataBuilderDao
from ..model.dataBuilderModel import DataBuilder, DataTask
class DataBuilderService(object):
@staticmethod
def create(session, model_cls, add_info):
return DataBuilderDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return DataBuilderDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return DataBuilderDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return DataBuilderDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return DataBuilderDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def execute_builder(session, builder_id, params=None, created_by=None):
builder = DataBuilderDao.get_by_id(session, DataBuilder, builder_id)
if not builder:
return {}, '未查询到对应造数器!'
params = params or {}
task_info = {
'builder_id': builder.id,
'project_id': builder.project_id,
'params': params,
'status': 1,
'created_by': created_by
}
# 先写入执行中任务,保证失败时也能追踪任务记录。
task_id, err_msg = DataBuilderDao.create(session, DataTask, task_info)
if err_msg:
return {}, err_msg
try:
# 当前 MVP 只做同步模板渲染执行,后续可在 executor 内扩展 http/db step。
executor = DataBuilderExecutor(builder.definition or {}, {})
result_data = executor.execute(params)
DataBuilderDao.update_by_id(session, DataTask, task_id, {
'status': 2,
'result_data': result_data,
'completed_time': datetime.now()
}, soft_delete=False)
return {'taskId': task_id, 'data': result_data}, ''
except Exception as e:
DataBuilderDao.update_by_id(session, DataTask, task_id, {
'status': 3,
'error_message': str(e),
'completed_time': datetime.now()
}, soft_delete=False)
return {}, f'执行造数失败!{e}'

View File

@@ -0,0 +1,34 @@
# encoding: UTF-8
from ..dao.planDao import PlanDao
class PlanService(object):
"""测试计划域 Service 层,封装计划统计等业务能力。"""
@staticmethod
def create(session, model_cls, add_info):
return PlanDao.create(session, model_cls, add_info)
@staticmethod
def batch_create(session, model_cls, batch_info_list):
return PlanDao.batch_create(session, model_cls, batch_info_list)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return PlanDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return PlanDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None, asc=False):
return PlanDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column, asc)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return PlanDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def plan_stats(session, plan_id):
return PlanDao.plan_stats(session, plan_id)

View File

@@ -0,0 +1,24 @@
# encoding: UTF-8
from ..dao.productDao import ProductDao
class ProductService(object):
@staticmethod
def create(session, model_cls, add_info):
return ProductDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return ProductDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return ProductDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return ProductDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return ProductDao.delete_by_id(session, model_cls, obj_id)

View File

@@ -0,0 +1,36 @@
# encoding: UTF-8
from ..dao.projectHookDao import ProjectHookDao
from ..model.projectHookModel import ProjectHook
class ProjectHookService(object):
@staticmethod
def create(session, model_cls, add_info):
return ProjectHookDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return ProjectHookDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return ProjectHookDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return ProjectHookDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return ProjectHookDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def get_hooks_by_project(session, project_id, hook_type=None):
filters = [
ProjectHook.project_id == int(project_id),
ProjectHook.is_delete == 0,
ProjectHook.enabled == 1
]
if hook_type not in (None, ''):
filters.append(ProjectHook.hook_type == int(hook_type))
return ProjectHookDao.list_all_by_filters(session, ProjectHook, filters)

View File

@@ -0,0 +1,34 @@
# encoding: UTF-8
from ..dao.projectDao import ProjectDao
class ProjectService(object):
"""项目域 Service 层,保持业务入口与 DAO 解耦。"""
@staticmethod
def create(session, model_cls, add_info):
return ProjectDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return ProjectDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return ProjectDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return ProjectDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return ProjectDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def get_product_map(session, product_ids):
return ProjectDao.get_product_map(session, product_ids)
@staticmethod
def get_project_name_map(session, project_ids):
return ProjectDao.get_project_name_map(session, project_ids)

View File

@@ -0,0 +1,103 @@
# encoding: UTF-8
from ..dao.rbacDao import RbacDao
def has_permission(permission_code, permission_codes):
if not permission_code:
return True
if not permission_codes:
return False
if permission_code in permission_codes:
return True
if '*:*' in permission_codes:
return True
if ':' in permission_code:
module_code = permission_code.split(':', 1)[0]
if f'{module_code}:*' in permission_codes:
return True
if '_' in module_code:
parent_module_code = module_code.split('_', 1)[0]
if f'{parent_module_code}:*' in permission_codes:
return True
return False
class RbacService(object):
@staticmethod
def create(session, model_cls, add_info):
return RbacDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return RbacDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return RbacDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return RbacDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return RbacDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def assign_permissions(session, role_ids, permission_id):
return RbacDao.assign_permissions_to_roles(session, role_ids, permission_id)
@staticmethod
def assign_menus(session, role_id, menu_ids):
return RbacDao.replace_role_menus(session, role_id, menu_ids)
@staticmethod
def get_role_permission_ids(session, role_id):
return RbacDao.get_role_permission_ids(session, role_id)
@staticmethod
def get_role_menu_ids(session, role_id):
return RbacDao.get_role_menu_ids(session, role_id)
@staticmethod
def build_menu_tree(session, filters, role_ids=None, menu_ids=None):
items = RbacDao.get_menu_tree_items(session, filters)
visible_ids = set()
if not role_ids and not menu_ids:
visible_ids = {item.id for item in items}
else:
role_menu_ids = set(menu_ids or [])
if role_ids:
for role_id in role_ids:
role_menu_ids.update(RbacDao.get_role_menu_ids(session, role_id))
visible_ids = set(role_menu_ids)
item_by_id = {item.id: item for item in items}
for item_id in list(visible_ids):
if item_id not in item_by_id:
continue
parent_id = item_by_id[item_id].parent_id
while parent_id and parent_id in item_by_id:
if parent_id in visible_ids:
break
visible_ids.add(parent_id)
parent_id = item_by_id[parent_id].parent_id
item_map = {}
roots = []
for item in items:
if item.id not in visible_ids:
continue
item_dict = item.to_dict()
item_dict['children'] = []
item_map[item.id] = item_dict
for item in items:
if item.id not in item_map:
continue
if item.parent_id and item.parent_id in item_map:
item_map[item.parent_id]['children'].append(item_map[item.id])
else:
roots.append(item_map[item.id])
return roots
@staticmethod
def get_role_permission_codes(session, role_ids):
return RbacDao.get_role_permission_codes(session, role_ids)

View File

@@ -0,0 +1,46 @@
# encoding: UTF-8
from ..dao.planDao import PlanDao
from ..dao.projectDao import ProjectDao
from ..dao.reportDao import ReportDao
from ..model.planModel import TestPlan
from ..model.reportModel import Report
class ReportService(object):
@staticmethod
def create(session, model_cls, add_info):
return ReportDao.create(session, model_cls, add_info)
@staticmethod
def get_by_id(session, model_cls, obj_id):
return ReportDao.get_by_id(session, model_cls, obj_id)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None, asc=False):
return ReportDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column, asc)
@staticmethod
def generate_report(session, plan_id, generated_by=None):
plan = PlanDao.get_by_id(session, TestPlan, plan_id)
if not plan:
return 0, '未查询到对应计划!'
project = ProjectDao.get_by_id(session, ProjectDao.project_model(), plan.project_id)
if not project:
return 0, '未查询到对应项目!'
# 复用计划统计,保证计划详情和报告中的指标口径一致。
stats = PlanDao.plan_stats(session, plan_id)
# MVP 阶段先生成简单 HTML后续可替换为模板渲染器。
content = '<html><body><h1>{}</h1><p>总用例:{}</p><p>通过率:{}%</p></body></html>'.format(
plan.name, stats['total_cases'], stats['pass_rate']
)
add_info = {
'plan_id': int(plan_id),
'project_id': plan.project_id,
'product_id': project.product_id,
'name': '{}_报告'.format(plan.name),
'report_type': 1,
'summary': stats,
'content': content,
'generated_by': generated_by
}
return ReportDao.create(session, Report, add_info)

View File

@@ -0,0 +1,58 @@
# encoding: UTF-8
from ..dao.userDao import UserDao
from ..dao.rbacDao import RbacDao
class UserService(object):
@staticmethod
def create(session, model_cls, add_info):
return UserDao.create(session, model_cls, add_info)
@staticmethod
def update_by_id(session, model_cls, obj_id, update_info, soft_delete=True):
return UserDao.update_by_id(session, model_cls, obj_id, update_info, soft_delete)
@staticmethod
def get_by_id(session, model_cls, obj_id, soft_delete=True):
return UserDao.get_by_id(session, model_cls, obj_id, soft_delete)
@staticmethod
def list_by_filters(session, model_cls, filter_list, page_num=1, page_size=20, order_column=None):
return UserDao.list_by_filters(session, model_cls, filter_list, int(page_num), int(page_size), order_column)
@staticmethod
def delete_by_id(session, model_cls, obj_id):
return UserDao.delete_by_id(session, model_cls, obj_id)
@staticmethod
def assign_roles(session, user_id, role_ids):
return UserDao.replace_user_roles(session, user_id, role_ids)
@staticmethod
def get_user_role_ids(session, user_id):
return UserDao.get_user_role_ids(session, user_id)
@staticmethod
def get_user_roles_map(session, user_ids):
user_role_map = UserDao.get_user_roles(session, user_ids)
role_ids = list({role_id for role_list in user_role_map.values() for role_id in role_list})
role_name_map = RbacDao.get_role_names_map(session, role_ids)
ret = {}
for user_id, ids in user_role_map.items():
ret[user_id] = {
'role_ids': ids,
'role_names': [role_name_map.get(role_id, '') for role_id in ids]
}
return ret
@staticmethod
def get_by_username(session, username):
return UserDao.get_by_username(session, username)
@staticmethod
def get_user_info_map(session, user_ids):
return UserDao.get_user_info_map(session, user_ids)
@staticmethod
def update_last_login_time(session, user_id):
return UserDao.update_last_login_time(session, user_id)