1
0
forked from erp-dev/erp

feat: stateflow v2 (params required)

This commit is contained in:
2025-11-15 19:39:15 +08:00
parent 9ad404a365
commit 9c38e2ac09
24 changed files with 3670 additions and 547 deletions

View File

@@ -5,11 +5,34 @@ 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='状态名称')
# previous = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, 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
@@ -25,27 +48,26 @@ class State(ModelBase):
"""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 StateParameter(ModelBase):
"""StateParameter model representing parameters associated with a state."""
state = models.ForeignKey(State, on_delete=models.PROTECT, related_name='parameters', verbose_name='关联参数')
key = models.CharField(max_length=100, blank=True, null=True, verbose_name='参数键')
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='参数描述')
def __str__(self):
return f"{self.state.name} - {self.key}"
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='流程名称')
@@ -152,9 +174,132 @@ class StateFlowRecord(ModelBase):
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']),
]