forked from erp-dev/erp
feat: stateflow v2 (params required)
This commit is contained in:
@@ -11,56 +11,45 @@ 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
|
||||
1. 如果没有任何完成记录(或所有记录都被撤销),返回 None(未开始)
|
||||
2. 如果有完成记录,返回最后完成的状态(进行中或已完成)
|
||||
|
||||
# 获取已完成且未被撤销的状态ID集合
|
||||
completed_state_ids = set(
|
||||
business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True)
|
||||
注意:current_state 表示"最后完成的状态",而不是"待执行的状态"
|
||||
"""
|
||||
# 获取最后一条完成且未撤销的记录
|
||||
last_completed_record = (
|
||||
business_object.state_logs
|
||||
.filter(is_cancelled=False)
|
||||
.select_related('state')
|
||||
.order_by('-completed_at', '-id')
|
||||
.first()
|
||||
)
|
||||
|
||||
# 如果没有任何有效完成记录,返回 None(未开始)
|
||||
if not completed_state_ids:
|
||||
return None
|
||||
if last_completed_record:
|
||||
return last_completed_record.state
|
||||
|
||||
# 找到第一个未完成的节点
|
||||
for node in process_nodes:
|
||||
if node.state_id not in completed_state_ids:
|
||||
return node.state
|
||||
|
||||
# 所有节点都已完成,返回 None(已完成)
|
||||
# 没有任何完成记录,返回 None(未开始)
|
||||
return None
|
||||
|
||||
|
||||
def get_business_object_state_status(business_object: 'models.BusinessObject', state: 'models.State') -> str:
|
||||
"""
|
||||
获取订单中某个状态的状态
|
||||
获取业务对象中某个状态的状态
|
||||
|
||||
返回值:
|
||||
- 'not_started': 未开始
|
||||
- 'in_progress': 进行中
|
||||
- 'completed': 已完成
|
||||
|
||||
注意:由于 current_state 现在表示"最后完成的状态",不再有 'in_progress' 概念
|
||||
"""
|
||||
# 检查是否已完成且未被撤销
|
||||
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'
|
||||
|
||||
|
||||
@@ -83,50 +72,64 @@ def get_progress_percentage(business_object: 'models.BusinessObject') -> float:
|
||||
return (completed_count / total_nodes) * 100
|
||||
|
||||
|
||||
def advance_to_next_state(business_object: 'models.BusinessObject', user) -> Tuple[bool, str]:
|
||||
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
|
||||
return False, reason, None
|
||||
|
||||
with transaction.atomic():
|
||||
# 获取当前状态(返回第一个未完成的节点)
|
||||
current_state = get_business_object_current_state(business_object)
|
||||
# 获取下一个待执行的节点
|
||||
next_pending = get_next_pending_state(business_object, include_parameters=False)
|
||||
|
||||
# 如果当前状态为 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 not next_pending:
|
||||
return False, "没有待执行的节点", None
|
||||
|
||||
# current_state 不为 None 时,它一定是未完成的节点
|
||||
# 标记当前状态为已完成
|
||||
models.StateFlowRecord.objects.create(
|
||||
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=current_state,
|
||||
state=next_state,
|
||||
completed_by=user
|
||||
)
|
||||
|
||||
# 检查是否所有状态都已完成
|
||||
next_state = get_business_object_current_state(business_object)
|
||||
if next_state is None:
|
||||
# 所有状态都已完成
|
||||
return True, f"流程已完成,最后状态: {current_state.name}"
|
||||
# 如果提供了参数,创建参数记录
|
||||
if parameters:
|
||||
create_parameter_record(state_log, **parameters)
|
||||
|
||||
return True, f"已完成状态: {current_state.name}"
|
||||
# 检查是否所有状态都已完成
|
||||
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:
|
||||
@@ -189,23 +192,24 @@ def get_current_state_parameters(business_object: 'models.BusinessObject') -> Li
|
||||
|
||||
def get_overall_status(business_object: 'models.BusinessObject') -> str:
|
||||
"""
|
||||
获取订单的整体状态
|
||||
获取业务对象的整体状态
|
||||
|
||||
返回值:
|
||||
- 'not_started': 未开始(没有任何有效的完成记录)
|
||||
- 'in_progress': 进行中(有部分状态已完成)
|
||||
- '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,说明所有状态都已完成
|
||||
# 检查是否有下一个待执行节点
|
||||
next_pending = get_next_pending_state(business_object, include_parameters=False)
|
||||
|
||||
if next_pending is None:
|
||||
# 没有待执行节点,说明所有状态都已完成
|
||||
return 'completed'
|
||||
|
||||
return 'in_progress'
|
||||
@@ -213,7 +217,7 @@ def get_overall_status(business_object: 'models.BusinessObject') -> str:
|
||||
|
||||
def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple[bool, str]:
|
||||
"""
|
||||
检查订单是否可以推进到下一个状态
|
||||
检查业务对象是否可以推进到下一个状态
|
||||
|
||||
返回: (是否可以推进, 原因)
|
||||
"""
|
||||
@@ -222,35 +226,27 @@ def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple
|
||||
if not first_node:
|
||||
return False, "流程没有任何节点"
|
||||
|
||||
current_state = get_business_object_current_state(business_object)
|
||||
# 获取下一个待执行节点
|
||||
next_pending = get_next_pending_state(business_object, include_parameters=False)
|
||||
|
||||
# 如果当前状态为 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, "流程已完成,无法继续推进"
|
||||
if next_pending is None:
|
||||
# 没有待执行节点,流程已完成
|
||||
return False, "流程已完成,无法继续推进"
|
||||
|
||||
# current_state 不为 None 时,它一定是第一个未完成的节点,可以推进
|
||||
return True, f"可以推进到: {current_state.name}"
|
||||
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' | 'in_progress' | 'completed' | 'cancelled',
|
||||
'business_object': 顺序号,
|
||||
'status': 'not_started' | 'completed' | 'cancelled',
|
||||
'order': 顺序号,
|
||||
'completed_at': 完成时间(如果已完成),
|
||||
'completed_by': 完成人(如果已完成),
|
||||
'cancelled_at': 撤销时间(如果已撤销),
|
||||
@@ -261,7 +257,6 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
||||
"""
|
||||
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 = {
|
||||
@@ -282,12 +277,6 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
||||
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
|
||||
@@ -306,3 +295,169 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user