优化后端代码质量基础

This commit is contained in:
yml2213
2026-05-26 10:48:30 +08:00
parent a2bce8b272
commit d126b35579
13 changed files with 142 additions and 25 deletions
@@ -55,6 +55,7 @@ import {
mapAdminCloudtentaclesSourceConfig,
maskPhone,
} from "./mappers.js";
import { createHttpError } from "../../../../utils/http.js";
import type {
AdminCloudtentaclesCatalogQueryInput,
@@ -153,7 +154,10 @@ export function updateAdminCloudtentaclesSourceConfig(
export function deleteAdminCloudtentaclesSource(sourceKey: string) {
const key = String(sourceKey || "").trim();
if (!key) {
throw new Error("sourceKey is required for deletion");
throw createHttpError("cloudtentacles sourceKey 不能为空", {
statusCode: 400,
errorCode: "cloudtentacles_source_key_required",
});
}
deleteCloudtentaclesSessionStateByKey(key);
deleteCloudtentaclesSourceByKey(key);
@@ -1,5 +1,6 @@
import { runtimeConfig } from '../../config/runtime.js'
import { createClaimToken } from '../../repositories/claim-token-repo.js'
import { createHttpError } from '../../utils/http.js'
import { addHours, nowIso } from '../../utils/time.js'
import { randomToken } from '../../utils/random.js'
@@ -23,7 +24,10 @@ export async function createTaskClaimToken(taskId: number | string): Promise<Tas
})
if (!token) {
throw new Error('领取 token 创建失败')
throw createHttpError('领取 token 创建失败', {
statusCode: 500,
errorCode: 'claim_token_create_failed',
})
}
return {
@@ -2,7 +2,7 @@ import { createHttpError } from '../../utils/http.js'
const DEFAULT_TIMEOUT_MS = 10000
type JsonObject = Record<string, any>
type JsonObject = Record<string, unknown>
type BarkSendInput = {
serverUrl?: string
recipient?: {
@@ -4,7 +4,7 @@ import { sendInternalNotification } from './notification-service.js'
const DEFAULT_COOLDOWN_MS = 10 * 60 * 1000
const notificationCooldownMap = new Map<string, number>()
type JsonObject = Record<string, any>
type JsonObject = Record<string, unknown>
type InternalNotificationPayload = {
title: string
body?: string
@@ -13,6 +13,55 @@ type InternalNotificationPayload = {
cooldownKey?: string
cooldownMs?: number
}
type KuaishouCloudAssetNotEnoughPayload = {
task?: unknown
flow?: unknown
assetBefore?: unknown
requiredAsset?: unknown
skuName?: unknown
}
type CloudtentaclesAuthExpiredPayload = {
pathname?: unknown
errorCode?: unknown
message?: unknown
cooldownSeconds?: unknown
sourceKey?: unknown
accountLabel?: unknown
}
type CloudtentaclesAssetLowPayload = {
asset?: unknown
threshold?: unknown
cooldownSeconds?: unknown
sourceKey?: unknown
accountLabel?: unknown
}
type Open91PendingConfigPayload = {
order?: unknown
orderNo?: unknown
productNo?: unknown
buyNum?: unknown
reason?: unknown
}
type TaskNotificationPayload = {
task?: unknown
reason?: unknown
source?: unknown
errorMessage?: unknown
}
type KuaishouCloudConsumeFailedPayload = {
task?: unknown
order?: unknown
ticketCodeMasked?: unknown
shopId?: unknown
shopName?: unknown
errorMessage?: unknown
}
type ClaimRedeemNeedsAttentionPayload = {
task?: unknown
order?: unknown
status?: unknown
errorMessage?: unknown
}
export async function notifyInternalSafely(payload: InternalNotificationPayload) {
const cooldownKey = String(payload.cooldownKey || '').trim()
@@ -52,7 +101,7 @@ export function notifyKuaishouCloudAssetNotEnough({
assetBefore = 0,
requiredAsset = 0,
skuName = '',
}: JsonObject = {}) {
}: KuaishouCloudAssetNotEnoughPayload = {}) {
const taskRecord = toRecord(task)
const flowRecord = toRecord(flow)
const binding = toRecord(flowRecord.binding)
@@ -81,7 +130,7 @@ export function notifyCloudtentaclesAuthExpired({
cooldownSeconds = 600,
sourceKey = '',
accountLabel = '',
}: JsonObject = {}) {
}: CloudtentaclesAuthExpiredPayload = {}) {
const normalizedSourceKey = String(sourceKey || '').trim()
const normalizedAccountLabel = String(accountLabel || '').trim()
@@ -107,7 +156,7 @@ export function notifyCloudtentaclesAssetLow({
cooldownSeconds = 1800,
sourceKey = '',
accountLabel = '',
}: JsonObject = {}) {
}: CloudtentaclesAssetLowPayload = {}) {
const normalizedSourceKey = String(sourceKey || '').trim()
const normalizedAccountLabel = String(accountLabel || '').trim()
@@ -133,7 +182,7 @@ export function notifyOpen91PendingConfig({
productNo = '',
buyNum = 0,
reason = '未命中履约配置',
}: JsonObject = {}) {
}: Open91PendingConfigPayload = {}) {
const orderRecord = toRecord(order)
return notifyInternalSafely({
@@ -152,7 +201,7 @@ export function notifyOpen91PendingConfig({
export function notifyKuaishouCloudBindUrlRefreshFailed({
task = {},
errorMessage = '',
}: JsonObject = {}) {
}: TaskNotificationPayload = {}) {
const taskRecord = toRecord(task)
return notifyInternalSafely({
@@ -174,7 +223,7 @@ export function notifyKuaishouCloudConsumeFailed({
shopId = '',
shopName = '',
errorMessage = '',
}: JsonObject = {}) {
}: KuaishouCloudConsumeFailedPayload = {}) {
const taskRecord = toRecord(task)
const orderRecord = toRecord(order)
@@ -196,7 +245,7 @@ export function notifyTaskAutoManualReview({
task = {},
reason = '',
source = '',
}: JsonObject = {}) {
}: TaskNotificationPayload = {}) {
const taskRecord = toRecord(task)
return notifyInternalSafely({
@@ -216,7 +265,7 @@ export function notifyClaimRedeemNeedsAttention({
order = {},
status = '',
errorMessage = '',
}: JsonObject = {}) {
}: ClaimRedeemNeedsAttentionPayload = {}) {
const taskRecord = toRecord(task)
const orderRecord = toRecord(order)
@@ -6,7 +6,7 @@ import { createHttpError } from '../../utils/http.js'
const DEFAULT_WPUSH_ENDPOINT = 'https://api.wpush.cn/api/v1/send'
const DEFAULT_TIMEOUT_MS = 10000
type JsonObject = Record<string, any>
type JsonObject = Record<string, unknown>
type NodeHttpResponse = {
ok: boolean
status: number
+7 -3
View File
@@ -1,5 +1,6 @@
import { runtimeConfig } from '../../config/runtime.js'
import { createHttpError } from '../../utils/http.js'
import type { RuntimeConfig } from '../../types/runtime-config.js'
export const OPEN_91_PROVIDER = '91kaquan'
export const OPEN_91_PLATFORM = 'kuaishou'
@@ -7,10 +8,13 @@ export const OPEN_91_SUCCESS_MESSAGE = '接口调用成功'
export const OPEN_91_DEFAULT_FAIL_CODE = 1204
export const OPEN_91_COST_EXCEED_FAIL_CODE = 1220
type JsonObject = Record<string, any>
type Open91RuntimeConfig = RuntimeConfig['platforms']['ninetyone']
export function getOpen91Config() {
const config = (runtimeConfig.platforms?.ninetyone || {}) as JsonObject
export function getOpen91Config(overrides: Partial<Open91RuntimeConfig> = {}) {
const config = {
...(runtimeConfig.platforms?.ninetyone || {}),
...overrides,
}
return {
userId: String(config.userId || '').trim(),
@@ -9,6 +9,7 @@ import { syncDeliveryTasksForOrder } from './delivery-task-service.js'
import { resolveOrderItemForFulfillment } from './product-match-service.js'
import { nowIso } from '../../utils/time.js'
import { logIntegration } from '../../utils/logger.js'
import { createHttpError } from '../../utils/http.js'
import type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'
type SourceOrderItem = {
@@ -173,7 +174,10 @@ export async function upsertOrderFromSource(
})
if (!order) {
throw new Error('订单写入失败')
throw createHttpError('订单写入失败', {
statusCode: 500,
errorCode: 'order_write_failed',
})
}
const orderItems = await replaceOrderItems(
@@ -2,6 +2,7 @@ import fs from 'node:fs'
import path from 'node:path'
import { PROJECT_ROOT } from '../../../config/runtime.js'
import { createHttpError } from '../../../utils/http.js'
const CLOUDTENTACLES_SESSION_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'cloudtentacles-session.json')
@@ -58,7 +59,10 @@ export function saveCloudtentaclesSessionState(rawValue: unknown) {
export function saveCloudtentaclesSessionStateByKey(sourceKey: unknown, rawValue: unknown) {
const key = String(sourceKey || '').trim()
if (!key) {
throw new Error('saveCloudtentaclesSessionStateByKey: sourceKey is required')
throw createHttpError('cloudtentacles sourceKey 不能为空', {
statusCode: 400,
errorCode: 'cloudtentacles_source_key_required',
})
}
const states = loadCloudtentaclesSessionStatesFromFile()
@@ -75,7 +79,10 @@ export function saveCloudtentaclesSessionStateByKey(sourceKey: unknown, rawValue
export function deleteCloudtentaclesSessionStateByKey(sourceKey: unknown) {
const key = String(sourceKey || '').trim()
if (!key) {
throw new Error('deleteCloudtentaclesSessionStateByKey: sourceKey is required')
throw createHttpError('cloudtentacles sourceKey 不能为空', {
statusCode: 400,
errorCode: 'cloudtentacles_source_key_required',
})
}
const states = loadCloudtentaclesSessionStatesFromFile()
@@ -99,7 +106,10 @@ export function clearCloudtentaclesSessionState() {
export function clearCloudtentaclesSessionStateByKey(sourceKey: unknown) {
const key = String(sourceKey || '').trim()
if (!key) {
throw new Error('clearCloudtentaclesSessionStateByKey: sourceKey is required')
throw createHttpError('cloudtentacles sourceKey 不能为空', {
statusCode: 400,
errorCode: 'cloudtentacles_source_key_required',
})
}
const cleared = createDefaultCloudtentaclesSessionState()
@@ -2,6 +2,7 @@ import fs from 'node:fs'
import path from 'node:path'
import { PROJECT_ROOT } from '../../../config/runtime.js'
import { createHttpError } from '../../../utils/http.js'
const CLOUDTENTACLES_SOURCES_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'cloudtentacles-sources.json')
@@ -63,7 +64,10 @@ export function saveCloudtentaclesSourcesList(rawValue: unknown) {
export function saveCloudtentaclesSourceByKey(sourceKey: unknown, data: JsonObject = {}) {
const key = String(sourceKey || '').trim()
if (!key) {
throw new Error('saveCloudtentaclesSourceByKey: sourceKey is required')
throw createHttpError('cloudtentacles sourceKey 不能为空', {
statusCode: 400,
errorCode: 'cloudtentacles_source_key_required',
})
}
const config = loadCloudtentaclesSourcesConfigFromFile()
@@ -87,10 +91,16 @@ export function saveCloudtentaclesSourceByKey(sourceKey: unknown, data: JsonObje
export function deleteCloudtentaclesSourceByKey(sourceKey: unknown) {
const key = String(sourceKey || '').trim()
if (!key) {
throw new Error('deleteCloudtentaclesSourceByKey: sourceKey is required')
throw createHttpError('cloudtentacles sourceKey 不能为空', {
statusCode: 400,
errorCode: 'cloudtentacles_source_key_required',
})
}
if (key === 'default') {
throw new Error('deleteCloudtentaclesSourceByKey: cannot delete the default source')
throw createHttpError('不能删除默认 cloudtentacles 账号', {
statusCode: 409,
errorCode: 'cloudtentacles_default_source_forbidden',
})
}
const config = loadCloudtentaclesSourcesConfigFromFile()
@@ -1,4 +1,5 @@
import { logError, logInfo, logWarn } from '../../utils/logger.js'
import { createHttpError } from '../../utils/http.js'
import {
getCloudtentaclesHealthJob,
getScheduledJobsConfig,
@@ -53,7 +54,10 @@ export async function runScheduledJobNow(jobId: unknown) {
const job = config.jobs.find((item) => String(item.id || '').trim() === String(jobId || '').trim())
if (!job) {
throw new Error('定时任务不存在')
throw createHttpError('定时任务不存在', {
statusCode: 404,
errorCode: 'scheduled_job_not_found',
})
}
const currentTimer = timers.get(job.id)
@@ -171,7 +175,10 @@ function dispatchJob(job: JsonObject) {
return runCloudtentaclesHealthJob(job)
}
throw new Error(`不支持的定时任务类型:${job.type}`)
throw createHttpError(`不支持的定时任务类型:${job.type}`, {
statusCode: 400,
errorCode: 'unsupported_scheduled_job_type',
})
}
function updateJobState(jobId: string, patch: JsonObject) {