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

332 lines
12 KiB
Python
Raw Permalink 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.
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from flower.common import ModelBase
from typing import List
class StateParameter(ModelBase):
"""StateParameter model representing parameters that can be shared across states."""
key = models.CharField(max_length=100, unique=True, verbose_name='参数键', help_text='唯一标识符')
value = models.CharField(max_length=200, blank=True, null=True, verbose_name='参数值')
attachment = models.FileField(upload_to='state_parameters/', null=True, blank=True, verbose_name='附件')
description = models.CharField(max_length=200, blank=True, verbose_name='参数描述')
is_required = models.BooleanField(default=False, verbose_name='是否必填', help_text='标记该参数是否必须提供')
is_image_path = models.BooleanField(default=False, verbose_name='是否图片路径', help_text='标记value字段是否为图片URL路径')
def __str__(self):
return self.key
class Meta:
verbose_name = '工艺参数'
verbose_name_plural = '工艺参数'
class State(ModelBase):
"""State model representing a state in a stateflow diagram."""
name = models.CharField(max_length=100, verbose_name='状态名称')
description = models.CharField(max_length=200, blank=True, verbose_name='状态描述')
parameters = models.ManyToManyField(
StateParameter,
blank=True,
related_name='states',
verbose_name='关联参数',
help_text='此状态使用的工艺参数'
)
def __str__(self):
return self.name
def add_additional_params(self, params: dict):
"""Add additional parameters to the state's data field."""
if not self.data:
self.data = {}
self.data.update(params)
self.save()
def get_additional_params(self) -> dict:
"""Retrieve additional parameters from the state's data field."""
return self.data if self.data else {}
def get_parameters(self, required_only: bool = False) -> List['StateParameter']:
"""
获取状态关联的参数列表
Args:
required_only: 是否只返回必填参数,默认 False 返回所有参数
Returns:
参数列表
"""
params = self.parameters.all()
if required_only:
params = params.filter(is_required=True)
return list(params)
class Meta:
verbose_name = '流程节点'
verbose_name_plural = '流程节点'
class Process(ModelBase):
"""Process model representing a stateflow process template."""
name = models.CharField(max_length=100, verbose_name='流程名称')
state_nodes = models.ManyToManyField(
State,
through='ProcessNode',
related_name='processes',
verbose_name='节点列表',
)
description = models.TextField(blank=True, verbose_name='流程描述')
def __str__(self):
return self.name
def replace_nodes(self, nodes: List[State]):
"""Replace all nodes with new ordered list."""
# 清空现有节点后按照传入顺序重建
self.process_nodes.all().delete()
for order, node in enumerate(nodes):
ProcessNode.objects.create(process=self, state=node, order=order)
def get_nodes(self) -> List[State]:
"""Retrieve the list of state nodes as State instances."""
process_nodes = self.process_nodes.select_related('state').order_by('order', 'id')
return [item.state for item in process_nodes]
class Meta:
verbose_name = '流程编排'
verbose_name_plural = '流程编排'
class ProcessNode(ModelBase):
"""Intermediate model describing ordered relation between Process and State."""
process = models.ForeignKey(Process, on_delete=models.CASCADE, related_name='process_nodes', verbose_name='流程')
state = models.ForeignKey(State, on_delete=models.PROTECT, related_name='state_process_nodes', verbose_name='节点')
order = models.PositiveIntegerField(default=0, verbose_name='顺序')
class Meta:
verbose_name = '流程节点关联'
verbose_name_plural = '流程节点关联'
ordering = ['order', 'id']
unique_together = ('process', 'order')
def __str__(self) -> str:
return f"{self.process.name} -> {self.state.name} ({self.order})"
class BusinessObject(ModelBase):
"""业务对象 - 代表流程的一个实例,通过日志跟踪进度"""
name = models.CharField(max_length=100, verbose_name='业务对象名称')
process = models.ForeignKey(Process, on_delete=models.PROTECT, related_name='business_objects', verbose_name='关联流程')
description = models.TextField(blank=True, verbose_name='描述')
# GenericForeignKey - 关联到任意模型(可选)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True, verbose_name='关联对象类型')
object_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='关联对象ID')
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return f"{self.name} ({self.process.name})"
def get_current_state(self) -> 'State':
"""
获取当前状态通过services推导行为可配置
根据 settings.STATEFLOW_CURRENT_STATE_MODE 决定返回:
- 'NEXT' (默认): 下一个待执行的节点
- 'LAST': 最后完成的节点
注意:推荐在新代码中直接使用 get_last_completed_state() 或 get_next_pending_state() 以明确语义
"""
from . import services
return services.get_business_object_current_state(self)
def get_last_completed_state(self) -> 'State':
"""
获取最后完成的状态
返回:最后一个已完成的状态节点,如果没有则返回 None
"""
from . import services
return services.get_last_completed_state(self)
def get_next_pending_state(self) -> 'State':
"""
获取下一个待执行的状态
返回:下一个待执行的状态节点,如果没有则返回 None
"""
from . import services
return services.get_next_pending_state_simple(self)
def get_completed_node_ids(self) -> List[int]:
"""获取已完成的节点ID列表"""
from . import services
return services.get_completed_node_ids(self)
def get_current_parameters(self) -> List['StateParameter']:
"""获取当前状态的参数"""
from . import services
return services.get_current_state_parameters(self)
def get_progress_percentage(self) -> float:
"""计算进度百分比"""
from . import services
return services.get_progress_percentage(self)
def get_state_status(self, state: 'State') -> str:
"""获取指定状态的状态"""
from . import services
return services.get_business_object_state_status(self, state)
def get_timeline(self) -> List[dict]:
"""获取状态时间线"""
from . import services
return services.get_business_object_state_timeline(self)
class Meta:
db_table = 'business_object'
verbose_name = '业务对象'
verbose_name_plural = '业务对象'
class StateFlowRecord(ModelBase):
"""状态流转记录"""
business_object = models.ForeignKey(BusinessObject, on_delete=models.CASCADE, related_name='state_logs', verbose_name='业务对象')
state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='状态')
completed_at = models.DateTimeField(auto_now_add=True, verbose_name='完成时间')
completed_by = models.ForeignKey('auth.User', on_delete=models.SET_NULL, null=True, verbose_name='操作人')
cancelled_at = models.DateTimeField(null=True, blank=True, verbose_name='撤销时间')
is_cancelled = models.BooleanField(default=False, verbose_name='是否已撤销')
def get_latest_parameter(self, key, default=None, include_cancelled=False):
"""
获取参数的最新值(最后一次提交的值)
参数:
key: 参数键
default: 默认值
include_cancelled: 是否包含已撤销状态的参数记录,默认 False
返回:
参数值或默认值
"""
# 如果状态已撤销且不包含已撤销记录,返回默认值
if self.is_cancelled and not include_cancelled:
return default
records = self.parameter_records.filter(
parameters__has_key=key
).order_by('-created_at')
if records.exists():
return records.first().parameters.get(key, default)
return default
def get_parameter_history(self, key, include_cancelled=False):
"""
获取参数的完整历史
参数:
key: 参数键
include_cancelled: 是否包含已撤销状态的参数记录,默认 False
返回:
list: 历史记录列表,每项包含 value, created_at, remark
"""
# 如果状态已撤销且不包含已撤销记录,返回空列表
if self.is_cancelled and not include_cancelled:
return []
records = self.parameter_records.filter(
parameters__has_key=key
).order_by('created_at')
history = []
for record in records:
history.append({
'value': record.parameters.get(key),
'created_at': record.created_at,
'remark': record.remark
})
return history
def get_all_parameters_summary(self, include_cancelled=False):
"""
获取所有参数的最新值汇总
参数:
include_cancelled: 是否包含已撤销状态的参数记录,默认 False
返回:
dict: {key: value} 每个key取最新的值
"""
# 如果状态已撤销且不包含已撤销记录,返回空字典
if self.is_cancelled and not include_cancelled:
return {}
summary = {}
# 按时间顺序,后面的记录覆盖前面的
for record in self.parameter_records.all().order_by('created_at'):
summary.update(record.parameters)
return summary
def get_all_parameter_records_count(self):
"""获取参数记录总数"""
return self.parameter_records.count()
class Meta:
db_table = 'state_flow_record'
verbose_name = '状态流转记录'
verbose_name_plural = '状态流转记录'
# 移除 unique_together 约束,允许同一状态多次记录(支持撤销后重新执行)
ordering = ['completed_at']
class StateLogParameterRecord(ModelBase):
"""
状态流转参数记录
使用 JSON 存储一次提交的所有参数
支持对同一状态流转多次补充参数
"""
state_log = models.ForeignKey(
StateFlowRecord,
on_delete=models.CASCADE,
related_name='parameter_records',
verbose_name='状态流转记录'
)
parameters = models.JSONField(
verbose_name='参数数据',
help_text='JSON格式存储参数例如: {"temperature": "25.5", "operator": "张三"}'
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
remark = models.TextField(blank=True, default='', verbose_name='备注')
def __str__(self):
return f"{self.state_log} - {len(self.parameters)} 个参数 ({self.created_at})"
def get_parameter(self, key, default=None):
"""快捷方式:获取单个参数值"""
return self.parameters.get(key, default)
def has_parameter(self, key):
"""快捷方式:检查参数是否存在"""
return key in self.parameters
def get_all_keys(self):
"""快捷方式:获取所有参数键"""
return list(self.parameters.keys())
class Meta:
db_table = 'state_log_parameter_record'
verbose_name = '状态流转参数记录'
verbose_name_plural = '状态流转参数记录'
ordering = ['created_at']
indexes = [
models.Index(fields=['state_log', 'created_at']),
]