648 lines
21 KiB
TypeScript
648 lines
21 KiB
TypeScript
import { asJsonObject, type JsonObject } from '../../types/json.js'
|
||
import {
|
||
findKuaishouIndustryVoucherByCode,
|
||
listKuaishouIndustryVouchersByOid,
|
||
listKuaishouIndustryVouchersByTaskId,
|
||
listKuaishouIndustryVouchersForAdmin,
|
||
updateKuaishouIndustryVoucherByCode,
|
||
type KuaishouIndustryVoucherAdminListQuery,
|
||
type KuaishouIndustryVoucherAdminListRow,
|
||
} from '../../repositories/kuaishou-industry-voucher-repo.js'
|
||
import { getOrderById } from '../../repositories/order-repo.js'
|
||
import { getTaskById } from '../../repositories/task-repo.js'
|
||
import { createHttpError } from '../../utils/http.js'
|
||
import { maskSecret } from '../../utils/masking.js'
|
||
import {
|
||
checkKuaishouIndustryEticketAvailable,
|
||
isKuaishouIndustryEticketDestroyableStatus,
|
||
KUAISHOU_INDUSTRY_DESTROYABLE_DISPLAY_STATUS,
|
||
resolveKuaishouIndustryEticketDisplayStatus,
|
||
} from '../platforms/kuaishou-industry/check-available-service.js'
|
||
import {
|
||
getKuaishouIndustrySourceConfig,
|
||
listKuaishouIndustryShopConfigs,
|
||
type KuaishouIndustrySourceConfig,
|
||
} from '../platforms/kuaishou-industry/source-config-service.js'
|
||
import { resendKuaishouIndustryVoucherSendCallback } from '../platforms/kuaishou-industry/send-code-service.js'
|
||
import {
|
||
consumeKuaishouIndustryVoucher,
|
||
destroyKuaishouIndustryVoucher,
|
||
resolveKuaishouIndustryEticketType,
|
||
} from '../platforms/kuaishou-industry/voucher-service.js'
|
||
import {
|
||
approveKuaishouIndustryRefund,
|
||
disagreeKuaishouIndustryRefund,
|
||
listKuaishouIndustryRefunds,
|
||
} from '../platforms/kuaishou-industry/refund-service.js'
|
||
import { reverseKuaishouIndustryCallback } from '../platforms/kuaishou-industry/reverse-callback-service.js'
|
||
import { attachKuaishouIndustryVoucherToTask } from '../platforms/kuaishou-industry/voucher-binding-service.js'
|
||
import type { KuaishouIndustryOpenApiCallResult } from '../platforms/kuaishou-industry/openapi-client.js'
|
||
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../types/repository/rows.js'
|
||
|
||
export async function listAdminKuaishouIndustryVouchers(
|
||
input: KuaishouIndustryVoucherAdminListQuery = {},
|
||
) {
|
||
const result = await listKuaishouIndustryVouchersForAdmin(input)
|
||
const shopNameBySellerId = buildKuaishouIndustryShopNameMap()
|
||
|
||
return {
|
||
items: result.items.map((voucher) =>
|
||
mapAdminKuaishouIndustryVoucher(voucher, shopNameBySellerId),
|
||
),
|
||
pagination: {
|
||
page: result.page,
|
||
pageSize: result.pageSize,
|
||
total: result.total,
|
||
},
|
||
}
|
||
}
|
||
|
||
/** 轻量店铺选项:仅展示名与 ID,供筛选/下拉使用(不含 token 等敏感字段) */
|
||
export function listAdminKuaishouIndustryShops() {
|
||
const shops = listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig())
|
||
|
||
return {
|
||
shops: shops
|
||
.map((shop) => {
|
||
const sellerId = String(shop.sellerId || '').trim()
|
||
const shopId = String(shop.shopId || '').trim()
|
||
const shopName = String(shop.customShopName || shop.shopName || '').trim()
|
||
return {
|
||
sellerId,
|
||
shopId,
|
||
shopName,
|
||
enabled: shop.enabled !== false,
|
||
}
|
||
})
|
||
.filter((shop) => shop.sellerId || shop.shopId),
|
||
}
|
||
}
|
||
|
||
export async function listAdminKuaishouIndustryRefunds(input: JsonObject = {}) {
|
||
assertRequired(input.beginTime, '开始时间未填写')
|
||
assertRequired(input.endTime, '结束时间未填写')
|
||
|
||
const payload = await buildRefundOpenApiPayload(input)
|
||
|
||
return mapOpenApiResult(await listKuaishouIndustryRefunds(payload))
|
||
}
|
||
|
||
export async function approveAdminKuaishouIndustryRefund(input: JsonObject = {}) {
|
||
assertRequired(input.refundId, '退款单编号未填写')
|
||
assertRequired(input.refundAmount, '退款金额未填写')
|
||
|
||
const payload = await buildRefundOpenApiPayload(input)
|
||
|
||
return mapOpenApiResult(await approveKuaishouIndustryRefund(payload))
|
||
}
|
||
|
||
export async function disagreeAdminKuaishouIndustryRefund(input: JsonObject = {}) {
|
||
assertRequired(input.refundId, '退款单编号未填写')
|
||
assertRequired(input.sellerDisagreeReason, '拒绝原因未填写')
|
||
assertRequired(input.sellerDisagreeDesc, '拒绝说明未填写')
|
||
assertRequired(input.status, '退款单当前状态未填写')
|
||
assertRequired(input.negotiateStatus, '协商状态未填写')
|
||
|
||
const payload = await buildRefundOpenApiPayload(input)
|
||
|
||
return mapOpenApiResult(await disagreeKuaishouIndustryRefund(payload))
|
||
}
|
||
|
||
export async function checkAdminKuaishouIndustryVoucherAvailable(input: JsonObject = {}) {
|
||
const voucher = await resolveOptionalVoucher(input)
|
||
const payload = buildVoucherOpenApiPayload(input, voucher)
|
||
|
||
assertRequired(payload.sellerId, '卖家编号未填写')
|
||
assertRequired(
|
||
payload.eticketType || payload.bizTypeCode,
|
||
'电子凭证类型未填写(请选择券码或手动填写类型)',
|
||
)
|
||
assertEtickets(payload.etickets, '电子凭证列表未填写')
|
||
|
||
const result = mapOpenApiResult(await checkKuaishouIndustryEticketAvailable(payload))
|
||
return {
|
||
...result,
|
||
eticketType: String(payload.eticketType || payload.bizTypeCode || ''),
|
||
voucher: voucher ? mapAdminKuaishouIndustryVoucher(voucher) : null,
|
||
}
|
||
}
|
||
|
||
export async function reverseAdminKuaishouIndustryVoucher(input: JsonObject = {}) {
|
||
const voucher = await resolveOptionalVoucher(input)
|
||
const payload = buildVoucherOpenApiPayload(input, voucher)
|
||
|
||
assertRequired(payload.oid, '订单号未填写')
|
||
assertEtickets(payload.etickets, '冲正券码列表未填写')
|
||
|
||
const result = await reverseKuaishouIndustryCallback(payload)
|
||
let updatedVoucher: KuaishouIndustryVoucherRow | null = null
|
||
|
||
if (result.success && voucher) {
|
||
updatedVoucher = await updateKuaishouIndustryVoucherByCode(voucher.voucher_code, {
|
||
status: 'UNUSED',
|
||
consumeSerialNum: '',
|
||
consumeDetailsJson: [],
|
||
consumedAt: null,
|
||
destroyedAt: null,
|
||
updatedAt: new Date().toISOString(),
|
||
})
|
||
}
|
||
|
||
return {
|
||
...mapOpenApiResult(result),
|
||
voucher: updatedVoucher ? mapAdminKuaishouIndustryVoucher(updatedVoucher) : null,
|
||
}
|
||
}
|
||
|
||
export async function consumeAdminKuaishouIndustryVoucherByCode(input: JsonObject = {}) {
|
||
const voucher = await resolveRequiredVoucher(input)
|
||
const task = voucher.task_id ? await getTaskById(voucher.task_id) : null
|
||
const consumeType = String(input.consumeType || '').trim()
|
||
const consumeInput: Parameters<typeof consumeKuaishouIndustryVoucher>[1] = {
|
||
source: 'admin_tool_manual_consume',
|
||
token: String(input.token || voucher.token || '').trim(),
|
||
consumeTime: Date.now(),
|
||
...(consumeType ? { consumeType } : {}),
|
||
...(input.serialNum ? { serialNum: String(input.serialNum).trim() } : {}),
|
||
...(input.storeName ? { storeName: String(input.storeName).trim() } : {}),
|
||
...(input.storeAddress ? { storeAddress: String(input.storeAddress).trim() } : {}),
|
||
...(input.expressCode ? { expressCode: String(input.expressCode).trim() } : {}),
|
||
...(input.expressNo ? { expressNo: String(input.expressNo).trim() } : {}),
|
||
}
|
||
if (task) {
|
||
consumeInput.task = task
|
||
}
|
||
|
||
const result = await consumeKuaishouIndustryVoucher(voucher, consumeInput)
|
||
|
||
if (!result.ok || !result.voucher) {
|
||
throw createHttpError(result.errorMessage || '电子凭证核销失败', {
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_consume_failed',
|
||
})
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
voucher: mapAdminKuaishouIndustryVoucher(result.voucher),
|
||
task: task ? mapTaskReference(task) : null,
|
||
}
|
||
}
|
||
|
||
export async function resendAdminKuaishouIndustryVoucherCode(input: JsonObject = {}) {
|
||
const voucher = await resolveRequiredVoucher(input)
|
||
const order = voucher.order_id ? await getOrderById(voucher.order_id) : null
|
||
const result = await resendKuaishouIndustryVoucherSendCallback({
|
||
voucherCode: voucher.voucher_code,
|
||
oid: voucher.oid,
|
||
preferredTotalGoodsValue: Number(order?.total_amount || 0) || 0,
|
||
})
|
||
|
||
return {
|
||
success: result.success,
|
||
response: 'response' in result ? result.response || null : null,
|
||
error: result.error || '',
|
||
voucher: result.voucher ? mapAdminKuaishouIndustryVoucher(result.voucher) : null,
|
||
}
|
||
}
|
||
|
||
export async function destroyAdminKuaishouIndustryVoucherByCode(input: JsonObject = {}) {
|
||
const voucher = await resolveRequiredVoucher(input)
|
||
const checkPayload = buildVoucherOpenApiPayload(input, voucher)
|
||
|
||
assertRequired(checkPayload.sellerId, '卖家编号未填写,无法在销毁前查询卡券状态')
|
||
assertRequired(
|
||
checkPayload.eticketType || checkPayload.bizTypeCode,
|
||
'电子凭证类型未填写,无法在销毁前查询卡券状态',
|
||
)
|
||
assertEtickets(checkPayload.etickets, '电子凭证列表未填写,无法在销毁前查询卡券状态')
|
||
|
||
const checkResult = await checkKuaishouIndustryEticketAvailable(checkPayload)
|
||
if (!checkResult.success || checkResult.skippedReason) {
|
||
const platformMessage = String(
|
||
checkResult.error || checkResult.response?.error_msg || checkResult.response?.msg || '',
|
||
).trim()
|
||
throw createHttpError(
|
||
`销毁前查询卡券状态失败,已取消销毁${platformMessage ? `:${platformMessage}` : ''}`,
|
||
{
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_destroy_status_check_failed',
|
||
},
|
||
)
|
||
}
|
||
|
||
const displayStatus = resolveKuaishouIndustryEticketDisplayStatus(
|
||
checkResult.response,
|
||
voucher.voucher_code,
|
||
)
|
||
if (!isKuaishouIndustryEticketDestroyableStatus(displayStatus)) {
|
||
throw createHttpError(
|
||
displayStatus
|
||
? `卡券当前展示状态为 ${displayStatus},仅 ${KUAISHOU_INDUSTRY_DESTROYABLE_DISPLAY_STATUS}(冻结)状态可以销毁`
|
||
: `未获取到卡券展示状态,仅 ${KUAISHOU_INDUSTRY_DESTROYABLE_DISPLAY_STATUS}(冻结)状态可以销毁`,
|
||
{
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_voucher_not_frozen',
|
||
},
|
||
)
|
||
}
|
||
|
||
const task = voucher.task_id ? await getTaskById(voucher.task_id) : null
|
||
const destroyInput: Parameters<typeof destroyKuaishouIndustryVoucher>[1] = {
|
||
source: 'admin_tool_manual_destroy',
|
||
reason: String(input.reason || '').trim(),
|
||
token: String(input.token || voucher.token || '').trim(),
|
||
eticketType: resolveKuaishouIndustryEticketType(voucher, input.eticketType),
|
||
}
|
||
if (task) {
|
||
destroyInput.task = task
|
||
}
|
||
|
||
const result = await destroyKuaishouIndustryVoucher(voucher, destroyInput)
|
||
|
||
if (!result.ok || !result.voucher) {
|
||
throw createHttpError(result.errorMessage || '电子凭证销毁失败', {
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_destroy_failed',
|
||
})
|
||
}
|
||
|
||
const nextTask = result.task || task
|
||
if (nextTask && result.voucher && !result.alreadyDestroyed) {
|
||
await attachKuaishouIndustryVoucherToTask(nextTask, result.voucher, {
|
||
source: 'admin_tool_manual_destroy',
|
||
now: new Date().toISOString(),
|
||
})
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
alreadyDestroyed: Boolean(result.alreadyDestroyed),
|
||
reason: result.reason || '',
|
||
voucher: mapAdminKuaishouIndustryVoucher(result.voucher),
|
||
task: nextTask ? mapTaskReference(nextTask) : null,
|
||
}
|
||
}
|
||
|
||
function buildVoucherOpenApiPayload(
|
||
input: JsonObject,
|
||
voucher: KuaishouIndustryVoucherRow | null,
|
||
): JsonObject {
|
||
const etickets = normalizeAdminEtickets(input.etickets)
|
||
const voucherCode = String(voucher?.voucher_code || input.voucherCode || '').trim()
|
||
const eticketType = resolveKuaishouIndustryEticketType(
|
||
voucher,
|
||
input.eticketType || input.bizTypeCode,
|
||
)
|
||
|
||
return {
|
||
...input,
|
||
sellerId: String(input.sellerId || voucher?.seller_id || '').trim(),
|
||
oid: String(input.oid || input.orderId || voucher?.oid || '').trim(),
|
||
orderId: String(input.orderId || input.oid || voucher?.oid || '').trim(),
|
||
token: String(input.token || voucher?.token || '').trim(),
|
||
serialNum: String(input.serialNum || voucher?.consume_serial_num || '').trim(),
|
||
eticketType,
|
||
...(eticketType ? { bizTypeCode: eticketType } : {}),
|
||
etickets:
|
||
etickets.length > 0
|
||
? etickets
|
||
: voucherCode
|
||
? [{ id: voucherCode, code: voucherCode, num: 1 }]
|
||
: [],
|
||
}
|
||
}
|
||
|
||
async function buildRefundOpenApiPayload(input: JsonObject): Promise<JsonObject> {
|
||
const source = getKuaishouIndustrySourceConfig()
|
||
const sellerId =
|
||
String(input.sellerId || '').trim() ||
|
||
(await resolveRefundSellerIdFromLocalVoucher(input)) ||
|
||
resolveSingleEnabledKuaishouIndustrySellerId(source)
|
||
|
||
if (!sellerId && hasMultipleEnabledKuaishouIndustryShops(source)) {
|
||
throw createHttpError('存在多个快手售后退款店铺授权,请先选择 sellerId', {
|
||
statusCode: 400,
|
||
errorCode: 'admin_kuaishou_industry_refund_seller_id_required',
|
||
})
|
||
}
|
||
|
||
return {
|
||
...input,
|
||
...(sellerId ? { sellerId } : {}),
|
||
}
|
||
}
|
||
|
||
async function resolveRefundSellerIdFromLocalVoucher(input: JsonObject): Promise<string> {
|
||
const voucherCode = String(input.voucherCode || '').trim()
|
||
const oid = String(input.oid || input.orderId || '').trim()
|
||
if (voucherCode) {
|
||
const voucher = await findKuaishouIndustryVoucherByCode(voucherCode, oid)
|
||
return String(voucher?.seller_id || '').trim()
|
||
}
|
||
|
||
const taskId = Number(input.taskId || 0)
|
||
if (Number.isFinite(taskId) && taskId > 0) {
|
||
return resolveSingleSellerIdFromVouchers(
|
||
await listKuaishouIndustryVouchersByTaskId(Math.trunc(taskId)),
|
||
'当前任务关联多个卖家,请指定 sellerId',
|
||
)
|
||
}
|
||
|
||
if (oid) {
|
||
return resolveSingleSellerIdFromVouchers(
|
||
await listKuaishouIndustryVouchersByOid(oid),
|
||
'当前订单关联多个卖家,请指定 sellerId',
|
||
)
|
||
}
|
||
|
||
return ''
|
||
}
|
||
|
||
function resolveSingleSellerIdFromVouchers(
|
||
vouchers: KuaishouIndustryVoucherRow[],
|
||
multipleMessage: string,
|
||
): string {
|
||
const sellerIds = Array.from(
|
||
new Set(vouchers.map((voucher) => String(voucher.seller_id || '').trim()).filter(Boolean)),
|
||
)
|
||
|
||
if (sellerIds.length > 1) {
|
||
throw createHttpError(multipleMessage, {
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_refund_seller_id_ambiguous',
|
||
})
|
||
}
|
||
|
||
return sellerIds[0] || ''
|
||
}
|
||
|
||
function resolveSingleEnabledKuaishouIndustrySellerId(
|
||
source: KuaishouIndustrySourceConfig,
|
||
): string {
|
||
const enabledShops = listEnabledKuaishouIndustryShops(source)
|
||
|
||
return enabledShops.length === 1
|
||
? String(enabledShops[0]?.sellerId || enabledShops[0]?.shopId || '').trim()
|
||
: ''
|
||
}
|
||
|
||
function hasMultipleEnabledKuaishouIndustryShops(source: KuaishouIndustrySourceConfig): boolean {
|
||
return listEnabledKuaishouIndustryShops(source).length > 1
|
||
}
|
||
|
||
function listEnabledKuaishouIndustryShops(source: KuaishouIndustrySourceConfig) {
|
||
return listKuaishouIndustryShopConfigs(source).filter(
|
||
(shop) => shop.enabled !== false && String(shop.sellerId || shop.shopId || '').trim(),
|
||
)
|
||
}
|
||
|
||
async function resolveOptionalVoucher(
|
||
input: JsonObject,
|
||
): Promise<KuaishouIndustryVoucherRow | null> {
|
||
const voucherCode = String(input.voucherCode || '').trim()
|
||
if (!voucherCode) {
|
||
return null
|
||
}
|
||
|
||
const voucher = await findKuaishouIndustryVoucherByCode(
|
||
voucherCode,
|
||
String(input.oid || input.orderId || '').trim(),
|
||
)
|
||
if (!voucher) {
|
||
throw createHttpError('电子凭证不存在', {
|
||
statusCode: 404,
|
||
errorCode: 'admin_kuaishou_industry_voucher_not_found',
|
||
})
|
||
}
|
||
|
||
return voucher
|
||
}
|
||
|
||
async function resolveRequiredVoucher(input: JsonObject): Promise<KuaishouIndustryVoucherRow> {
|
||
const voucher = await resolveOptionalVoucher(input)
|
||
if (voucher) {
|
||
return voucher
|
||
}
|
||
|
||
const taskId = Number(input.taskId || 0)
|
||
if (Number.isFinite(taskId) && taskId > 0) {
|
||
const vouchers = await listKuaishouIndustryVouchersByTaskId(Math.trunc(taskId))
|
||
return resolveSingleVoucher(
|
||
vouchers,
|
||
'当前任务没有关联电子凭证',
|
||
'当前任务关联多个电子凭证,请指定券码',
|
||
)
|
||
}
|
||
|
||
const oid = String(input.oid || input.orderId || '').trim()
|
||
if (oid) {
|
||
const vouchers = await listKuaishouIndustryVouchersByOid(oid)
|
||
return resolveSingleVoucher(
|
||
vouchers,
|
||
'当前订单没有关联电子凭证',
|
||
'当前订单关联多个电子凭证,请指定券码',
|
||
)
|
||
}
|
||
|
||
throw createHttpError('请填写券码、任务 ID 或订单号', {
|
||
statusCode: 400,
|
||
errorCode: 'admin_kuaishou_industry_voucher_selector_missing',
|
||
})
|
||
}
|
||
|
||
function resolveSingleVoucher(
|
||
vouchers: KuaishouIndustryVoucherRow[],
|
||
missingMessage: string,
|
||
multipleMessage: string,
|
||
): KuaishouIndustryVoucherRow {
|
||
if (vouchers.length === 0) {
|
||
throw createHttpError(missingMessage, {
|
||
statusCode: 404,
|
||
errorCode: 'admin_kuaishou_industry_voucher_not_found',
|
||
})
|
||
}
|
||
|
||
if (vouchers.length > 1) {
|
||
throw createHttpError(multipleMessage, {
|
||
statusCode: 409,
|
||
errorCode: 'admin_kuaishou_industry_voucher_ambiguous',
|
||
})
|
||
}
|
||
|
||
return vouchers[0] as KuaishouIndustryVoucherRow
|
||
}
|
||
|
||
function mapOpenApiResult(result: KuaishouIndustryOpenApiCallResult) {
|
||
return {
|
||
success: result.success,
|
||
response: result.response || null,
|
||
error: result.error || '',
|
||
request: sanitizeOpenApiRequest(result.request),
|
||
durationMs: result.durationMs || 0,
|
||
httpStatus: result.httpStatus || 0,
|
||
skippedReason: result.skippedReason || '',
|
||
}
|
||
}
|
||
|
||
function sanitizeOpenApiRequest(request: JsonObject | undefined): JsonObject | null {
|
||
if (!request) {
|
||
return null
|
||
}
|
||
|
||
return {
|
||
...request,
|
||
access_token: maskSecret(request.access_token),
|
||
sign: maskSecret(request.sign),
|
||
}
|
||
}
|
||
|
||
function mapAdminKuaishouIndustryVoucher(
|
||
voucher: KuaishouIndustryVoucherRow | KuaishouIndustryVoucherAdminListRow,
|
||
shopNameBySellerId: Map<string, string> = buildKuaishouIndustryShopNameMap(),
|
||
) {
|
||
const sellerId = String(voucher.seller_id || '').trim()
|
||
const listRow = voucher as KuaishouIndustryVoucherAdminListRow
|
||
return {
|
||
id: voucher.id,
|
||
voucherCode: voucher.voucher_code,
|
||
oid: voucher.oid,
|
||
orderId: voucher.order_id,
|
||
taskId: voucher.task_id,
|
||
taskNo: String(listRow.task_no || '').trim(),
|
||
unitIndex: voucher.unit_index,
|
||
sellerId,
|
||
shopName: shopNameBySellerId.get(sellerId) || '',
|
||
skuCode: String(listRow.sku_code || '').trim(),
|
||
skuName: String(listRow.sku_name || '').trim(),
|
||
tokenMasked: maskSecret(voucher.token),
|
||
eticketType: resolveKuaishouIndustryEticketType(voucher),
|
||
status: voucher.status,
|
||
validStartTime: Number(voucher.valid_start_time || 0) || 0,
|
||
validEndTime: Number(voucher.valid_end_time || 0) || 0,
|
||
consumeSerialNum: voucher.consume_serial_num,
|
||
consumeDetails: parseJsonArray(voucher.consume_details_json),
|
||
consumedAt: voucher.consumed_at,
|
||
destroyedAt: voucher.destroyed_at,
|
||
sendCallbackStatus: voucher.send_callback_status,
|
||
sendCallbackAttemptCount: voucher.send_callback_attempt_count,
|
||
sendCallbackLastError: voucher.send_callback_last_error,
|
||
sendCallbackSentAt: voucher.send_callback_sent_at,
|
||
rawPayload: parseJsonObject(voucher.raw_payload_json),
|
||
createdAt: voucher.created_at,
|
||
updatedAt: voucher.updated_at,
|
||
}
|
||
}
|
||
|
||
function buildKuaishouIndustryShopNameMap(): Map<string, string> {
|
||
const map = new Map<string, string>()
|
||
const shops = listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig())
|
||
|
||
for (const shop of shops) {
|
||
const name = String(shop.customShopName || shop.shopName || '').trim()
|
||
if (!name) {
|
||
continue
|
||
}
|
||
|
||
const sellerId = String(shop.sellerId || '').trim()
|
||
const shopId = String(shop.shopId || '').trim()
|
||
if (sellerId) {
|
||
map.set(sellerId, name)
|
||
}
|
||
if (shopId && shopId !== sellerId) {
|
||
map.set(shopId, name)
|
||
}
|
||
}
|
||
|
||
return map
|
||
}
|
||
|
||
function mapTaskReference(task: TaskRow) {
|
||
return {
|
||
taskId: task.id,
|
||
taskNo: task.task_no,
|
||
status: task.task_status,
|
||
deliveryStatus: task.delivery_status,
|
||
}
|
||
}
|
||
|
||
function normalizeAdminEtickets(value: unknown): JsonObject[] {
|
||
if (!Array.isArray(value)) {
|
||
return []
|
||
}
|
||
|
||
const items: JsonObject[] = []
|
||
for (const item of value) {
|
||
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
||
continue
|
||
}
|
||
|
||
const current = item as JsonObject
|
||
const id = String(current.id || '').trim()
|
||
const code = String(current.code || '').trim()
|
||
const num = Number(current.num || 1)
|
||
|
||
if (!id && !code) {
|
||
continue
|
||
}
|
||
|
||
items.push({
|
||
...(id ? { id } : {}),
|
||
...(code ? { code } : {}),
|
||
num: Number.isInteger(num) && num > 0 ? num : 1,
|
||
})
|
||
}
|
||
|
||
return items
|
||
}
|
||
|
||
function assertRequired(value: unknown, message: string) {
|
||
if (value === undefined || value === null || String(value).trim() === '') {
|
||
throw createHttpError(message, {
|
||
statusCode: 400,
|
||
errorCode: 'admin_kuaishou_industry_missing_required_field',
|
||
})
|
||
}
|
||
}
|
||
|
||
function assertEtickets(value: unknown, message: string) {
|
||
if (!Array.isArray(value) || value.length === 0) {
|
||
throw createHttpError(message, {
|
||
statusCode: 400,
|
||
errorCode: 'admin_kuaishou_industry_missing_etickets',
|
||
})
|
||
}
|
||
}
|
||
|
||
function parseJsonObject(value: unknown): JsonObject {
|
||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||
return value as JsonObject
|
||
}
|
||
|
||
try {
|
||
const parsed = JSON.parse(String(value || '{}'))
|
||
return asJsonObject(parsed)
|
||
} catch {
|
||
return {}
|
||
}
|
||
}
|
||
|
||
function parseJsonArray(value: unknown): JsonObject[] {
|
||
if (Array.isArray(value)) {
|
||
return value.filter((item): item is JsonObject =>
|
||
Boolean(item && typeof item === 'object' && !Array.isArray(item)),
|
||
)
|
||
}
|
||
|
||
try {
|
||
const parsed = JSON.parse(String(value || '[]'))
|
||
return Array.isArray(parsed)
|
||
? parsed.filter((item): item is JsonObject =>
|
||
Boolean(item && typeof item === 'object' && !Array.isArray(item)),
|
||
)
|
||
: []
|
||
} catch {
|
||
return []
|
||
}
|
||
}
|