后端迁移后台管理写侧基础工具

This commit is contained in:
yml
2026-05-21 16:51:32 +08:00
parent 89754802d1
commit a27467bfad
3 changed files with 45 additions and 26 deletions
@@ -1,5 +1,3 @@
// @ts-check
import { buildClaimUrl, createTaskClaimToken } from '../../claim/claim-service.js' import { buildClaimUrl, createTaskClaimToken } from '../../claim/claim-service.js'
import { createHttpError } from '../../../utils/http.js' import { createHttpError } from '../../../utils/http.js'
import { import {
@@ -9,7 +7,27 @@ import {
parseTaskContext, parseTaskContext,
} from '../admin-read-shared-helpers.js' } from '../admin-read-shared-helpers.js'
export function maskPhone(value) { import type { TaskRow } from '../../../types/repository-rows.js'
type AssistedTaskAction = 'confirm' | 'redeem'
type AdminViewerContextLike = {
canOperateAssistedTask?: boolean
[key: string]: unknown
}
type TaskClaimLink = {
token: string
expiredAt: string | null
claimUrl: string
}
type ErrorLike = {
errorCode?: string
code?: string
}
export function maskPhone(value: unknown): string {
const text = String(value || '').trim() const text = String(value || '').trim()
if (!text) { if (!text) {
return '' return ''
@@ -22,7 +40,7 @@ export function maskPhone(value) {
return `${text.slice(0, 3)}****${text.slice(-4)}` return `${text.slice(0, 3)}****${text.slice(-4)}`
} }
export function maskCode(value) { export function maskCode(value: unknown): string {
const text = String(value || '').trim() const text = String(value || '').trim()
if (!text) { if (!text) {
return '' return ''
@@ -35,18 +53,17 @@ export function maskCode(value) {
return `${text.slice(0, 4)}****${text.slice(-4)}` return `${text.slice(0, 4)}****${text.slice(-4)}`
} }
export function resolveTaskInventoryGroupCodes(task) { export function resolveTaskInventoryGroupCodes(task: TaskRow): string[] | null {
const taskContext = parseTaskContext(task) const taskContext = parseTaskContext(task)
const inventoryGroupCode = String(taskContext?.inventoryGroupCode || '').trim() const inventoryGroupCode = String(taskContext?.inventoryGroupCode || '').trim()
return inventoryGroupCode ? [inventoryGroupCode] : null return inventoryGroupCode ? [inventoryGroupCode] : null
} }
/** export function ensureViewerCanOperateAssistedTask(
* @param {any} task task: TaskRow,
* @param {any} viewerContext viewerContext: AdminViewerContextLike,
* @param {'confirm' | 'redeem'} action action: AssistedTaskAction,
*/ ): void {
export function ensureViewerCanOperateAssistedTask(task, viewerContext, action) {
if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task)) { if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task)) {
throw createHttpError('当前账号没有此操作权限', { throw createHttpError('当前账号没有此操作权限', {
statusCode: 403, statusCode: 403,
@@ -69,12 +86,12 @@ export function ensureViewerCanOperateAssistedTask(task, viewerContext, action)
} }
} }
export function isRecoverableTaskSessionCloseError(error) { export function isRecoverableTaskSessionCloseError(error: ErrorLike | null | undefined): boolean {
const errorCode = String(error?.errorCode || error?.code || '').trim() const errorCode = String(error?.errorCode || error?.code || '').trim()
return errorCode === 'session_not_found' || errorCode === 'session_closed' return errorCode === 'session_not_found' || errorCode === 'session_closed'
} }
export function normalizeManualDispatchOutcome(value) { export function normalizeManualDispatchOutcome(value: unknown): 'delivered' | 'failed' {
const normalized = String(value || '').trim().toLowerCase() const normalized = String(value || '').trim().toLowerCase()
if (normalized === 'failed') { if (normalized === 'failed') {
@@ -84,14 +101,11 @@ export function normalizeManualDispatchOutcome(value) {
return 'delivered' return 'delivered'
} }
export function getTaskClaimExpiresAt(task) { export function getTaskClaimExpiresAt(task: Partial<TaskRow> | null | undefined): string | null {
return task?.claim_expires_at || task?.primary_claim_expires_at || null return task?.claim_expires_at || task?.primary_claim_expires_at || null
} }
/** export async function ensureTaskClaimLink(task: TaskRow): Promise<TaskClaimLink> {
* @param {any} task
*/
export async function ensureTaskClaimLink(task) {
const tokenStatus = String(task?.primary_claim_token_status || '').trim() const tokenStatus = String(task?.primary_claim_token_status || '').trim()
const token = String(task?.primary_claim_token || task?.claim_token || '').trim() const token = String(task?.primary_claim_token || task?.claim_token || '').trim()
const expiredAt = getTaskClaimExpiresAt(task) const expiredAt = getTaskClaimExpiresAt(task)
@@ -112,7 +126,7 @@ export async function ensureTaskClaimLink(task) {
} }
} }
function isClaimExpired(expiredAt) { function isClaimExpired(expiredAt: string | null): boolean {
if (!expiredAt) { if (!expiredAt) {
return false return false
} }
@@ -1,15 +1,11 @@
// @ts-check
import { getWebhookEventById } from '../../../repositories/webhook-event-repo.js' import { getWebhookEventById } from '../../../repositories/webhook-event-repo.js'
import { replayAgisoTradeWebhookEvent } from '../../order/webhook-service.js' import { replayAgisoTradeWebhookEvent } from '../../order/webhook-service.js'
import { createHttpError } from '../../../utils/http.js' import { createHttpError } from '../../../utils/http.js'
/** @typedef {import('../../../types/admin-read-inputs.js').AdminEntityIdInput} AdminEntityIdInput */ import type { AdminEntityIdInput } from '../../../types/admin-read-inputs.js'
/** @typedef {import('../../../types/admin-write-models.js').AdminWebhookReplayResponse} AdminWebhookReplayResponse */ import type { AdminWebhookReplayResponse } from '../../../types/admin-write-models.js'
/** @returns {Promise<AdminWebhookReplayResponse>} */ export async function replayAdminWebhookEvent(eventId: AdminEntityIdInput): Promise<AdminWebhookReplayResponse> {
/** @param {AdminEntityIdInput} eventId */
export async function replayAdminWebhookEvent(eventId) {
const event = await getWebhookEventById(Number(eventId)) const event = await getWebhookEventById(Number(eventId))
if (!event) { if (!event) {
@@ -726,6 +726,15 @@
- `npm run typecheck` - `npm run typecheck`
- `npm run build` - `npm run build`
- `npm test` 共 139 个用例通过 - `npm test` 共 139 个用例通过
180. 后台 Admin 写侧共享 helper 与 Webhook 重放入口迁移到 `.ts`
- `src/services/admin/write/shared.ts`
- `src/services/admin/write/webhook-events.ts`
181. Admin 脱敏、任务库存组解析、辅助领取权限校验、任务 claim 链接补齐、手动发货结果归一化、Webhook 重放入口已进入 TS 编译链路;任务 row、viewer context、错误对象、entity id 与重放响应补齐类型
182. Docker 内验证通过:
- `src/services/admin/*.test.js` 共 8 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 139 个用例通过
## 下一步建议 ## 下一步建议