Przeglądaj źródła

定时任务调整

Long 1 dzień temu
rodzic
commit
79a155fec8

+ 0 - 18
src/api/monitor/taskRegistry.js

@@ -1,18 +0,0 @@
-import request from '@/utils/request'
-
-// Bean registry list (paginated)
-export function listTaskRegistry(query) {
-  return request({
-    url: '/monitor/taskRegistry/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// Refresh Bean registry cache
-export function refreshTaskRegistry() {
-  return request({
-    url: '/monitor/taskRegistry/refresh',
-    method: 'post'
-  })
-}

+ 8 - 0
src/api/monitor/tenantJob.js

@@ -44,6 +44,14 @@ export function syncAllTenantJob() {
   })
 }
 
+export function listTenantJobConfig(query) {
+  return request({
+    url: '/monitor/tenantJob/config/list',
+    method: 'get',
+    params: query
+  })
+}
+
 export function updateTenantJobStatus(configId, status) {
   return request({
     url: '/monitor/tenantJob/config/status',

+ 108 - 64
src/views/monitor/job/index.vue

@@ -35,12 +35,9 @@
       <el-col :span="1.5">
         <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['monitor:job:remove']">删除</el-button>
       </el-col>
-      <el-col :span="1.5">
-        <el-button type="info" plain icon="el-icon-notebook-2" size="mini" @click="handleTaskRegistry" v-hasPermi="['monitor:job:add']">Bean注册表</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button type="primary" plain icon="el-icon-user" size="mini" @click="handleTenantJobConfig" v-hasPermi="['monitor:job:edit']">租户任务配置</el-button>
-      </el-col>
+<!--      <el-col :span="1.5">-->
+<!--        <el-button type="primary" plain icon="el-icon-user" size="mini" @click="handleTenantJobConfig" v-hasPermi="['monitor:job:edit']">租户任务配置</el-button>-->
+<!--      </el-col>-->
       <el-col :span="1.5">
         <el-button type="info" plain icon="el-icon-s-operation" size="mini" @click="handleJobLog" v-hasPermi="['monitor:job:query']">任务日志</el-button>
       </el-col>
@@ -79,6 +76,7 @@
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['monitor:job:edit']">修改</el-button>
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['monitor:job:remove']">删除</el-button>
+          <el-button size="mini" type="text" icon="el-icon-connection" @click="handleAssignTenant(scope.row)" v-hasPermi="['monitor:job:edit']">分配</el-button>
           <el-button size="mini" type="text" icon="el-icon-s-operation" @click="handleRowJobLog(scope.row)" v-hasPermi="['monitor:job:query']">日志</el-button>
         </template>
       </el-table-column>
@@ -121,11 +119,7 @@
                   <i class="el-icon-question"></i>
                 </el-tooltip>
               </span>
-              <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串">
-                <template slot="append">
-                  <el-button @click="openRegistry = true; initRegistry()">从注册表选择</el-button>
-                </template>
-              </el-input>
+              <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" />
             </el-form-item>
           </el-col>
           <el-col :span="24">
@@ -191,37 +185,6 @@
       <crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab>
     </el-dialog>
 
-    <!-- Bean 注册表 -->
-    <el-dialog title="可调度 Bean 方法(invoke_target 参考)" :visible.sync="openRegistry" width="960px" append-to-body @open="initRegistry">
-      <el-form :model="registryQueryParams" ref="registryQueryForm" :inline="true" size="small">
-        <el-form-item label="Bean" prop="beanName">
-          <el-input v-model="registryQueryParams.beanName" placeholder="Bean 名称" clearable @keyup.enter.native="handleRegistryQuery" />
-        </el-form-item>
-        <el-form-item label="方法" prop="methodName">
-          <el-input v-model="registryQueryParams.methodName" placeholder="方法名" clearable @keyup.enter.native="handleRegistryQuery" />
-        </el-form-item>
-        <el-form-item label="调用目标" prop="invokeTarget">
-          <el-input v-model="registryQueryParams.invokeTarget" placeholder="invoke_target" clearable @keyup.enter.native="handleRegistryQuery" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleRegistryQuery">搜索</el-button>
-          <el-button icon="el-icon-refresh" size="mini" @click="resetRegistryQuery">重置</el-button>
-        </el-form-item>
-      </el-form>
-      <el-table v-loading="registryLoading" :data="registryList" max-height="420">
-        <el-table-column label="Bean" prop="beanName" width="180" show-overflow-tooltip />
-        <el-table-column label="类名" prop="className" width="160" show-overflow-tooltip />
-        <el-table-column label="方法" prop="methodName" width="160" show-overflow-tooltip />
-        <el-table-column label="调用目标" prop="invokeTarget" show-overflow-tooltip />
-        <el-table-column label="操作" width="100" align="center">
-          <template slot-scope="scope">
-            <el-button type="text" size="mini" @click="applyInvokeTarget(scope.row)">选用</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination v-show="registryTotal > 0" :total="registryTotal" :page.sync="registryQueryParams.pageNum" :limit.sync="registryQueryParams.pageSize" @pagination="getRegistryList" />
-    </el-dialog>
-
     <!-- 租户任务配置 -->
     <el-dialog title="租户定时任务配置" :visible.sync="openTenantJob" width="960px" append-to-body @open="loadTenantJobDialog">
       <el-form :inline="true" size="small">
@@ -256,13 +219,36 @@
       <el-empty v-if="tenantJobForm.tenantId && !tenantJobLoading && !tenantJobConfigList.length" description="暂未同步,请点击「同步到租户库」按钮" />
       <el-empty v-if="!tenantJobForm.tenantId" description="请先选择租户" />
     </el-dialog>
+
+    <!-- 分配租户弹窗 -->
+    <el-dialog :title="'分配租户 — ' + assignTemplate.jobName" :visible.sync="openAssignTenant" width="680px" append-to-body @open="loadAssignTenantDialog">
+      <el-form :inline="true" size="small">
+        <el-form-item>
+          <el-input v-model="assignTenantSearch" placeholder="搜索租户" clearable size="small" style="width: 220px" @input="handleAssignTenantSearch" />
+        </el-form-item>
+        <el-form-item>
+          <el-checkbox v-model="selectAllTenants" @change="handleSelectAllTenants">全选</el-checkbox>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="mini" :loading="assignTenantSaving" @click="submitAssignTenant">保存分配</el-button>
+        </el-form-item>
+      </el-form>
+      <el-checkbox-group v-model="selectedTenantIds" v-loading="assignTenantLoading" style="display:flex; flex-wrap:wrap; gap:4px 0;">
+        <el-checkbox
+          v-for="t in filteredAssignTenants"
+          :key="t.id"
+          :label="t.id"
+          style="width:50%; margin-right:0;"
+        >{{ t.tenantName + ' (' + t.tenantCode + ')' }}</el-checkbox>
+      </el-checkbox-group>
+      <el-empty v-if="!assignTenantLoading && !filteredAssignTenants.length" description="无可用租户" />
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { listTemplate, getTemplate, addTemplate, updateTemplate, delTemplate } from "@/api/monitor/jobTemplate";
-import { listTaskRegistry } from "@/api/monitor/taskRegistry";
-import { getTenantJobConfig, updateTenantJobStatus, syncTenantJob, syncAllTenantJob } from "@/api/monitor/tenantJob";
+import { getTenantJobConfig, updateTenantJobStatus, syncTenantJob, syncAllTenantJob, saveTenantJobConfig, listTenantJobConfig } from "@/api/monitor/tenantJob";
 import { tenantList } from "@/api/tenant/tenant";
 import Crontab from '@/components/Crontab'
 
@@ -282,18 +268,22 @@ export default {
       open: false,
       openCron: false,
       expression: "",
-      // Bean 注册表
-      openRegistry: false,
-      registryLoading: false,
-      registryList: [],
-      registryTotal: 0,
-      registryQueryParams: { pageNum: 1, pageSize: 10, beanName: undefined, methodName: undefined, invokeTarget: undefined },
       // 租户任务配置
       openTenantJob: false,
       tenantJobLoading: false,
       tenantJobConfigList: [],
       tenantOptions: [],
       tenantJobForm: { tenantId: undefined },
+      // 分配租户弹窗
+      openAssignTenant: false,
+      assignTenantLoading: false,
+      assignTenantSaving: false,
+      assignTemplate: {},
+      assignTenantSearch: '',
+      selectAllTenants: false,
+      selectedTenantIds: [],
+      previousSelectedTenantIds: [],
+      allTenants: [],
       // 字典
       jobGroupOptions: [],
       // 查询参数
@@ -350,20 +340,6 @@ export default {
       const logPath = this.$route.path.startsWith('/admin/') ? '/admin/shezhi/jobLog' : '/monitor/jobLog';
       this.$router.push({ path: logPath, query: { jobName: row.jobName } });
     },
-    // Bean 注册表
-    handleTaskRegistry() { this.openRegistry = true; },
-    initRegistry() { this.registryQueryParams.pageNum = 1; this.getRegistryList(); },
-    getRegistryList() {
-      this.registryLoading = true;
-      listTaskRegistry(this.registryQueryParams).then(res => { this.registryList = res.rows || []; this.registryTotal = res.total || 0; this.registryLoading = false; }).catch(() => { this.registryLoading = false; });
-    },
-    handleRegistryQuery() { this.registryQueryParams.pageNum = 1; this.getRegistryList(); },
-    resetRegistryQuery() { this.registryQueryParams = { pageNum: 1, pageSize: 10, beanName: undefined, methodName: undefined, invokeTarget: undefined }; this.getRegistryList(); },
-    applyInvokeTarget(row) {
-      if (!this.open) { this.handleAdd(); }
-      this.form.invokeTarget = row.invokeTarget;
-      this.openRegistry = false;
-    },
     // 模板 CRUD
     handleAdd() { this.reset(); this.open = true; this.title = "添加任务模板"; },
     handleUpdate(row) {
@@ -415,6 +391,74 @@ export default {
       this.$confirm("确认将全部租户的任务配置同步到各租户库?", "提示", { type: "warning" }).then(() => {
         return syncAllTenantJob();
       }).then(res => { const d = res.data || {}; this.msgSuccess("同步完成:成功 " + (d.successCount || 0) + ",失败 " + (d.failCount || 0)); }).catch(() => {});
+    },
+    // 分配租户
+    handleAssignTenant(row) {
+      this.assignTemplate = row
+      this.openAssignTenant = true
+    },
+    loadAssignTenantDialog() {
+      this.assignTenantLoading = true
+      this.assignTenantSearch = ''
+      this.selectAllTenants = false
+      this.selectedTenantIds = []
+      // 加载全部租户(与 tenantJobConfig 弹窗保持一致)
+      tenantList({}).then(res => {
+        this.allTenants = res.data || res.rows || []
+        this.assignTenantLoading = false
+        // 异步查询该模板已分配了哪些租户
+        listTenantJobConfig({ templateId: this.assignTemplate.templateId, status: 0, pageSize: 999 }).then(res2 => {
+          // 兼容多种响应格式:TableDataInfo { rows } / AjaxResult { data } / 直接数组
+          let rows = res2.rows || res2.data || (Array.isArray(res2) ? res2 : [])
+          console.log('分配租户 — 已分配查询结果 rows:', rows)
+          this.selectedTenantIds = rows.filter(r => String(r.status) === '0').map(r => Number(r.tenantId))
+          this.previousSelectedTenantIds = [...this.selectedTenantIds]
+          this.selectAllTenants = this.selectedTenantIds.length === this.allTenants.length && this.allTenants.length > 0
+        }).catch(() => {})
+      }).catch(() => { this.assignTenantLoading = false })
+    },
+    handleAssignTenantSearch() {},
+    handleSelectAllTenants(val) {
+      this.selectedTenantIds = val ? this.allTenants.map(t => t.id) : []
+    },
+    submitAssignTenant() {
+      this.assignTenantSaving = true
+      // 只处理选择状态发生变化的租户,避免覆盖其他模板的分配
+      const promises = []
+      const prevIds = this.previousSelectedTenantIds || []
+      this.allTenants.forEach(t => {
+        const isSelected = this.selectedTenantIds.includes(t.id)
+        const wasSelected = prevIds.includes(t.id)
+        if (isSelected === wasSelected) return  // 无变化,跳过
+        const p = getTenantJobConfig(t.id).then(res => {
+          const currentIds = (res.data || []).map(c => c.templateId)
+          const newIds = isSelected
+            ? [...new Set([...currentIds, this.assignTemplate.templateId])]
+            : currentIds.filter(id => id !== this.assignTemplate.templateId)
+          return saveTenantJobConfig({ tenantId: t.id, templateIds: newIds, status: '0' })
+        })
+        promises.push(p)
+      })
+      if (promises.length === 0) {
+        this.assignTenantSaving = false
+        this.openAssignTenant = false
+        return
+      }
+      Promise.all(promises).then(() => {
+        this.msgSuccess('分配成功')
+        this.assignTenantSaving = false
+        this.openAssignTenant = false
+      }).catch(() => { this.assignTenantSaving = false })
+    }
+  },
+  computed: {
+    filteredAssignTenants() {
+      if (!this.assignTenantSearch) return this.allTenants
+      const kw = this.assignTenantSearch.toLowerCase()
+      return this.allTenants.filter(t =>
+        (t.tenantName || '').toLowerCase().includes(kw) ||
+        (t.tenantCode || '').toLowerCase().includes(kw)
+      )
     }
   }
 };