from django.contrib import admin from django.contrib.admin import action from django.utils.safestring import mark_safe from . import models @admin.register(models.StateParameter) class StateParameterAdmin(admin.ModelAdmin): """工艺参数管理""" list_display = ( 'id', 'key', 'value', 'is_required', 'is_image_path', 'state_list', 'description', 'attachment_preview', ) search_fields = ('key', 'description') list_filter = ('is_required', 'is_image_path', 'created_at', 'updated_at', 'states__processes__name') readonly_fields = ('attachment_preview_detail', 'value_preview') @admin.display(description='附件') def attachment_preview(self, obj): """列表中显示是否有附件""" if obj.attachment: return '有' return '无' @admin.display(description='所属节点') def state_list(self, obj): """显示关联的状态节点""" states = obj.states.all() if states: return mark_safe('
'.join([f"{state.name}" for state in states])) return '-' def attachment_preview_detail(self, obj): """详情页显示附件预览""" if obj.attachment: if obj.attachment.name.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')): return mark_safe(f'') else: return mark_safe(f'下载附件') return "-" attachment_preview_detail.short_description = '附件预览' def value_preview(self, obj): """如果标记为图片路径,则显示图片预览""" if obj.is_image_path and obj.value: # 假设 value 是图片 URL return mark_safe(f'') return obj.value or "-" value_preview.short_description = 'Value预览' class ProcessNodeInline(admin.StackedInline): model = models.ProcessNode extra = 1 autocomplete_fields = ('state',) ordering = ('order',) @admin.register(models.State) class StateAdmin(admin.ModelAdmin): list_display = ( 'id', 'name', 'description', 'created_at', 'updated_at', 'parameter_count', ) search_fields = ('name', 'description') list_filter = ('created_at', 'updated_at', 'processes__name') filter_horizontal = ('parameters',) @admin.display(description='参数数量') def parameter_count(self, obj: models.State): count = obj.parameters.count() if count > 0: params = obj.parameters.all()[:3] keys = ', '.join([p.key for p in params]) if count > 3: keys += f' 等{count}个' return f"{count} ({keys})" return "0" @admin.register(models.Process) class ProcessAdmin(admin.ModelAdmin): list_display = ( 'id', 'name', 'node_count', 'description', 'updated_at', ) search_fields = ('name', 'description') list_filter = ('created_at', 'updated_at') inlines = [ProcessNodeInline] @admin.display(description='节点数量') def node_count(self, obj: models.Process): return obj.process_nodes.count() class StateFlowRecordInline(admin.StackedInline): model = models.StateFlowRecord extra = 0 fields = ('state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at') readonly_fields = ('completed_at', 'completed_by', 'cancelled_at') can_delete = False ordering = ('completed_at',) @admin.register(models.BusinessObject) class BusinessObjectAdmin(admin.ModelAdmin): list_display = ( 'id', 'name', 'process', 'content_object_display', 'current_state_display', 'progress', 'params', 'updated_at', ) search_fields = ('name', 'description') list_filter = ('process', 'created_at', 'updated_at') actions = ['advance_to_next_state_action', 'step_back_one_state_action', 'reset_progress_action'] autocomplete_fields = ('process',) inlines = [StateFlowRecordInline] fieldsets = [ ('基本信息', { 'fields': ['name', 'process', 'description'] }), ('关联对象(可选)', { 'fields': ['content_type', 'object_id'], 'description': '可选择关联到实际的业务对象,如订单、工单等' }), ] @admin.display(description='关联对象') def content_object_display(self, obj: models.BusinessObject): """显示关联对象""" if obj.content_object: return f"{obj.content_type} #{obj.object_id}" return "-" @action(description='前进到下一个状态') def advance_to_next_state_action(self, request, queryset): from . import services success_count = 0 messages = [] for obj in queryset: success, message, _ = services.advance_to_next_state(obj, request.user) if success: success_count += 1 messages.append(f"{obj.name}: {message}") self.message_user(request, f'成功推进 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}') @action(description='回退一步') def step_back_one_state_action(self, request, queryset): from . import services success_count = 0 messages = [] for obj in queryset: success, message = services.step_back_one_state(obj, request.user) if success: success_count += 1 messages.append(f"{obj.name}: {message}") self.message_user(request, f'成功回退 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}') @action(description='重置进度') def reset_progress_action(self, request, queryset): from . import services for obj in queryset: services.reset_business_object_progress(obj) self.message_user(request, f'已重置 {queryset.count()} 个业务对象的进度。') @admin.display(description='当前状态', ordering='process') def current_state_display(self, obj: models.BusinessObject): from django.conf import settings from . import services overall_status = services.get_overall_status(obj) mode = getattr(settings, 'STATEFLOW_CURRENT_STATE_MODE', 'NEXT') if overall_status == 'completed': return '已完成' # 进行中状态 current_state = obj.get_current_state() if current_state: if mode == 'NEXT': return f'进行中 (下一步: {current_state.name})' else: # LAST return f'进行中 (已完成: {current_state.name})' return '-' @admin.display(description='进度') def progress(self, obj: models.BusinessObject): return f"{obj.get_progress_percentage():.1f}%" @admin.display(description='当前状态参数') def params(self, obj: models.BusinessObject): """显示当前状态的参数(根据配置模式决定是最后完成的还是下一个待执行的)""" current_state = obj.get_current_state() if current_state: params = current_state.parameters.all() if params: return ", ".join([f"{param.key}={param.value}" for param in params]) return "-" class StateLogParameterRecordInline(admin.TabularInline): """状态流转参数记录内联显示""" model = models.StateLogParameterRecord extra = 0 fields = ('parameters', 'remark', 'created_at') readonly_fields = ('created_at',) can_delete = False @admin.register(models.StateFlowRecord) class StateFlowRecordAdmin(admin.ModelAdmin): list_display = ('id', 'business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at', 'parameter_count') search_fields = ('business_object__name', 'state__name') list_filter = ('state', 'is_cancelled', 'completed_at', 'completed_by') readonly_fields = ('business_object', 'state', 'completed_at', 'completed_by', 'cancelled_at', 'parameters_summary') fields = ('business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at', 'parameters_summary') autocomplete_fields = ('business_object', 'state', 'completed_by') inlines = [StateLogParameterRecordInline] @admin.display(description='参数记录数') def parameter_count(self, obj): count = obj.parameter_records.count() return f"{count} 条" if count > 0 else "-" @admin.display(description='参数汇总') def parameters_summary(self, obj): """显示所有参数的最新值汇总""" summary = obj.get_all_parameters_summary(include_cancelled=True) if summary: items = [f"{k}: {v}" for k, v in list(summary.items())[:5]] if len(summary) > 5: items.append(f"... 共{len(summary)}个参数") return mark_safe('
'.join(items)) return "-" @admin.register(models.ProcessNode) class ProcessNodeAdmin(admin.ModelAdmin): """流程节点关联管理""" list_display = ('id', 'process', 'state', 'order', 'created_at') search_fields = ('process__name', 'state__name') list_filter = ('process', 'created_at') autocomplete_fields = ('process', 'state') list_editable = ('order',) ordering = ('process', 'order') fieldsets = [ ('关联信息', { 'fields': ['process', 'state', 'order'] }), ] @admin.register(models.StateLogParameterRecord) class StateLogParameterRecordAdmin(admin.ModelAdmin): """状态流转参数记录管理""" list_display = ('id', 'state_log', 'parameter_keys', 'remark_preview', 'created_at') search_fields = ('state_log__business_object__name', 'state_log__state__name', 'remark') list_filter = ('created_at', 'state_log__state') readonly_fields = ('created_at', 'parameter_preview') autocomplete_fields = ('state_log',) fieldsets = [ ('关联信息', { 'fields': ['state_log'] }), ('参数数据', { 'fields': ['parameters', 'parameter_preview', 'remark'] }), ('时间信息', { 'fields': ['created_at'] }), ] @admin.display(description='参数键') def parameter_keys(self, obj): """显示参数键列表""" keys = obj.get_all_keys() if keys: preview = ', '.join(keys[:5]) if len(keys) > 5: preview += f' 等{len(keys)}个' return preview return "-" @admin.display(description='备注') def remark_preview(self, obj): """显示备注预览""" if obj.remark: return obj.remark[:50] + ('...' if len(obj.remark) > 50 else '') return "-" @admin.display(description='参数详情') def parameter_preview(self, obj): """格式化显示参数详情""" if obj.parameters: items = [] for key, value in obj.parameters.items(): # 如果值太长,截断显示 value_str = str(value) if len(value_str) > 100: value_str = value_str[:100] + '...' items.append(f"{key}: {value_str}") return mark_safe('
'.join(items)) return "-"