diff --git a/apps/backend/src/services/scheduler/cloudtentacles-health-job.js b/apps/backend/src/services/scheduler/cloudtentacles-health-job.ts similarity index 94% rename from apps/backend/src/services/scheduler/cloudtentacles-health-job.js rename to apps/backend/src/services/scheduler/cloudtentacles-health-job.ts index ba14cb33..092ef9f4 100644 --- a/apps/backend/src/services/scheduler/cloudtentacles-health-job.js +++ b/apps/backend/src/services/scheduler/cloudtentacles-health-job.ts @@ -1,5 +1,3 @@ -// @ts-check - import { getCloudtentaclesAsset } from '../platforms/cloudtentacles/catalog-service.js' import { getCloudtentaclesSourceConfig } from '../platforms/cloudtentacles/source-config-service.js' import { getCloudtentaclesSessionState } from '../platforms/cloudtentacles/session-state-service.js' @@ -8,7 +6,9 @@ import { notifyCloudtentaclesAuthExpired, } from '../notification/domain-notifications.js' -export async function runCloudtentaclesHealthJob(job) { +type JsonObject = Record + +export async function runCloudtentaclesHealthJob(job: JsonObject) { const source = getCloudtentaclesSourceConfig() const session = getCloudtentaclesSessionState() const token = String(session.token || '').trim() diff --git a/apps/backend/src/services/scheduler/config-service.js b/apps/backend/src/services/scheduler/config-service.ts similarity index 85% rename from apps/backend/src/services/scheduler/config-service.js rename to apps/backend/src/services/scheduler/config-service.ts index 49aa3fd3..0429e263 100644 --- a/apps/backend/src/services/scheduler/config-service.js +++ b/apps/backend/src/services/scheduler/config-service.ts @@ -1,5 +1,3 @@ -// @ts-check - import fs from 'node:fs' import path from 'node:path' @@ -8,6 +6,8 @@ import { PROJECT_ROOT } from '../../config/runtime.js' const SCHEDULED_JOBS_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'scheduled-jobs.json') const CLOUDTENTACLES_HEALTH_JOB_ID = 'cloudtentacles-health' +type JsonObject = Record + export function getScheduledJobsFilePath() { return SCHEDULED_JOBS_FILE_PATH } @@ -16,14 +16,14 @@ export function getScheduledJobsConfig() { return loadScheduledJobsConfigFromFile() } -export function saveScheduledJobsConfig(rawValue) { +export function saveScheduledJobsConfig(rawValue: unknown) { const normalized = normalizeScheduledJobsConfig(rawValue) fs.mkdirSync(path.dirname(SCHEDULED_JOBS_FILE_PATH), { recursive: true }) fs.writeFileSync(SCHEDULED_JOBS_FILE_PATH, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8') return normalized } -export function getCloudtentaclesHealthJob(config = getScheduledJobsConfig()) { +export function getCloudtentaclesHealthJob(config: JsonObject = getScheduledJobsConfig()) { return (Array.isArray(config.jobs) ? config.jobs : []) .find((item) => String(item.id || '').trim() === CLOUDTENTACLES_HEALTH_JOB_ID) || createDefaultCloudtentaclesHealthJob() @@ -42,7 +42,7 @@ function loadScheduledJobsConfigFromFile() { } } -function normalizeScheduledJobsConfig(rawValue) { +function normalizeScheduledJobsConfig(rawValue: unknown) { const source = isPlainObject(rawValue) ? rawValue : {} const rawJobs = Array.isArray(source.jobs) ? source.jobs : [] const jobs = rawJobs.map((item) => normalizeScheduledJob(item)).filter(Boolean) @@ -58,7 +58,7 @@ function normalizeScheduledJobsConfig(rawValue) { } } -function normalizeScheduledJob(rawValue) { +function normalizeScheduledJob(rawValue: unknown) { if (!isPlainObject(rawValue)) { return null } @@ -71,7 +71,7 @@ function normalizeScheduledJob(rawValue) { return normalizeCloudtentaclesHealthJob(rawValue) } -function normalizeCloudtentaclesHealthJob(rawValue) { +function normalizeCloudtentaclesHealthJob(rawValue: JsonObject) { const config = isPlainObject(rawValue.config) ? rawValue.config : {} return { @@ -108,7 +108,7 @@ function createDefaultCloudtentaclesHealthJob() { } } -function normalizeRangeInteger(value, fallback, min, max) { +function normalizeRangeInteger(value: unknown, fallback: number, min: number, max: number) { const parsed = Number(value) if (!Number.isInteger(parsed)) { return fallback @@ -117,6 +117,6 @@ function normalizeRangeInteger(value, fallback, min, max) { return Math.min(max, Math.max(min, parsed)) } -function isPlainObject(value) { +function isPlainObject(value: unknown): value is JsonObject { return Object.prototype.toString.call(value) === '[object Object]' } diff --git a/apps/backend/src/services/scheduler/scheduler-service.js b/apps/backend/src/services/scheduler/scheduler-service.ts similarity index 89% rename from apps/backend/src/services/scheduler/scheduler-service.js rename to apps/backend/src/services/scheduler/scheduler-service.ts index 02ae3d5b..3922633f 100644 --- a/apps/backend/src/services/scheduler/scheduler-service.js +++ b/apps/backend/src/services/scheduler/scheduler-service.ts @@ -1,5 +1,3 @@ -// @ts-check - import { logError, logInfo, logWarn } from '../../utils/logger.js' import { getCloudtentaclesHealthJob, @@ -7,8 +5,10 @@ import { } from './config-service.js' import { runCloudtentaclesHealthJob } from './cloudtentacles-health-job.js' -const timers = new Map() -const jobStates = new Map() +type JsonObject = Record + +const timers = new Map() +const jobStates = new Map() export function startScheduledJobs() { reloadScheduledJobs() @@ -26,7 +26,7 @@ export function reloadScheduledJobs() { const config = getScheduledJobsConfig() if (config.enabled === false) { - logInfo('[scheduler]', '定时任务系统已停用') + logInfo('[scheduler]', '定时任务系统已停用', {}) return } @@ -48,7 +48,7 @@ export function getScheduledJobRuntimeStates() { return Array.from(jobStates.values()) } -export async function runScheduledJobNow(jobId) { +export async function runScheduledJobNow(jobId: unknown) { const config = getScheduledJobsConfig() const job = config.jobs.find((item) => String(item.id || '').trim() === String(jobId || '').trim()) @@ -72,7 +72,7 @@ export async function runScheduledJobNow(jobId) { return result } -function scheduleJob(job, delayMs) { +function scheduleJob(job: JsonObject, delayMs: number) { const jobId = String(job.id || '').trim() if (!jobId) { return @@ -97,7 +97,7 @@ function scheduleJob(job, delayMs) { timers.set(jobId, timer) } -async function runJob(job, { manual = false } = {}) { +async function runJob(job: JsonObject, { manual = false }: { manual?: boolean } = {}) { const jobId = String(job.id || '').trim() const startedAt = new Date().toISOString() @@ -160,7 +160,7 @@ async function runJob(job, { manual = false } = {}) { } } -function dispatchJob(job) { +function dispatchJob(job: JsonObject) { if (job.type === 'cloudtentacles_health') { return runCloudtentaclesHealthJob(job) } @@ -168,7 +168,7 @@ function dispatchJob(job) { throw new Error(`不支持的定时任务类型:${job.type}`) } -function updateJobState(jobId, patch) { +function updateJobState(jobId: string, patch: JsonObject) { const current = jobStates.get(jobId) || { id: jobId, enabled: false, diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md index ddc866f2..15f92f7c 100644 --- a/docs/backend-typescript-migration-plan.md +++ b/docs/backend-typescript-migration-plan.md @@ -685,6 +685,16 @@ - `npm run typecheck` - `npm run build` - `npm test` 共 139 个用例通过 +168. Scheduler 定时任务模块迁移到 `.ts`: + - `src/services/scheduler/config-service.ts` + - `src/services/scheduler/scheduler-service.ts` + - `src/services/scheduler/cloudtentacles-health-job.ts` +169. 定时任务配置归一化、运行态缓存、立即执行、重载调度、Cloudtentacles 余额健康检查与告警已进入 TS 编译链路;job 配置、timer map、runtime state、健康检查 payload 补齐类型 +170. Docker 内验证通过: + - `src/services/admin/platform-config/*.test.js` 共 37 个用例通过 + - `npm run typecheck` + - `npm run build` + - `npm test` 共 139 个用例通过 ## 下一步建议