1
0
forked from erp-dev/erp
Files
erpnew/stateflow/services.py

464 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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. 如果有完成记录,返回最后完成的状态(进行中或已完成)
注意current_state 表示"最后完成的状态",而不是"待执行的状态"
"""
# 获取最后一条完成且未撤销的记录
last_completed_record = (
business_object.state_logs
.filter(is_cancelled=False)
.select_related('state')
.order_by('-completed_at', '-id')
.first()
)
if last_completed_record:
return last_completed_record.state
# 没有任何完成记录,返回 None未开始
return None
def get_business_object_state_status(business_object: 'models.BusinessObject', state: 'models.State') -> str:
"""
获取业务对象中某个状态的状态
返回值:
- 'not_started': 未开始
- 'completed': 已完成
注意:由于 current_state 现在表示"最后完成的状态",不再有 'in_progress' 概念
"""
# 检查是否已完成且未被撤销
is_completed = business_object.state_logs.filter(state=state, is_cancelled=False).exists()
if is_completed:
return 'completed'
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, **parameters) -> Tuple[bool, str, Optional['models.StateFlowRecord']]:
"""
将业务对象推进到下一个状态(完成下一个未完成的节点)
参数:
business_object: BusinessObject 实例
user: 操作用户
**parameters: 状态参数(可选)
返回:
tuple: (success: bool, message: str, state_log: StateFlowRecord|None)
- success: 是否成功
- message: 提示信息
- state_log: 创建的状态日志(成功时)
"""
can_advance, reason = can_advance_to_next_state(business_object)
if not can_advance:
return False, reason, None
with transaction.atomic():
# 获取下一个待执行的节点
next_pending = get_next_pending_state(business_object, include_parameters=False)
if not next_pending:
return False, "没有待执行的节点", None
next_state = next_pending['state']
# 验证必填参数
if parameters:
is_valid, missing_params = validate_required_parameters(next_state, **parameters)
if not is_valid:
return False, f"缺失必填参数: {', '.join(missing_params)}", None
else:
# 检查是否有必填参数
required_params = get_required_parameters(next_state)
if required_params.exists():
required_keys = list(required_params.values_list('key', flat=True))
return False, f"缺失必填参数: {', '.join(required_keys)}", None
# 创建状态流转记录
state_log = models.StateFlowRecord.objects.create(
business_object=business_object,
state=next_state,
completed_by=user
)
# 如果提供了参数,创建参数记录
if parameters:
create_parameter_record(state_log, **parameters)
# 检查是否所有状态都已完成
after_advance = get_next_pending_state(business_object, include_parameters=False)
if after_advance is None:
# 所有状态都已完成
return True, f"流程已完成,最后状态: {next_state.name}", state_log
return True, f"已完成状态: {next_state.name}", state_log
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 step_back_one_state(business_object: 'models.BusinessObject', user) -> Tuple[bool, str]:
"""
回退一步(撤销最后一次完成的状态)
这是 advance_to_next_state 的逆向操作
返回: (是否成功, 消息)
"""
from django.utils import timezone
with transaction.atomic():
# 获取最后一条有效的状态流转记录(按完成时间倒序)
last_record = (
business_object.state_logs
.filter(is_cancelled=False)
.order_by('-completed_at', '-id')
.first()
)
# 如果没有任何有效记录,说明当前没有任何状态流转,无需回退
if not last_record:
return False, "当前没有任何状态流转记录,无法回退"
# 标记最后一条记录为已撤销
last_record.is_cancelled = True
last_record.cancelled_at = timezone.now()
last_record.save(update_fields=['is_cancelled', 'cancelled_at'])
return True, f"已回退状态: {last_record.state.name}"
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': 已完成(所有状态都已完成)
"""
# 检查是否有任何有效的完成记录
has_completed = business_object.state_logs.filter(is_cancelled=False).exists()
if not has_completed:
return 'not_started'
# 检查是否有下一个待执行节点
next_pending = get_next_pending_state(business_object, include_parameters=False)
if next_pending is 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, "流程没有任何节点"
# 获取下一个待执行节点
next_pending = get_next_pending_state(business_object, include_parameters=False)
if next_pending is None:
# 没有待执行节点,流程已完成
return False, "流程已完成,无法继续推进"
next_state = next_pending['state']
return True, f"可以推进到: {next_state.name}"
def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]:
"""
获取业务对象状态时间线(包括未开始和已完成的状态)
返回格式:
[
{
'state': State对象,
'status': 'not_started' | 'completed' | 'cancelled',
'order': 顺序号,
'completed_at': 完成时间(如果已完成),
'completed_by': 完成人(如果已完成),
'cancelled_at': 撤销时间(如果已撤销),
'is_cancelled': 是否已撤销,
},
...
]
"""
timeline = []
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
# 构建状态日志映射(包括已撤销的记录)
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
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
def get_next_pending_state(
business_object: 'models.BusinessObject',
include_parameters: bool = True
) -> Optional[dict]:
"""
获取指定业务对象的下一个待执行节点(第一个未完成的节点)
Args:
business_object: 业务对象
include_parameters: 是否包含参数列表,默认 True
Returns:
包含状态信息的字典,如果没有待执行节点则返回 None
{
'state': State对象,
'order': 节点顺序号,
'parameters': [StateParameter列表] (如果 include_parameters=True)
}
"""
# 获取所有节点(按顺序)
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)
)
# 找到第一个未完成的节点
for node in process_nodes:
if node.state_id not in completed_state_ids:
result = {
'state': node.state,
'order': node.order,
}
if include_parameters:
result['parameters'] = list(node.state.parameters.all())
return result
# 所有节点都已完成
return None
def get_all_pending_states(business_object: 'models.BusinessObject') -> List[dict]:
"""
获取指定业务对象所有待执行的节点列表(不包含参数)
Args:
business_object: 业务对象
Returns:
待执行节点列表,每个元素包含:
{
'state': State对象,
'order': 节点顺序号
}
"""
# 获取所有节点
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
# 获取已完成且未被撤销的状态ID集合
completed_state_ids = set(
business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True)
)
# 找到所有未完成的节点
pending_nodes = []
for node in process_nodes:
if node.state_id not in completed_state_ids:
pending_nodes.append({
'state': node.state,
'order': node.order,
})
return pending_nodes
def get_state_parameters(
state: 'models.State',
required_only: bool = False
) -> List['models.StateParameter']:
"""
获取指定节点的所有参数列表
Args:
state: 状态节点
required_only: 是否仅包含必填参数,默认 False
Returns:
参数列表
"""
return state.get_parameters(required_only=required_only)
def get_required_parameters(state: 'models.State'):
"""
获取状态的所有必填参数
参数:
state: State 实例
返回:
QuerySet: 必填的 StateParameter 对象
"""
return state.parameters.filter(is_required=True)
def validate_required_parameters(state: 'models.State', **kwargs) -> Tuple[bool, List[str]]:
"""
验证必填参数是否全部提供
参数:
state: State 实例
**kwargs: 用户提供的参数字典
返回:
tuple: (is_valid: bool, missing_params: list)
- is_valid: 是否通过验证
- missing_params: 缺失的必填参数 key 列表
"""
required_params = get_required_parameters(state)
required_keys = list(required_params.values_list('key', flat=True))
provided_keys = set(kwargs.keys())
missing_keys = [key for key in required_keys if key not in provided_keys]
is_valid = len(missing_keys) == 0
return is_valid, missing_keys
def create_parameter_record(state_log: 'models.StateFlowRecord', remark: str = '', **parameters) -> 'models.StateLogParameterRecord':
"""
为状态流转记录创建参数记录
参数:
state_log: StateFlowRecord 实例
remark: 备注
**parameters: 参数字典(直接保存,不做验证)
返回:
StateLogParameterRecord: 创建的参数记录
"""
record = models.StateLogParameterRecord.objects.create(
state_log=state_log,
parameters=parameters, # 直接保存为 JSON
remark=remark
)
return record
def add_parameters_to_state_log(state_log: 'models.StateFlowRecord', remark: str = '', **parameters) -> 'models.StateLogParameterRecord':
"""
为已有的状态流转记录补充参数支持重复key
参数:
state_log: StateFlowRecord 实例
remark: 备注
**parameters: 参数字典
返回:
StateLogParameterRecord: 创建的参数记录
"""
return create_parameter_record(state_log, remark, **parameters)