diff --git a/packages/ui/certd-client/src/style/common.less b/packages/ui/certd-client/src/style/common.less index 81b657541..b9236c1f5 100644 --- a/packages/ui/certd-client/src/style/common.less +++ b/packages/ui/certd-client/src/style/common.less @@ -319,4 +319,109 @@ h6 { .ant-input-number{ min-width: 100px; +} + + +.cd-table { + /* 我的客户样式 */ + width: 100%; + border-collapse: collapse; + overflow: auto; + + .fs-loading{ + position: absolute; + left :0; + top :0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.05); + z-index: 10000 + } + + table { + width: 1000px; + min-width: 100%; + table-layout: fixed; + font-size: 16px; + } + + thead { + position: sticky; + top: 0; + z-index: 100; + } + + .position-sticky-right { + position: sticky; + right: 0; + z-index: 100; + border-left: 0px; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 1px; + /* 边框宽度 */ + background: #eee; + /* 边框颜色 */ + } + } + + th, + td { + + text-align: left; + border-bottom: 1px solid #eee; + border-left: 1px solid #eee; + } + + th { + padding: 15px 5px; + background: #f5f7ff; + font-weight: 600; + color: #6e8efb; + font-size: 14px; + + &:last-child { + border-right: 1px solid #eee; + } + } + + td { + padding: 10px; + font-size: 14px; + + &:last-child { + border-right: 1px solid #eee; + } + } + + td.position-sticky-right { + background-color: #fff; + } + + + tr.hover-color:hover td { + background: #f9f9ff; + } + + .status { + padding: 5px 10px; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 600; + } + + .status-active { + background: #e7f6e9; + color: #2e7d32; + } + + .status-pending { + background: #ffecb3; + color: #f57c00; + } } \ No newline at end of file diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/api.ts b/packages/ui/certd-client/src/views/certd/cert/domain/api.ts index 7e004b9a4..53d402878 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/api.ts +++ b/packages/ui/certd-client/src/views/certd/cert/domain/api.ts @@ -71,18 +71,18 @@ export async function ImportTaskStatus() { method: "post", }); } -export async function ImportTaskDelete(key: any) { +export async function ImportTaskDelete(key: string) { return await request({ url: apiPrefix + "/import/delete", method: "post", - params: { key }, + data: { key }, }); } -export async function ImportTaskStart(key: any) { +export async function ImportTaskStart(key: string) { return await request({ url: apiPrefix + "/import/start", method: "post", - params: { key }, + data: { key }, }); } diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx index baa841656..c275bb4b1 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx @@ -3,7 +3,7 @@ import { Modal, notification } from "ant-design-vue"; import { Ref, ref } from "vue"; import { useRouter } from "vue-router"; import * as api from "./api"; -import DomainImportTaskStatus from "./domain-import-task-status.vue"; +import DomainImportTaskStatus from "./import.vue"; import { Dicts } from "/@/components/plugins/lib/dicts"; import { useSettingStore } from "/@/store/settings"; import { useUserStore } from "/@/store/user"; diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/import.vue b/packages/ui/certd-client/src/views/certd/cert/domain/import.vue index 81653ab6a..2ea78b896 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/import.vue +++ b/packages/ui/certd-client/src/views/certd/cert/domain/import.vue @@ -1,29 +1,46 @@ @@ -44,7 +61,7 @@ async function loadImportTaskStatus() { } async function startTask(item: any) { - await api.ImportTaskStart(item); + await api.ImportTaskStart(item.key); await loadImportTaskStatus(); } @@ -54,7 +71,7 @@ async function deleteTask(item: any) { okText: "确认", okType: "danger", onOk: async () => { - await api.ImportTaskDelete(item.taskId); + await api.ImportTaskDelete(item.key); await loadImportTaskStatus(); }, }); @@ -74,3 +91,11 @@ onMounted(async () => { await loadImportTaskStatus(); }); + + diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx b/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx index 5fd6ef6ff..04d07f4fd 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx +++ b/packages/ui/certd-client/src/views/certd/cert/domain/use.tsx @@ -2,6 +2,7 @@ import { message } from "ant-design-vue"; import * as api from "./api"; import { useFormDialog } from "/@/use/use-dialog"; import { compute } from "@fast-crud/fast-crud"; +import { Dicts } from "/@/components/plugins/lib/dicts"; export function useDomainImport() { const { openFormDialog } = useFormDialog(); @@ -13,11 +14,12 @@ export function useDomainImport() { form: { component: { name: "dns-provider-selector", - }, - on: { - //@ts-ignore - onSelectedChange: ({ form, $event }) => { - form.dnsProviderAccessType = $event.accessType; + on: { + //@ts-ignore + selectedChange: ({ form, $event }) => { + form.dnsProviderAccessType = $event.accessType; + form.dnsProviderTitle = $event.label; + }, }, }, //@ts-ignore @@ -43,6 +45,12 @@ export function useDomainImport() { type: compute(({ form }) => { return form.dnsProviderAccessType || form.dnsProviderType; }), + on: { + //@ts-ignore + selectedChange({ form, $event }) { + form.accessTitle = $event.name; + }, + }, }, }, }, @@ -56,6 +64,7 @@ export function useDomainImport() { await api.ImportTaskAdd({ dnsProviderType: form.dnsProviderType, dnsProviderAccessId: form.dnsProviderAccessId, + title: form.dnsProviderTitle + "_" + form.accessTitle, }); if (req.afterSubmit) { req.afterSubmit(); diff --git a/packages/ui/certd-server/src/modules/auto/auto-c-register-cron.ts b/packages/ui/certd-server/src/modules/auto/auto-c-register-cron.ts index eada89e23..8f6c45113 100644 --- a/packages/ui/certd-server/src/modules/auto/auto-c-register-cron.ts +++ b/packages/ui/certd-server/src/modules/auto/auto-c-register-cron.ts @@ -220,7 +220,7 @@ export class AutoCRegisterCron { name: 'domain-expire-check', cron: `0 ${randomMinute} ${randomHour} ? * ${randomWeek}`, // 每周随机一天检查一次 job: async () => { - await this.domainService.doSyncDomainsExpirationDate({}) + await this.domainService.startSyncExpirationTask({}) } }) } diff --git a/packages/ui/certd-server/src/modules/basic/service/task-executor.ts b/packages/ui/certd-server/src/modules/basic/service/task-executor.ts index 72a25e40b..809596b30 100644 --- a/packages/ui/certd-server/src/modules/basic/service/task-executor.ts +++ b/packages/ui/certd-server/src/modules/basic/service/task-executor.ts @@ -9,8 +9,11 @@ export class BackTaskExecutor { this.tasks[type] = {} } const oldTask = this.tasks[type][task.key] - if (oldTask && oldTask.status === "running") { - throw new Error(`任务 ${task.title} 正在运行中`) + if (oldTask ){ + if (oldTask.status === "running") { + throw new Error(`任务 ${task.title} 正在运行中`) + } + this.clear(type, task.key); } this.tasks[type][task.key] = task this.run(task); @@ -55,9 +58,9 @@ export class BackTaskExecutor { } finally { task.endTime = Date.now(); task.status = "done"; - task.timeoutId = setTimeout(() => { + task.setTimeoutId(setTimeout(() => { this.clear(type, task.key); - }, 24 * 60 * 60 * 1000); + }, 24 * 60 * 60 * 1000)); delete task.run; } } @@ -76,11 +79,11 @@ export class BackTask { endTime: number; status: string = "pending"; errors?: string[] = []; - timeoutId?: NodeJS.Timeout; + private _timeoutId?: NodeJS.Timeout; - run: (task: BackTask) => Promise; + private _run: (task: BackTask) => Promise; constructor(opts: { type: string, @@ -90,21 +93,64 @@ export class BackTask { this.type = type this.key = key; this.title = title; - Object.defineProperty(this, 'run', { - value: run, - writable: true, - enumerable: false, // 设置为false使其不可遍历 + this._run = run; + + Object.defineProperty(this, '_run', { + enumerable: false, + value: this._run + }) + Object.defineProperty(this, '_timeoutId', { + enumerable: false, + value: null + }) + + Object.defineProperty(this, 'progress', { + get: ()=> { + return this.getProgress() + }, + enumerable: true, // 关键:设置为可枚举 configurable: true }); + Object.defineProperty(this, 'successCount', { + get: ()=> { + return this.getSuccessCount() + }, + enumerable: true, // 关键:设置为可枚举 + configurable: true + }) + Object.defineProperty(this, 'errorCount', { + get: ()=> { + return this.getErrorCount() + }, + enumerable: true, // 关键:设置为可枚举 + configurable: true + }) + Object.defineProperty(this, 'skipCount', { + get: ()=> { + return this.getSkipCount() + }, + enumerable: true, // 关键:设置为可枚举 + configurable: true + }) + } + + async run(task: BackTask) { + return await this._run(task); } clearTimeout() { - if (this.timeoutId) { - clearTimeout(this.timeoutId); - this.timeoutId = null; + if (this._timeoutId) { + clearTimeout(this._timeoutId); + this._timeoutId = null; } } + + setTimeoutId(timeoutId: NodeJS.Timeout) { + this.clearTimeout(); + this._timeoutId = timeoutId; + } + setTotal(total: number) { this.total = total; } @@ -117,22 +163,23 @@ export class BackTask { this.errors.push(error) } - getSuccessCount() { - return this.current - this.errors.length - } - getErrorCount() { return this.errors.length } - getProgress() { - return (this.current / this.total * 1.0).toFixed(2) - } - getSkipCount() { return this.skip } + getSuccessCount() { + return this.current - this.errors.length + } + + getProgress() { + return (this.current * 1.0 / this.total * 100.0); + } + + incrementSkip() { this.skip++ } diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts index a7538aff7..0b8020c0b 100644 --- a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -7,15 +7,15 @@ import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { InjectEntityModel } from '@midwayjs/typeorm'; import dayjs from 'dayjs'; import { In, Not, Repository } from 'typeorm'; +import { BackTask, taskExecutor } from '../../basic/service/task-executor.js'; import { CnameRecordEntity } from "../../cname/entity/cname-record.js"; import { CnameRecordService } from '../../cname/service/cname-record-service.js'; +import { UserDomainImportSetting } from '../../mine/service/models.js'; +import { UserSettingsService } from '../../mine/service/user-settings-service.js'; import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js'; import { TaskServiceBuilder } from '../../pipeline/service/getter/task-service-getter.js'; import { SubDomainService } from "../../pipeline/service/sub-domain-service.js"; import { DomainEntity } from '../entity/domain.js'; -import { BackTask, taskExecutor } from '../../basic/service/task-executor.js'; -import { UserSettingsService } from '../../mine/service/user-settings-service.js'; -import { UserDomainImportSetting } from '../../mine/service/models.js'; export interface SyncFromProviderReq { userId: number; @@ -306,7 +306,7 @@ export class DomainService extends BaseService { } await doPageTurn({ pager, getPage, itemHandle, batchHandle }) const key = `user_${userId || 0}` - logger.info(`从域名提供商${dnsProviderType}导入域名完成(${key}),共导入${task.current}个域名,跳过${task.getSkipCount()}个域名`) + logger.info(`从域名提供商${dnsProviderType}导入域名完成(${key}),共导入${task.total}个域名,跳过${task.getSkipCount()}个域名,成功${task.getSuccessCount()}个域名,失败${task.getErrorCount()}个域名`) } async getDomainImportTaskStatus(req:{userId?:number}) { @@ -324,7 +324,7 @@ export class DomainService extends BaseService { taskList.push({ ...item, - task, + task:task, }) } return taskList @@ -377,7 +377,7 @@ export class DomainService extends BaseService { async getSyncExpirationTaskStatus(req:{userId?:number}) { - const userId = req.userId || 0 + const userId = req.userId ?? 'all' const key = `user_${userId}` const task = taskExecutor.get(DOMAIN_EXPIRE_TASK_TYPE,key) return task @@ -385,7 +385,7 @@ export class DomainService extends BaseService { async startSyncExpirationTask(req: { userId?: number }) { const userId = req.userId - const key = `user_${userId || 0}` + const key = `user_${userId ?? 'all'}` taskExecutor.start(new BackTask({ type: DOMAIN_EXPIRE_TASK_TYPE, key, @@ -501,7 +501,7 @@ export class DomainService extends BaseService { await doPageTurn({ pager, getPage: getDomainPage, itemHandle: itemHandle }) const key = `user_${req.userId || 'all'}` - logger.info(`同步用户(${key})注册域名过期时间完成(${req.task.getSuccessCount()}个成功,${req.task.errors.length}个失败)` ) + logger.info(`同步用户(${key})注册域名过期时间完成(${req.task.getSuccessCount()}个成功,${req.task.getErrorCount()}个失败)` ) } diff --git a/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts b/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts index 123b9ff31..131d2e1fc 100644 --- a/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts +++ b/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts @@ -499,7 +499,8 @@ export class CnameRecordService extends BaseService { throw new ValidateException("CNAME服务提供商不能为空"); } - taskExecutor.start("cnameImport",new BackTask({ + taskExecutor.start(new BackTask({ + type:"cnameImport", key: "user_"+userId, title: "导入CNAME记录", run: async (task) => {