后端迁移后台管理写侧基础工具
This commit is contained in:
+33
-19
@@ -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
|
||||||
}
|
}
|
||||||
+3
-7
@@ -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 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user