forked from erp-dev/erp
feat: new modul (stateflow)
This commit is contained in:
275
stateflow/services.py
Normal file
275
stateflow/services.py
Normal file
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
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: 'User') -> Tuple[bool, str]:
|
||||
"""
|
||||
将订单推进到下一个状态
|
||||
|
||||
返回: (是否成功, 消息)
|
||||
"""
|
||||
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}"
|
||||
|
||||
# 检查当前状态是否已完成(且未撤销)
|
||||
if business_object.state_logs.filter(state=current_state, is_cancelled=False).exists():
|
||||
return False, f"当前状态 '{current_state.name}' 已完成"
|
||||
|
||||
# 标记当前状态为已完成
|
||||
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_order_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()
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
if current_state is None:
|
||||
# 检查是否所有状态都已完成
|
||||
total_nodes = business_object.process.process_nodes.count()
|
||||
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, "流程已完成,无法继续推进"
|
||||
|
||||
# 检查当前状态是否已完成
|
||||
if business_object.state_logs.filter(state=current_state, is_cancelled=False).exists():
|
||||
return False, f"当前状态 '{current_state.name}' 已完成,无法重复完成"
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user