YY968XX пре 5 месеци
родитељ
комит
373513b4a8

+ 2 - 0
Web/src/views/aidop/s8/api/s8ExceptionApi.ts

@@ -28,6 +28,8 @@ export interface S8ExceptionRow {
 	timeoutFlag: boolean;
 	timeoutFlag: boolean;
 	createdAt: string;
 	createdAt: string;
 	closedAt?: string | null;
 	closedAt?: string | null;
+	activeFlowInstanceId?: number | null;
+	activeFlowBizType?: string | null;
 }
 }
 
 
 export interface S8DecisionRow {
 export interface S8DecisionRow {

+ 14 - 16
Web/src/views/aidop/s8/exceptions/S8TaskDetailPage.vue

@@ -28,13 +28,7 @@ const imageEvidences = computed(() => evidences.value.filter((item) => imagePatt
 const fileEvidences = computed(() => evidences.value.filter((item) => !imagePattern.test(item.fileUrl || item.fileName)));
 const fileEvidences = computed(() => evidences.value.filter((item) => !imagePattern.test(item.fileUrl || item.fileName)));
 const currentStatus = computed(() => String(detail.value?.status || ''));
 const currentStatus = computed(() => String(detail.value?.status || ''));
 const hasActiveFlow = computed(() => !!detail.value?.activeFlowInstanceId);
 const hasActiveFlow = computed(() => !!detail.value?.activeFlowInstanceId);
-const activeBizType = computed(() => {
-	if (!hasActiveFlow.value) return '';
-	const lastAction = [...timeline.value].reverse().find(
-		(t) => t.actionCode === 'ESCALATE_START' || t.actionCode === 'CLOSURE_START',
-	);
-	return lastAction?.actionCode === 'ESCALATE_START' ? 'EXCEPTION_ESCALATION' : 'EXCEPTION_CLOSURE';
-});
+const activeBizType = computed(() => detail.value?.activeFlowBizType ?? '');
 const canClaim = computed(() => currentStatus.value === 'NEW');
 const canClaim = computed(() => currentStatus.value === 'NEW');
 const canTransfer = computed(() => !['', 'CLOSED'].includes(currentStatus.value));
 const canTransfer = computed(() => !['', 'CLOSED'].includes(currentStatus.value));
 const canUpgrade = computed(() => !hasActiveFlow.value && ['ASSIGNED', 'IN_PROGRESS'].includes(currentStatus.value));
 const canUpgrade = computed(() => !hasActiveFlow.value && ['ASSIGNED', 'IN_PROGRESS'].includes(currentStatus.value));
@@ -66,7 +60,8 @@ async function loadDetail() {
 }
 }
 
 
 async function loadEmployees() {
 async function loadEmployees() {
-	employees.value = (await s8ExceptionApi.employees({ factoryRefId: 1 })) as typeof employees.value;
+	const factoryRefId = Number(detail.value?.factoryId ?? 1);
+	employees.value = (await s8ExceptionApi.employees({ factoryRefId })) as typeof employees.value;
 }
 }
 
 
 function openAction(mode: string) {
 function openAction(mode: string) {
@@ -83,7 +78,7 @@ function actionTitle() {
 			transfer: '转派',
 			transfer: '转派',
 			upgrade: '升级',
 			upgrade: '升级',
 			reject: '驳回',
 			reject: '驳回',
-			close: '关闭',
+			close: '提交关闭申请',
 			comment: '补充说明',
 			comment: '补充说明',
 		}[dialogMode.value] ?? '动作'
 		}[dialogMode.value] ?? '动作'
 	);
 	);
@@ -133,7 +128,8 @@ async function submitAction() {
 }
 }
 
 
 onMounted(async () => {
 onMounted(async () => {
-	await Promise.all([loadDetail(), loadEmployees()]);
+	await loadDetail();
+	await loadEmployees();
 });
 });
 </script>
 </script>
 
 
@@ -162,12 +158,14 @@ onMounted(async () => {
 				<el-col :span="8">
 				<el-col :span="8">
 					<el-card shadow="never">
 					<el-card shadow="never">
 						<template #header>操作面板</template>
 						<template #header>操作面板</template>
-						<ApprovalPanel
-							v-if="hasActiveFlow"
-							:biz-type="activeBizType"
-							:biz-id="detail.id"
-							@completed="onApprovalCompleted"
-						/>
+							<ApprovalPanel
+								v-if="hasActiveFlow"
+								:biz-type="activeBizType"
+								:biz-id="detail.id"
+								@refresh="onApprovalCompleted"
+								@flow-started="onApprovalCompleted"
+								@flow-completed="onApprovalCompleted"
+							/>
 						<template v-else>
 						<template v-else>
 							<div class="action-grid">
 							<div class="action-grid">
 								<el-button type="primary" :disabled="!canClaim" @click="openAction('claim')">认领</el-button>
 								<el-button type="primary" :disabled="!canClaim" @click="openAction('claim')">认领</el-button>

+ 3 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S8/AdoS8Dtos.cs

@@ -24,6 +24,7 @@ public class AdoS8ExceptionQueryDto
 public class AdoS8ExceptionListItemDto
 public class AdoS8ExceptionListItemDto
 {
 {
     public long Id { get; set; }
     public long Id { get; set; }
+    public long FactoryId { get; set; }
     public string ExceptionCode { get; set; } = string.Empty;
     public string ExceptionCode { get; set; } = string.Empty;
     public string Title { get; set; } = string.Empty;
     public string Title { get; set; } = string.Empty;
     public string Status { get; set; } = string.Empty;
     public string Status { get; set; } = string.Empty;
@@ -55,6 +56,8 @@ public class AdoS8ExceptionDetailDto : AdoS8ExceptionListItemDto
     public string? ReporterName { get; set; }
     public string? ReporterName { get; set; }
     public DateTime? AssignedAt { get; set; }
     public DateTime? AssignedAt { get; set; }
     public DateTime? UpdatedAt { get; set; }
     public DateTime? UpdatedAt { get; set; }
+    public long? ActiveFlowInstanceId { get; set; }
+    public string? ActiveFlowBizType { get; set; }
 }
 }
 
 
 public class AdoS8ManualReportCreateDto
 public class AdoS8ManualReportCreateDto

+ 4 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S8/AdoS8Exception.cs

@@ -90,6 +90,10 @@ public class AdoS8Exception
     [SugarColumn(ColumnName = "active_flow_instance_id", IsNullable = true)]
     [SugarColumn(ColumnName = "active_flow_instance_id", IsNullable = true)]
     public long? ActiveFlowInstanceId { get; set; }
     public long? ActiveFlowInstanceId { get; set; }
 
 
+    /// <summary>当前进行中的审批业务类型(EXCEPTION_ESCALATION / EXCEPTION_CLOSURE)</summary>
+    [SugarColumn(ColumnName = "active_flow_biz_type", Length = 32, IsNullable = true)]
+    public string? ActiveFlowBizType { get; set; }
+
     [SugarColumn(ColumnName = "is_deleted", ColumnDataType = "boolean")]
     [SugarColumn(ColumnName = "is_deleted", ColumnDataType = "boolean")]
     public bool IsDeleted { get; set; }
     public bool IsDeleted { get; set; }
 
 

+ 15 - 5
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/ExceptionClosureBizHandler.cs

@@ -27,15 +27,17 @@ public class ExceptionClosureBizHandler : IFlowBizHandler, ITransient
     {
     {
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         e.ActiveFlowInstanceId = instanceId;
         e.ActiveFlowInstanceId = instanceId;
+        e.ActiveFlowBizType = BizType;
         e.UpdatedAt = DateTime.Now;
         e.UpdatedAt = DateTime.Now;
         await _rep.UpdateAsync(e);
         await _rep.UpdateAsync(e);
-        await InsertTimelineAsync(e.Id, "CLOSURE_START", "发起关闭确认", null, null, instanceId, null);
+        await InsertTimelineAsync(e.Id, "CLOSE_START", "发起关闭确认", null, null, instanceId, null);
     }
     }
 
 
     public async Task OnFlowCompleted(long bizId, long instanceId, FlowInstanceStatusEnum finalStatus, long? lastApproverId)
     public async Task OnFlowCompleted(long bizId, long instanceId, FlowInstanceStatusEnum finalStatus, long? lastApproverId)
     {
     {
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         e.ActiveFlowInstanceId = null;
         e.ActiveFlowInstanceId = null;
+        e.ActiveFlowBizType = null;
         e.UpdatedAt = DateTime.Now;
         e.UpdatedAt = DateTime.Now;
 
 
         if (finalStatus == FlowInstanceStatusEnum.Approved)
         if (finalStatus == FlowInstanceStatusEnum.Approved)
@@ -43,14 +45,21 @@ public class ExceptionClosureBizHandler : IFlowBizHandler, ITransient
             e.Status = "CLOSED";
             e.Status = "CLOSED";
             e.ClosedAt = DateTime.Now;
             e.ClosedAt = DateTime.Now;
             await _rep.UpdateAsync(e);
             await _rep.UpdateAsync(e);
-            await InsertTimelineAsync(e.Id, "CLOSURE_APPROVED", "关闭已确认", "RESOLVED", "CLOSED",
+            await InsertTimelineAsync(e.Id, "CLOSE_APPROVED", "关闭已确认", "RESOLVED", "CLOSED",
+                instanceId, lastApproverId);
+        }
+        else if (finalStatus == FlowInstanceStatusEnum.Cancelled)
+        {
+            // 撤回:状态维持 RESOLVED,不写终态
+            await _rep.UpdateAsync(e);
+            await InsertTimelineAsync(e.Id, "CLOSE_CANCELLED", "处理人撤回关闭申请", null, null,
                 instanceId, lastApproverId);
                 instanceId, lastApproverId);
         }
         }
         else
         else
         {
         {
             e.Status = "IN_PROGRESS";
             e.Status = "IN_PROGRESS";
             await _rep.UpdateAsync(e);
             await _rep.UpdateAsync(e);
-            await InsertTimelineAsync(e.Id, "CLOSURE_REJECTED", "关闭被驳回", "RESOLVED", "IN_PROGRESS",
+            await InsertTimelineAsync(e.Id, "CLOSE_REJECTED", "关闭被驳回", "RESOLVED", "IN_PROGRESS",
                 instanceId, lastApproverId);
                 instanceId, lastApproverId);
         }
         }
     }
     }
@@ -61,14 +70,14 @@ public class ExceptionClosureBizHandler : IFlowBizHandler, ITransient
         if (e == null) return new Dictionary<string, object>();
         if (e == null) return new Dictionary<string, object>();
         return new Dictionary<string, object>
         return new Dictionary<string, object>
         {
         {
-            ["sceneCode"] = e.SceneCode,
+            ["sceneCode"] = e.SceneCode ?? string.Empty,
+            ["factoryCode"] = e.FactoryId.ToString(),
         };
         };
     }
     }
 
 
     private async Task InsertTimelineAsync(long exceptionId, string code, string label,
     private async Task InsertTimelineAsync(long exceptionId, string code, string label,
         string? from, string? to, long? instanceId, long? approverId)
         string? from, string? to, long? instanceId, long? approverId)
     {
     {
-        // P4-17: 补齐审批人 ID 到 Timeline 留痕
         string? remark = null;
         string? remark = null;
         if (instanceId.HasValue && approverId.HasValue)
         if (instanceId.HasValue && approverId.HasValue)
             remark = $"审批实例ID: {instanceId},审批人: {approverId}";
             remark = $"审批实例ID: {instanceId},审批人: {approverId}";
@@ -84,6 +93,7 @@ public class ExceptionClosureBizHandler : IFlowBizHandler, ITransient
             ActionLabel = label,
             ActionLabel = label,
             FromStatus = from,
             FromStatus = from,
             ToStatus = to,
             ToStatus = to,
+            OperatorId = approverId,
             ActionRemark = remark,
             ActionRemark = remark,
             CreatedAt = DateTime.Now
             CreatedAt = DateTime.Now
         });
         });

+ 16 - 4
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/ExceptionEscalationBizHandler.cs

@@ -28,6 +28,7 @@ public class ExceptionEscalationBizHandler : IFlowBizHandler, ITransient
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         e.Status = "ESCALATED";
         e.Status = "ESCALATED";
         e.ActiveFlowInstanceId = instanceId;
         e.ActiveFlowInstanceId = instanceId;
+        e.ActiveFlowBizType = BizType;
         e.UpdatedAt = DateTime.Now;
         e.UpdatedAt = DateTime.Now;
         await _rep.UpdateAsync(e);
         await _rep.UpdateAsync(e);
         await InsertTimelineAsync(e.Id, "ESCALATE_START", "发起升级", null, "ESCALATED", instanceId, null);
         await InsertTimelineAsync(e.Id, "ESCALATE_START", "发起升级", null, "ESCALATED", instanceId, null);
@@ -37,15 +38,25 @@ public class ExceptionEscalationBizHandler : IFlowBizHandler, ITransient
     {
     {
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         var e = await _rep.GetByIdAsync(bizId) ?? throw new S8BizException("异常不存在");
         e.ActiveFlowInstanceId = null;
         e.ActiveFlowInstanceId = null;
+        e.ActiveFlowBizType = null;
         e.UpdatedAt = DateTime.Now;
         e.UpdatedAt = DateTime.Now;
 
 
         if (finalStatus == FlowInstanceStatusEnum.Approved)
         if (finalStatus == FlowInstanceStatusEnum.Approved)
         {
         {
             e.Status = "ASSIGNED";
             e.Status = "ASSIGNED";
+            if (lastApproverId.HasValue)
+                e.AssigneeId = lastApproverId.Value;
             await _rep.UpdateAsync(e);
             await _rep.UpdateAsync(e);
             await InsertTimelineAsync(e.Id, "ESCALATE_APPROVED", "升级已确认", "ESCALATED", "ASSIGNED",
             await InsertTimelineAsync(e.Id, "ESCALATE_APPROVED", "升级已确认", "ESCALATED", "ASSIGNED",
                 instanceId, lastApproverId);
                 instanceId, lastApproverId);
         }
         }
+        else if (finalStatus == FlowInstanceStatusEnum.Cancelled)
+        {
+            e.Status = "IN_PROGRESS";
+            await _rep.UpdateAsync(e);
+            await InsertTimelineAsync(e.Id, "ESCALATE_CANCELLED", "发起人撤回升级申请", "ESCALATED", "IN_PROGRESS",
+                instanceId, lastApproverId);
+        }
         else
         else
         {
         {
             e.Status = "IN_PROGRESS";
             e.Status = "IN_PROGRESS";
@@ -61,16 +72,16 @@ public class ExceptionEscalationBizHandler : IFlowBizHandler, ITransient
         if (e == null) return new Dictionary<string, object>();
         if (e == null) return new Dictionary<string, object>();
         return new Dictionary<string, object>
         return new Dictionary<string, object>
         {
         {
-            ["severity"] = e.Severity,
-            ["sceneCode"] = e.SceneCode,
-            ["priorityLevel"] = e.PriorityLevel,
+            ["severity"] = e.Severity ?? string.Empty,
+            ["sceneCode"] = e.SceneCode ?? string.Empty,
+            ["priorityLevel"] = e.PriorityLevel ?? string.Empty,
+            ["factoryCode"] = e.FactoryId.ToString(),
         };
         };
     }
     }
 
 
     private async Task InsertTimelineAsync(long exceptionId, string code, string label,
     private async Task InsertTimelineAsync(long exceptionId, string code, string label,
         string? from, string? to, long? instanceId, long? approverId)
         string? from, string? to, long? instanceId, long? approverId)
     {
     {
-        // P4-17: 补齐审批人 ID 到 Timeline 留痕
         string? remark = null;
         string? remark = null;
         if (instanceId.HasValue && approverId.HasValue)
         if (instanceId.HasValue && approverId.HasValue)
             remark = $"审批实例ID: {instanceId},审批人: {approverId}";
             remark = $"审批实例ID: {instanceId},审批人: {approverId}";
@@ -86,6 +97,7 @@ public class ExceptionEscalationBizHandler : IFlowBizHandler, ITransient
             ActionLabel = label,
             ActionLabel = label,
             FromStatus = from,
             FromStatus = from,
             ToStatus = to,
             ToStatus = to,
+            OperatorId = approverId,
             ActionRemark = remark,
             ActionRemark = remark,
             CreatedAt = DateTime.Now
             CreatedAt = DateTime.Now
         });
         });

+ 6 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8ExceptionService.cs

@@ -53,6 +53,7 @@ public class S8ExceptionService : ITransient
             .Select((e, sc) => new AdoS8ExceptionListItemDto
             .Select((e, sc) => new AdoS8ExceptionListItemDto
             {
             {
                 Id = e.Id,
                 Id = e.Id,
+                FactoryId = e.FactoryId,
                 ExceptionCode = e.ExceptionCode,
                 ExceptionCode = e.ExceptionCode,
                 Title = e.Title,
                 Title = e.Title,
                 Status = e.Status,
                 Status = e.Status,
@@ -112,6 +113,7 @@ public class S8ExceptionService : ITransient
             .Select((e, sc) => new AdoS8ExceptionDetailDto
             .Select((e, sc) => new AdoS8ExceptionDetailDto
             {
             {
                 Id = e.Id,
                 Id = e.Id,
+                FactoryId = e.FactoryId,
                 ExceptionCode = e.ExceptionCode,
                 ExceptionCode = e.ExceptionCode,
                 Title = e.Title,
                 Title = e.Title,
                 Description = e.Description,
                 Description = e.Description,
@@ -132,7 +134,9 @@ public class S8ExceptionService : ITransient
                 CreatedAt = e.CreatedAt,
                 CreatedAt = e.CreatedAt,
                 ClosedAt = e.ClosedAt,
                 ClosedAt = e.ClosedAt,
                 AssignedAt = e.AssignedAt,
                 AssignedAt = e.AssignedAt,
-                UpdatedAt = e.UpdatedAt
+                UpdatedAt = e.UpdatedAt,
+                ActiveFlowInstanceId = e.ActiveFlowInstanceId,
+                ActiveFlowBizType = e.ActiveFlowBizType
             })
             })
             .Take(1)
             .Take(1)
             .ToListAsync();
             .ToListAsync();
@@ -191,6 +195,7 @@ public class S8ExceptionService : ITransient
             }
             }
             else
             else
             {
             {
+                row.ResponsibleDeptName = deptMap.GetValueOrDefault(row.ResponsibleDeptId);
                 row.AssigneeName = row.AssigneeId.HasValue ? empMap.GetValueOrDefault(row.AssigneeId.Value) : null;
                 row.AssigneeName = row.AssigneeId.HasValue ? empMap.GetValueOrDefault(row.AssigneeId.Value) : null;
             }
             }
         }
         }