Browse Source

feat: 任务调度增加 Http 请求方式

破坏性变更,SysJobDetail 移除 CreateFromScript 字段,增加 CreateType
许俊杰 3 years ago
parent
commit
d4fc8efe5e

+ 3 - 3
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -21,9 +21,9 @@
     <PackageReference Include="DotNetCore.Compile.Environment" Version="3.2.0" />
     <PackageReference Include="DotNetCore.Natasha.CSharp" Version="5.1.0" />
     <PackageReference Include="FluentEmail.Smtp" Version="3.0.2" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.7.19" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.7.19" />
-    <PackageReference Include="Furion.Pure" Version="4.8.7.19" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.7.24" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.7.24" />
+    <PackageReference Include="Furion.Pure" Version="4.8.7.24" />
     <PackageReference Include="IPTools.China" Version="1.6.0" />
     <PackageReference Include="Lazy.Captcha.Core" Version="2.0.3" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.7.4.3" />

+ 3 - 3
Admin.NET/Admin.NET.Core/Entity/SysJobDetail.cs

@@ -67,10 +67,10 @@ public class SysJobDetail : EntityBaseId
     public DateTime? UpdatedTime { get; set; }
 
     /// <summary>
-    /// 是否为脚本创建的作业
+    /// 作业创建类型
     /// </summary>
-    [SugarColumn(ColumnDescription = "是否为脚本创建的作业")]
-    public bool CreateFromScript { get; set; } = false;
+    [SugarColumn(ColumnDescription = "作业创建类型")]
+    public JobCreateTypeEnum CreateType { get; set; } = JobCreateTypeEnum.BuiltIn;
 
     /// <summary>
     /// 脚本代码

+ 26 - 0
Admin.NET/Admin.NET.Core/Enum/JobCreateTypeEnum.cs

@@ -0,0 +1,26 @@
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 作业创建类型枚举
+/// </summary>
+[Description("作业创建类型枚举")]
+public enum JobCreateTypeEnum
+{
+    /// <summary>
+    /// 内置
+    /// </summary>
+    [Description("内置")]
+    BuiltIn = 0,
+
+    /// <summary>
+    /// 脚本
+    /// </summary>
+    [Description("脚本")]
+    Script = 1,
+
+    /// <summary>
+    /// HTTP请求
+    /// </summary>
+    [Description("HTTP请求")]
+    Http = 2,
+}

+ 15 - 3
Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs

@@ -65,11 +65,23 @@ public class DbJobPersistence : IJobPersistence
         }
 
         // 获取数据库所有通过脚本创建的作业
-        var allDbScriptJobs = jobRepository.GetList(u => u.CreateFromScript);
+        var allDbScriptJobs = jobRepository.GetList(u => u.CreateType != JobCreateTypeEnum.BuiltIn);
         foreach (var dbDetail in allDbScriptJobs)
         {
             // 动态创建作业
-            var jobType = dynamicJobCompiler.BuildJob(dbDetail.ScriptCode);
+            Type jobType;
+            switch (dbDetail.CreateType)
+            {
+                case JobCreateTypeEnum.Script:
+                    jobType = dynamicJobCompiler.BuildJob(dbDetail.ScriptCode);
+                    break;
+                case JobCreateTypeEnum.Http:
+                    jobType = typeof(HttpJob);
+                    break;
+                default:
+                    throw new NotSupportedException();
+            }
+
             var jobBuilder = JobBuilder.Create(jobType).LoadFrom(dbDetail);
 
             // 强行设置为不扫描 IJob 实现类 [Trigger] 特性触发器,否则 SchedulerBuilder.Create 会再次扫描,导致重复添加同名触发器
@@ -116,7 +128,7 @@ public class DbJobPersistence : IJobPersistence
                 break;
 
             case PersistenceBehavior.Updated:
-                jobRepository.AsUpdateable(jobDetail).WhereColumns(u => new { u.JobId }).IgnoreColumns(u => new { u.Id, u.CreateFromScript, u.ScriptCode }).ExecuteCommand();
+                jobRepository.AsUpdateable(jobDetail).WhereColumns(u => new { u.JobId }).IgnoreColumns(u => new { u.Id, u.CreateType, u.ScriptCode }).ExecuteCommand();
                 break;
 
             case PersistenceBehavior.Removed:

+ 26 - 13
Admin.NET/Admin.NET.Core/Service/Job/SysJobService.cs

@@ -1,4 +1,6 @@
-namespace Admin.NET.Core.Service;
+using Furion.Schedule;
+
+namespace Admin.NET.Core.Service;
 
 /// <summary>
 /// 系统作业任务服务
@@ -67,17 +69,28 @@ public class SysJobService : IDynamicApiController, ITransient
         if (isExist)
             throw Oops.Oh(ErrorCodeEnum.D1006);
 
-        if (string.IsNullOrEmpty(input.ScriptCode))
-            throw Oops.Oh(ErrorCodeEnum.D1701);
-
-        input.CreateFromScript = true;//确保为true
         // 动态创建作业
-        var jobType = _dynamicJobCompiler.BuildJob(input.ScriptCode);
-
-        if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute)
-            throw Oops.Oh(ErrorCodeEnum.D1702);
-        if (jobDetailAttribute.JobId != input.JobId)
-            throw Oops.Oh(ErrorCodeEnum.D1703);
+        Type jobType;
+        switch (input.CreateType)
+        {
+            case JobCreateTypeEnum.Script when string.IsNullOrEmpty(input.ScriptCode):
+                throw Oops.Oh(ErrorCodeEnum.D1701);
+            case JobCreateTypeEnum.Script:
+                {
+                    jobType = _dynamicJobCompiler.BuildJob(input.ScriptCode);
+
+                    if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute)
+                        throw Oops.Oh(ErrorCodeEnum.D1702);
+                    if (jobDetailAttribute.JobId != input.JobId)
+                        throw Oops.Oh(ErrorCodeEnum.D1703);
+                    break;
+                }
+            case JobCreateTypeEnum.Http:
+                jobType = typeof(HttpJob);
+                break;
+            default:
+                throw new NotSupportedException();
+        }
 
         _schedulerFactory.AddJob(
             JobBuilder.Create(jobType)
@@ -86,7 +99,7 @@ public class SysJobService : IDynamicApiController, ITransient
         // 延迟一下等待持久化写入,再执行其他字段的更新
         await Task.Delay(500);
         await _sysJobDetailRep.AsUpdateable()
-            .SetColumns(u => new SysJobDetail { CreateFromScript = input.CreateFromScript, ScriptCode = input.ScriptCode })
+            .SetColumns(u => new SysJobDetail { CreateType = input.CreateType, ScriptCode = input.ScriptCode })
             .Where(u => u.JobId == input.JobId).ExecuteCommandAsync();
     }
 
@@ -110,7 +123,7 @@ public class SysJobService : IDynamicApiController, ITransient
         var oldScriptCode = sysJobDetail.ScriptCode;//旧脚本代码
         input.Adapt(sysJobDetail);
 
-        if (input.CreateFromScript)
+        if (input.CreateType == JobCreateTypeEnum.Script)
         {
             if (string.IsNullOrEmpty(input.ScriptCode))
                 throw Oops.Oh(ErrorCodeEnum.D1701);

+ 4 - 3
Web/src/api-services/models/add-job-detail-input.ts

@@ -11,6 +11,7 @@
  * https://github.com/swagger-api/swagger-codegen.git
  * Do not edit the class manually.
  */
+import { JobCreateTypeEnum } from './job-create-type-enum';
 /**
  * 
  * @export
@@ -72,11 +73,11 @@ export interface AddJobDetailInput {
      */
     updatedTime?: Date | null;
     /**
-     * 是否为脚本创建的作业
-     * @type {boolean}
+     * 
+     * @type {JobCreateTypeEnum}
      * @memberof AddJobDetailInput
      */
-    createFromScript?: boolean;
+    createType?: JobCreateTypeEnum;
     /**
      * 脚本代码
      * @type {string}

+ 1 - 0
Web/src/api-services/models/index.ts

@@ -123,6 +123,7 @@ export * from './gen-auth-url-input';
 export * from './gender-enum';
 export * from './iaction-result';
 export * from './jtoken';
+export * from './job-create-type-enum';
 export * from './job-detail-input';
 export * from './job-output';
 export * from './job-trigger-input';

+ 24 - 0
Web/src/api-services/models/job-create-type-enum.ts

@@ -0,0 +1,24 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * 所有接口
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 作业创建类型枚举<br />&nbsp;内置 BuiltIn = 0<br />&nbsp;脚本 Script = 1<br />&nbsp;HTTP Http = 2<br />
+ * @export
+ * @enum {string}
+ */
+export enum JobCreateTypeEnum {
+    NUMBER_0 = 0,
+    NUMBER_1 = 1,
+    NUMBER_2 = 2
+}
+

+ 4 - 3
Web/src/api-services/models/sys-job-detail.ts

@@ -11,6 +11,7 @@
  * https://github.com/swagger-api/swagger-codegen.git
  * Do not edit the class manually.
  */
+import { JobCreateTypeEnum } from './job-create-type-enum';
 /**
  * 系统作业信息表
  * @export
@@ -78,11 +79,11 @@ export interface SysJobDetail {
      */
     updatedTime?: Date | null;
     /**
-     * 是否为脚本创建的作业
-     * @type {boolean}
+     * 
+     * @type {JobCreateTypeEnum}
      * @memberof SysJobDetail
      */
-    createFromScript?: boolean;
+    createType?: JobCreateTypeEnum;
     /**
      * 脚本代码
      * @type {string}

+ 4 - 3
Web/src/api-services/models/update-job-detail-input.ts

@@ -11,6 +11,7 @@
  * https://github.com/swagger-api/swagger-codegen.git
  * Do not edit the class manually.
  */
+import { JobCreateTypeEnum } from './job-create-type-enum';
 /**
  * 
  * @export
@@ -72,11 +73,11 @@ export interface UpdateJobDetailInput {
      */
     updatedTime?: Date | null;
     /**
-     * 是否为脚本创建的作业
-     * @type {boolean}
+     * 
+     * @type {JobCreateTypeEnum}
      * @memberof UpdateJobDetailInput
      */
-    createFromScript?: boolean;
+    createType?: JobCreateTypeEnum;
     /**
      * 脚本代码
      * @type {string}

+ 7 - 0
Web/src/types/views.d.ts

@@ -342,3 +342,10 @@ declare type TableDemoState = {
 		defaultSort: EmptyObjectType;
 	};
 };
+
+// HTTP 作业消息(字段定义来源 Furion 的 HttpJobMessage)
+interface HttpJobMessage {
+	requestUri?: string | null;
+	httpMethod?: string | null;
+	body?: string | null;
+}

+ 99 - 11
Web/src/views/system/job/component/editJobDetail.vue

@@ -11,16 +11,25 @@
 				<el-tab-pane label="作业信息">
 					<el-form :model="state.ruleForm" ref="ruleFormRef" size="default" label-width="130px">
 						<el-row :gutter="35">
-							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="作业编号" prop="jobId" :rules="[{ required: true, message: '作业编号不能为空', trigger: 'blur' }]">
 									<el-input v-model="state.ruleForm.jobId" placeholder="作业编号" :disabled="isEdit" clearable />
 								</el-form-item>
 							</el-col>
-							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="组名称" prop="groupName" :rules="[{ required: true, message: '组名称不能为空', trigger: 'blur' }]">
 									<el-input v-model="state.ruleForm.groupName" placeholder="组名称" clearable />
 								</el-form-item>
 							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+								<el-form-item label="创建类型" prop="createType">
+									<el-radio-group v-model="state.ruleForm.createType" :disabled="isEdit">
+										<el-radio :label="JobCreateTypeEnum.NUMBER_0" v-show="isEdit">内置</el-radio>
+										<el-radio :label="JobCreateTypeEnum.NUMBER_1">脚本</el-radio>
+										<el-radio :label="JobCreateTypeEnum.NUMBER_2">Http请求</el-radio>
+									</el-radio-group>
+								</el-form-item>
+							</el-col>
 							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
 								<el-form-item label="执行方式" prop="concurrent">
 									<el-radio-group v-model="state.ruleForm.concurrent">
@@ -29,7 +38,7 @@
 									</el-radio-group>
 								</el-form-item>
 							</el-col>
-							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" v-show="!isEdit">
+							<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" v-show="!isEdit && !isHttpCreateType">
 								<el-form-item prop="includeAnnotations">
 									<template v-slot:label>
 										<div>
@@ -46,19 +55,39 @@
 								</el-form-item>
 							</el-col>
 							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+								<el-form-item label="描述信息" prop="description">
+									<el-input v-model="state.ruleForm.description" placeholder="描述信息" clearable type="textarea" :autosize="{ minRows: 1, maxRows: 3 }" />
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-if="!isHttpCreateType">
 								<el-form-item label="额外数据" prop="properties">
-									<el-input v-model="state.ruleForm.properties" placeholder="额外数据" clearable type="textarea" />
+									<el-input v-model="state.ruleForm.properties" placeholder="额外数据" clearable type="textarea" :autosize="{ minRows: 3, maxRows: 6 }" />
 								</el-form-item>
 							</el-col>
-							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
-								<el-form-item label="描述信息" prop="description">
-									<el-input v-model="state.ruleForm.description" placeholder="描述信息" clearable type="textarea" />
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-if="isHttpCreateType">
+								<el-form-item label="请求地址" prop="requestUri">
+									<el-input v-model="state.httpJobMessage.requestUri" placeholder="请求地址" clearable />
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-if="isHttpCreateType">
+								<el-form-item label="请求方法" prop="httpMethod">
+									<el-radio-group v-model="state.httpJobMessage.httpMethod">
+										<el-radio :label="httpMethodDef.get">Get</el-radio>
+										<el-radio :label="httpMethodDef.post">Post</el-radio>
+										<el-radio :label="httpMethodDef.put">Put</el-radio>
+										<el-radio :label="httpMethodDef.delete">Delete</el-radio>
+									</el-radio-group>
+								</el-form-item>
+							</el-col>
+							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" v-if="isHttpCreateType">
+								<el-form-item label="请求报文体" prop="body">
+									<el-input v-model="state.httpJobMessage.body" placeholder="请求报文体" clearable type="textarea" :autosize="{ minRows: 3, maxRows: 6 }" />
 								</el-form-item>
 							</el-col>
 						</el-row>
 					</el-form>
 				</el-tab-pane>
-				<el-tab-pane label="脚本代码" :disabled="!state.ruleForm.createFromScript">
+				<el-tab-pane label="脚本代码" :disabled="!isScriptCreateType">
 					<div ref="monacoEditorRef" style="width: 100%; height: 500px"></div>
 				</el-tab-pane>
 			</el-tabs>
@@ -80,7 +109,16 @@ import { JobScriptCode } from './JobScriptCode';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysJobApi } from '/@/api-services/api';
-import { UpdateJobDetailInput } from '/@/api-services/models';
+import { JobCreateTypeEnum, UpdateJobDetailInput } from '/@/api-services/models';
+
+// HttpMethod 定义,来源后端 HttpMethod 对象的序列化
+// 下面定义内容【不要】加空格,否则 getHttpJobMessage 中 JSON.stringify(httpJobMessageNet.HttpMethod) 后无法匹配
+const httpMethodDef = {
+	get: '{"Method":"GET"}',
+	post: '{"Method":"POST"}',
+	put: '{"Method":"PUT"}',
+	delete: '{"Method":"DELETE"}',
+};
 
 const props = defineProps({
 	title: String,
@@ -93,6 +131,7 @@ const state = reactive({
 	selectedTabName: '0', // 选中的 tab 页
 	ruleForm: {} as UpdateJobDetailInput,
 	monacoEditor: null as any,
+	httpJobMessage: { requestUri: '', httpMethod: httpMethodDef.get, body: '' } as HttpJobMessage,
 });
 
 // 是否编辑状态
@@ -100,6 +139,16 @@ const isEdit = computed(() => {
 	return state.ruleForm.id != undefined && state.ruleForm.id > 0;
 });
 
+// 是否脚本创建类型
+const isScriptCreateType = computed(() => {
+	return state.ruleForm.createType === JobCreateTypeEnum.NUMBER_1;
+});
+
+// 是否Http请求创建类型
+const isHttpCreateType = computed(() => {
+	return state.ruleForm.createType === JobCreateTypeEnum.NUMBER_2;
+});
+
 // 初始化monacoEditor对象
 var monacoEditor: any = null;
 const initMonacoEditor = () => {
@@ -135,6 +184,11 @@ const openDialog = (row: any) => {
 	state.ruleForm = JSON.parse(JSON.stringify(row));
 	state.isShowDialog = true;
 
+	// Http请求
+	if (state.ruleForm.createType === JobCreateTypeEnum.NUMBER_2) {
+		state.httpJobMessage = getHttpJobMessage(state.ruleForm.properties + '');
+	}
+
 	// 延迟拿值防止取不到
 	setTimeout(() => {
 		if (monacoEditor == null) initMonacoEditor();
@@ -157,7 +211,29 @@ const cancel = () => {
 const submit = () => {
 	ruleFormRef.value.validate(async (valid: boolean) => {
 		if (!valid) return;
-		state.ruleForm.scriptCode = monacoEditor.getValue();
+
+		// 脚本创建类型
+		if (state.ruleForm.createType === JobCreateTypeEnum.NUMBER_1) {
+			state.ruleForm.scriptCode = monacoEditor.getValue();
+		} else {
+			state.ruleForm.scriptCode = '';
+		}
+
+		// Http请求创建类型
+		if (state.ruleForm.createType === JobCreateTypeEnum.NUMBER_2) {
+			// 将 httpJobMessage 重新封装,按后端 HttpJob 序列化要求,字段要大写开头
+			// HttpJob 约定读取属性为“HttpJob”的值
+			const httpJobPropValue = JSON.stringify({
+				RequestUri: state.httpJobMessage.requestUri,
+				HttpMethod: JSON.parse(state.httpJobMessage.httpMethod + ''),
+				Body: state.httpJobMessage.body,
+				ClientName: 'HttpJob',
+				EnsureSuccessStatusCode: true,
+			});
+			const prop = { HttpJob: httpJobPropValue };
+			state.ruleForm.properties = JSON.stringify(prop);
+		}
+
 		if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
 			await getAPI(SysJobApi).apiSysJobUpdateJobDetailPost(state.ruleForm);
 		} else {
@@ -167,6 +243,18 @@ const submit = () => {
 	});
 };
 
+// 根据任务属性获取 HttpJobMessage
+const getHttpJobMessage = (properties: string): HttpJobMessage => {
+	const propData = JSON.parse(properties);
+	const httpJobMessageNet = JSON.parse(propData['HttpJob']); // 后端大写开头的 HttpJobMessage
+
+	return {
+		requestUri: httpJobMessageNet.RequestUri,
+		httpMethod: JSON.stringify(httpJobMessageNet.HttpMethod),
+		body: httpJobMessageNet.Body,
+	};
+};
+
 // 导出对象
-defineExpose({ openDialog });
+defineExpose({ httpMethodDef, openDialog, getHttpJobMessage });
 </script>

+ 95 - 45
Web/src/views/system/job/index.vue

@@ -44,28 +44,28 @@
 			<el-table :data="state.jobData" style="width: 100%" v-loading="state.loading" border>
 				<el-table-column type="expand" fixed>
 					<template #default="scope">
-						<el-table style="margin-left: 48px; width: calc(100% - 48px)" :data="scope.row.jobTriggers" border size="small">
+						<el-table style="margin-left: 48px; width: calc(100% - 48px)" :data="(scope.row as JobOutput).jobTriggers" border size="small">
 							<el-table-column type="index" label="序号" width="55" align="center" fixed />
 							<el-table-column prop="triggerId" label="触发器编号" width="120" fixed show-overflow-tooltip />
 							<el-table-column prop="triggerType" label="类型" show-overflow-tooltip />
-							<el-table-column prop="assemblyName" label="程序集" show-overflow-tooltip />
+							<!-- <el-table-column prop="assemblyName" label="程序集" show-overflow-tooltip /> -->
 							<el-table-column prop="args" label="参数" show-overflow-tooltip />
 							<el-table-column prop="description" label="描述" width="120" show-overflow-tooltip />
 							<el-table-column prop="status" label="状态" width="100" align="center" show-overflow-tooltip>
 								<template #default="scope">
-									<el-tag type="warning" effect="plain" v-if="scope.row.status == 0"> 积压 </el-tag>
-									<el-tag type="" effect="plain" v-if="scope.row.status == 1"> 就绪 </el-tag>
-									<el-tag type="success" effect="plain" v-if="scope.row.status == 2"> 正在运行 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 3"> 暂停 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 4"> 阻塞 </el-tag>
-									<el-tag type="" effect="plain" v-if="scope.row.status == 5"> 由失败进入就绪 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 6"> 归档 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 7"> 崩溃 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 8"> 超限 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 9"> 无触发时间 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 10"> 未启动 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 11"> 未知作业触发器 </el-tag>
-									<el-tag type="danger" effect="plain" v-if="scope.row.status == 12"> 未知作业处理程序 </el-tag>
+									<el-tag type="warning" effect="plain" v-if="(scope.row as SysJobTrigger).status == 0"> 积压 </el-tag>
+									<el-tag type="" effect="plain" v-if="(scope.row as SysJobTrigger).status == 1"> 就绪 </el-tag>
+									<el-tag type="success" effect="plain" v-if="(scope.row as SysJobTrigger).status == 2"> 正在运行 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 3"> 暂停 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 4"> 阻塞 </el-tag>
+									<el-tag type="" effect="plain" v-if="(scope.row as SysJobTrigger).status == 5"> 由失败进入就绪 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 6"> 归档 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 7"> 崩溃 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 8"> 超限 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 9"> 无触发时间 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 10"> 未启动 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 11"> 未知作业触发器 </el-tag>
+									<el-tag type="danger" effect="plain" v-if="(scope.row as SysJobTrigger).status == 12"> 未知作业处理程序 </el-tag>
 								</template>
 							</el-table-column>
 							<el-table-column prop="startTime" label="起始时间" width="100" align="center" show-overflow-tooltip />
@@ -80,19 +80,19 @@
 							<el-table-column prop="retryTimeout" label="重试间隔ms" width="100" align="center" show-overflow-tooltip />
 							<el-table-column prop="startNow" label="是否立即启动" width="100" align="center" show-overflow-tooltip>
 								<template #default="scope">
-									<el-tag v-if="scope.row.startNow == true"> 是 </el-tag>
+									<el-tag v-if="(scope.row as SysJobTrigger).startNow == true"> 是 </el-tag>
 									<el-tag type="info" v-else> 否 </el-tag>
 								</template>
 							</el-table-column>
 							<el-table-column prop="runOnStart" label="是否启动时执行一次" width="150" align="center" show-overflow-tooltip>
 								<template #default="scope">
-									<el-tag v-if="scope.row.runOnStart == true"> 是 </el-tag>
+									<el-tag v-if="(scope.row as SysJobTrigger).runOnStart == true"> 是 </el-tag>
 									<el-tag type="info" v-else> 否 </el-tag>
 								</template>
 							</el-table-column>
 							<el-table-column prop="resetOnlyOnce" label="是否重置触发次数" width="120" align="center" show-overflow-tooltip>
 								<template #default="scope">
-									<el-tag v-if="scope.row.resetOnlyOnce == true"> 是 </el-tag>
+									<el-tag v-if="(scope.row as SysJobTrigger).resetOnlyOnce == true"> 是 </el-tag>
 									<el-tag type="info" v-else> 否 </el-tag>
 								</template>
 							</el-table-column>
@@ -117,37 +117,60 @@
 					</template>
 				</el-table-column>
 				<el-table-column type="index" label="序号" width="55" align="center" fixed />
-				<el-table-column prop="jobDetail.jobId" label="作业编号" width="150" fixed>
+				<el-table-column prop="jobDetail.jobId" label="作业编号" width="180" fixed>
 					<template #default="scope">
 						<div style="display: flex; align-items: center">
 							<el-icon><timer /></el-icon>
-							<span style="margin-left: 5px">{{ scope.row.jobDetail.jobId }}</span>
+							<span style="margin-left: 5px">{{ (scope.row as JobOutput).jobDetail?.jobId }}</span>
 						</div>
 					</template>
 				</el-table-column>
-				<el-table-column prop="jobDetail.groupName" label="组名称" show-overflow-tooltip />
+				<el-table-column prop="jobDetail.groupName" label="组名称" width="100" show-overflow-tooltip />
 				<el-table-column prop="jobDetail.jobType" label="类型" show-overflow-tooltip />
-				<el-table-column prop="jobDetail.assemblyName" label="程序集" show-overflow-tooltip />
+				<!-- <el-table-column prop="jobDetail.assemblyName" label="程序集" show-overflow-tooltip /> -->
 				<el-table-column prop="jobDetail.description" label="描述" show-overflow-tooltip />
-				<el-table-column prop="jobDetail.concurrent" label="执行方式" width="100" align="center" show-overflow-tooltip>
+				<el-table-column prop="jobDetail.concurrent" label="执行方式" width="90" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-tag type="success" v-if="scope.row.jobDetail.concurrent == true"> 并行 </el-tag>
+						<el-tag type="success" v-if="(scope.row as JobOutput).jobDetail?.concurrent == true"> 并行 </el-tag>
 						<el-tag type="warning" v-else> 串行 </el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column prop="jobDetail.createFromScript" label="脚本创建" width="100" align="center" show-overflow-tooltip>
+				<el-table-column prop="jobDetail.createType" label="作业创建类型" width="110" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-tag v-if="scope.row.jobDetail.createFromScript == true"> 是 </el-tag>
-						<el-tag type="info" v-else> 否 </el-tag>
+						<el-tag type="info" v-if="(scope.row as JobOutput).jobDetail?.createType == JobCreateTypeEnum.NUMBER_0"> 内置 </el-tag>
+						<el-tag type="warning" v-if="(scope.row as JobOutput).jobDetail?.createType == JobCreateTypeEnum.NUMBER_1"> 脚本 </el-tag>
+						<el-tag type="success" v-if="(scope.row as JobOutput).jobDetail?.createType == JobCreateTypeEnum.NUMBER_2"> HTTP请求 </el-tag>
 					</template>
 				</el-table-column>
 				<!-- <el-table-column prop="jobDetail.includeAnnotations" label="扫描特性触发器" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-tag v-if="scope.row.jobDetail.includeAnnotations == true"> 是 </el-tag>
+						<el-tag v-if="(scope.row as JobOutput).jobDetail?.includeAnnotations == true"> 是 </el-tag>
 						<el-tag v-else> 否 </el-tag>
 					</template>
 				</el-table-column> -->
-				<el-table-column prop="jobDetail.properties" label="额外数据" show-overflow-tooltip />
+				<el-table-column prop="jobDetail.properties" label="额外数据" show-overflow-tooltip>
+					<template #default="scope">
+						<span v-if="(scope.row as JobOutput).jobDetail?.createType != JobCreateTypeEnum.NUMBER_2"> {{ (scope.row as JobOutput).jobDetail?.properties }} </span>
+						<div v-else style="text-align: center">
+							<el-popover placement="left" :width="400" trigger="hover">
+								<template #reference>
+									<el-tag effect="plain" type="info"> 请求参数 </el-tag>
+								</template>
+								<el-descriptions title="Http 请求参数" :column="1" size="small" :border="true">
+									<el-descriptions-item label="请求地址" label-align="right" label-class-name="job-index-descriptions-label-style">
+										{{ getHttpJobMessage((scope.row as JobOutput).jobDetail?.properties + '').requestUri }}
+									</el-descriptions-item>
+									<el-descriptions-item label="请求方法" label-align="right" label-class-name="job-index-descriptions-label-style">
+										{{ getHttpMethodDesc(getHttpJobMessage((scope.row as JobOutput).jobDetail?.properties + '').httpMethod + '') }}
+									</el-descriptions-item>
+									<el-descriptions-item label="请求报文体" label-align="right" label-class-name="job-index-descriptions-label-style">
+										{{ getHttpJobMessage((scope.row as JobOutput).jobDetail?.properties + '').body }}
+									</el-descriptions-item>
+								</el-descriptions>
+							</el-popover>
+						</div>
+					</template>
+				</el-table-column>
 				<el-table-column prop="jobDetail.updatedTime" label="更新时间" width="160" align="center" show-overflow-tooltip />
 				<el-table-column label="操作" width="170" fixed="right" align="center" show-overflow-tooltip>
 					<template #default="scope">
@@ -199,7 +222,7 @@ import JobCluster from '/@/views/system/job/component/jobCluster.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysJobApi } from '/@/api-services/api';
-import { JobOutput } from '/@/api-services/models';
+import { JobCreateTypeEnum, JobOutput, SysJobTrigger } from '/@/api-services/models';
 
 const router = useRouter();
 const editJobDetailRef = ref<InstanceType<typeof EditJobDetail>>();
@@ -253,24 +276,24 @@ const resetQuery = () => {
 // 打开新增作业页面
 const openAddJobDetail = () => {
 	state.editJobDetailTitle = '添加作业';
-	editJobDetailRef.value?.openDialog({ concurrent: true, includeAnnotations: true, groupName: 'default', createFromScript: true });
+	editJobDetailRef.value?.openDialog({ concurrent: true, includeAnnotations: true, groupName: 'default', createType: JobCreateTypeEnum.NUMBER_2 });
 };
 
 // 打开编辑作业页面
-const openEditJobDetail = (row: any) => {
+const openEditJobDetail = (row: JobOutput) => {
 	state.editJobDetailTitle = '编辑作业';
 	editJobDetailRef.value?.openDialog(row.jobDetail);
 };
 
 // 删除作业
-const delJobDetail = (row: any) => {
-	ElMessageBox.confirm(`确定删除作业:【${row.jobDetail.jobId}】?`, '提示', {
+const delJobDetail = (row: JobOutput) => {
+	ElMessageBox.confirm(`确定删除作业:【${row.jobDetail?.jobId}】?`, '提示', {
 		confirmButtonText: '确定',
 		cancelButtonText: '取消',
 		type: 'warning',
 	})
 		.then(async () => {
-			await getAPI(SysJobApi).apiSysJobDeleteJobDetailPost({ jobId: row.jobDetail.jobId });
+			await getAPI(SysJobApi).apiSysJobDeleteJobDetailPost({ jobId: row.jobDetail?.jobId });
 			handleQuery();
 			ElMessage.success('删除成功');
 		})
@@ -278,19 +301,19 @@ const delJobDetail = (row: any) => {
 };
 
 // 打开新增触发器页面
-const openAddJobTrigger = (row: any) => {
+const openAddJobTrigger = (row: JobOutput) => {
 	state.editJobTriggerTitle = '添加触发器';
-	editJobTriggerRef.value?.openDialog({ jobId: row.jobDetail.jobId, retryTimeout: 1000, startNow: true, runOnStart: true, resetOnlyOnce: true, triggerType: 'Furion.Schedule.PeriodTrigger' });
+	editJobTriggerRef.value?.openDialog({ jobId: row.jobDetail?.jobId, retryTimeout: 1000, startNow: true, runOnStart: true, resetOnlyOnce: true, triggerType: 'Furion.Schedule.PeriodTrigger' });
 };
 
 // 打开编辑触发器页面
-const openEditJobTrigger = (row: any) => {
+const openEditJobTrigger = (row: SysJobTrigger) => {
 	state.editJobTriggerTitle = '编辑触发器';
 	editJobTriggerRef.value?.openDialog(row);
 };
 
 // 删除触发器
-const delJobTrigger = (row: any) => {
+const delJobTrigger = (row: SysJobTrigger) => {
 	ElMessageBox.confirm(`确定删除触发器:【${row.triggerId}】?`, '提示', {
 		confirmButtonText: '确定',
 		cancelButtonText: '取消',
@@ -329,25 +352,25 @@ const pauseAllJob = async () => {
 };
 
 // 启动某个作业
-const startJob = async (row: any) => {
-	await getAPI(SysJobApi).apiSysJobStartJobPost({ jobId: row.jobDetail.jobId });
+const startJob = async (row: JobOutput) => {
+	await getAPI(SysJobApi).apiSysJobStartJobPost({ jobId: row.jobDetail?.jobId });
 	ElMessage.success('启动作业');
 };
 
 // 暂停某个作业
-const pauseJob = async (row: any) => {
-	await getAPI(SysJobApi).apiSysJobPauseJobPost({ jobId: row.jobDetail.jobId });
+const pauseJob = async (row: JobOutput) => {
+	await getAPI(SysJobApi).apiSysJobPauseJobPost({ jobId: row.jobDetail?.jobId });
 	ElMessage.success('暂停作业');
 };
 
 // 启动触发器
-const startTrigger = async (row: any) => {
+const startTrigger = async (row: SysJobTrigger) => {
 	await getAPI(SysJobApi).apiSysJobStartTriggerPost({ jobId: row.jobId, triggerId: row.triggerId });
 	ElMessage.success('启动触发器');
 };
 
 // 暂停触发器
-const pauseTrigger = async (row: any) => {
+const pauseTrigger = async (row: SysJobTrigger) => {
 	await getAPI(SysJobApi).apiSysJobPauseTriggerPost({ jobId: row.jobId, triggerId: row.triggerId });
 	ElMessage.success('暂停触发器');
 };
@@ -375,4 +398,31 @@ const openJobDashboard = () => {
 		path: '/platform/job/dashboard',
 	});
 };
+
+// 根据任务属性获取 HttpJobMessage
+const getHttpJobMessage = (properties: string): HttpJobMessage => {
+	const propData = JSON.parse(properties);
+	const httpJobMessageNet = JSON.parse(propData['HttpJob']); // 后端大写开头的 HttpJobMessage
+
+	return {
+		requestUri: httpJobMessageNet.RequestUri,
+		httpMethod: JSON.stringify(httpJobMessageNet.HttpMethod),
+		body: httpJobMessageNet.Body,
+	};
+};
+
+// 获取请求方法的对应描述
+const getHttpMethodDesc = (httpMethodStr: string): string => {
+	for (const key in editJobDetailRef.value?.httpMethodDef) {
+		if (editJobDetailRef.value?.httpMethodDef[key] === httpMethodStr) return key;
+	}
+	return '';
+};
 </script>
+
+<style>
+/* 此样式不能为 scoped */
+.job-index-descriptions-label-style {
+	width: 80px;
+}
+</style>