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']), ]