""" Stateflow业务逻辑服务层 """ from typing import List, Optional, Tuple from django.contrib.auth import get_user_model from django.db import transaction from . import models User = get_user_model() def get_business_object_current_state(business_object: 'models.BusinessObject') -> Optional['models.State']: """ 获取订单的当前状态 规则: 1. 如果没有任何完成记录(或所有记录都被撤销),返回 None(未开始处理) 2. 如果有未完成的节点,返回第一个未完成的节点(进行中) 3. 如果所有节点都已完成,返回 None(流程已完成) """ # 获取流程的所有节点(按顺序) process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id') if not process_nodes.exists(): return None # 获取已完成且未被撤销的状态ID集合 completed_state_ids = set( business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True) ) # 如果没有任何有效完成记录,返回 None(未开始) if not completed_state_ids: return None # 找到第一个未完成的节点 for node in process_nodes: if node.state_id not in completed_state_ids: return node.state # 所有节点都已完成,返回 None(已完成) return None def get_business_object_state_status(business_object: 'models.BusinessObject', state: 'models.State') -> str: """ 获取订单中某个状态的状态 返回值: - 'not_started': 未开始 - 'in_progress': 进行中 - 'completed': 已完成 """ # 检查是否已完成且未被撤销 is_completed = business_object.state_logs.filter(state=state, is_cancelled=False).exists() if is_completed: return 'completed' current_state = get_business_object_current_state(business_object) # 检查是否为当前状态 if current_state and current_state.id == state.id: return 'in_progress' return 'not_started' def get_completed_node_ids(business_object: 'models.BusinessObject') -> List[int]: """获取订单已完成且未撤销的节点ID列表(按完成顺序)""" return list( business_object.state_logs.filter(is_cancelled=False) .order_by('completed_at') .values_list('state_id', flat=True) ) def get_progress_percentage(business_object: 'models.BusinessObject') -> float: """计算订单进度百分比""" total_nodes = business_object.process.process_nodes.count() if total_nodes == 0: return 0.0 completed_count = business_object.state_logs.filter(is_cancelled=False).count() return (completed_count / total_nodes) * 100 def advance_to_next_state(business_object: 'models.BusinessObject', user) -> Tuple[bool, str]: """ 将订单推进到下一个状态 返回: (是否成功, 消息) """ can_advance, reason = can_advance_to_next_state(business_object) if not can_advance: return False, reason with transaction.atomic(): # 获取当前状态(返回第一个未完成的节点) current_state = get_business_object_current_state(business_object) # 如果当前状态为 None,说明是初始状态(未开始) if current_state is None: # 获取第一个节点 first_node = business_object.process.process_nodes.order_by('order', 'id').first() if not first_node: return False, "流程没有任何节点" # 标记第一个状态为已完成 models.StateFlowRecord.objects.create( business_object=business_object, state=first_node.state, completed_by=user ) return True, f"已完成状态: {first_node.state.name}" # current_state 不为 None 时,它一定是未完成的节点 # 标记当前状态为已完成 models.StateFlowRecord.objects.create( business_object=business_object, state=current_state, completed_by=user ) # 检查是否所有状态都已完成 next_state = get_business_object_current_state(business_object) if next_state is None: # 所有状态都已完成 return True, f"流程已完成,最后状态: {current_state.name}" return True, f"已完成状态: {current_state.name}" def reset_business_object_progress(business_object: 'models.BusinessObject') -> None: """ 重置业务对象进度 注意:不删除历史记录,而是标记所有记录为已撤销,并记录撤销时间 这样可以保留完整的操作历史,支持审计需求 """ from django.utils import timezone business_object.state_logs.filter(is_cancelled=False).update( is_cancelled=True, cancelled_at=timezone.now() ) # 向后兼容的别名 reset_order_progress = reset_business_object_progress def get_current_state_parameters(business_object: 'models.BusinessObject') -> List['models.StateParameter']: """获取订单当前状态的参数列表""" current_state = get_business_object_current_state(business_object) if current_state: return list(current_state.parameters.all()) return [] def get_overall_status(business_object: 'models.BusinessObject') -> str: """ 获取订单的整体状态 返回值: - 'not_started': 未开始(没有任何有效的完成记录) - 'in_progress': 进行中(有部分状态已完成) - 'completed': 已完成(所有状态都已完成) """ current_state = get_business_object_current_state(business_object) # 检查是否有任何有效的完成记录 has_completed = business_object.state_logs.filter(is_cancelled=False).exists() if not has_completed: return 'not_started' if current_state is None: # 有完成记录,但当前状态为 None,说明所有状态都已完成 return 'completed' return 'in_progress' def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple[bool, str]: """ 检查订单是否可以推进到下一个状态 返回: (是否可以推进, 原因) """ # 检查流程是否有节点 first_node = business_object.process.process_nodes.order_by('order', 'id').first() if not first_node: return False, "流程没有任何节点" current_state = get_business_object_current_state(business_object) # 如果当前状态为 None,有两种情况: # 1. 没有完成记录 - 未开始 # 2. 所有节点已完成 - 已完成 if current_state is None: completed_count = business_object.state_logs.filter(is_cancelled=False).count() if completed_count == 0: # 未开始,可以推进到第一个状态 return True, f"可以开始处理,将推进到: {first_node.state.name}" else: # 所有状态都已完成 return False, "流程已完成,无法继续推进" # current_state 不为 None 时,它一定是第一个未完成的节点,可以推进 return True, f"可以推进到: {current_state.name}" def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]: """ 获取订单状态时间线(包括未开始、进行中和已完成的状态) 返回格式: [ { 'state': State对象, 'status': 'not_started' | 'in_progress' | 'completed' | 'cancelled', 'business_object': 顺序号, 'completed_at': 完成时间(如果已完成), 'completed_by': 完成人(如果已完成), 'cancelled_at': 撤销时间(如果已撤销), 'is_cancelled': 是否已撤销, }, ... ] """ timeline = [] process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id') current_state = get_business_object_current_state(business_object) # 构建状态日志映射(包括已撤销的记录) state_logs_map = { log.state_id: log for log in business_object.state_logs.select_related('completed_by') } for node in process_nodes: state = node.state log = state_logs_map.get(state.id) if log: if log.is_cancelled: status = 'cancelled' else: status = 'completed' completed_at = log.completed_at completed_by = log.completed_by cancelled_at = log.cancelled_at is_cancelled = log.is_cancelled elif current_state and state.id == current_state.id: status = 'in_progress' completed_at = None completed_by = None cancelled_at = None is_cancelled = False else: status = 'not_started' completed_at = None completed_by = None cancelled_at = None is_cancelled = False timeline.append({ 'state': state, 'status': status, 'order': node.order, 'completed_at': completed_at, 'completed_by': completed_by, 'cancelled_at': cancelled_at, 'is_cancelled': is_cancelled, }) return timeline