后端迁移快手电子券平台服务
This commit is contained in:
+32
-16
@@ -1,10 +1,23 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import { createHttpError } from '../../../utils/http.js'
|
import { createHttpError } from '../../../utils/http.js'
|
||||||
import { kuaishouEticketRequest } from './http-client.js'
|
import { kuaishouEticketRequest } from './http-client.js'
|
||||||
import { resolveKuaishouEticketConfig } from './shared.js'
|
import { resolveKuaishouEticketConfig } from './shared.js'
|
||||||
|
|
||||||
export async function queryKuaishouEticketConsumeDetail(payload = {}) {
|
type JsonObject = Record<string, any>
|
||||||
|
|
||||||
|
type KuaishouEticketDetailPayload = {
|
||||||
|
eTicketId: string
|
||||||
|
oid: string
|
||||||
|
formToken: string
|
||||||
|
leftCount: number
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type KuaishouEticketDetailResult = {
|
||||||
|
detail: KuaishouEticketDetailPayload | null
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function queryKuaishouEticketConsumeDetail(payload: JsonObject = {}) {
|
||||||
const context = resolveKuaishouEticketActionContext(payload)
|
const context = resolveKuaishouEticketActionContext(payload)
|
||||||
const response = await kuaishouEticketRequest(context.detailPath, {
|
const response = await kuaishouEticketRequest(context.detailPath, {
|
||||||
baseUrl: context.baseUrl,
|
baseUrl: context.baseUrl,
|
||||||
@@ -21,7 +34,7 @@ export async function queryKuaishouEticketConsumeDetail(payload = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function consumeKuaishouEticket(payload = {}) {
|
export async function consumeKuaishouEticket(payload: JsonObject = {}) {
|
||||||
const context = resolveKuaishouEticketActionContext(payload)
|
const context = resolveKuaishouEticketActionContext(payload)
|
||||||
const detailResult = shouldResolveConsumeDetail(payload)
|
const detailResult = shouldResolveConsumeDetail(payload)
|
||||||
? await queryKuaishouEticketConsumeDetail({
|
? await queryKuaishouEticketConsumeDetail({
|
||||||
@@ -51,7 +64,7 @@ export async function consumeKuaishouEticket(payload = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapKuaishouEticketDetailResult(payload = {}, context = {}) {
|
export function mapKuaishouEticketDetailResult(payload: JsonObject = {}, context: JsonObject = {}) {
|
||||||
const normalized = normalizeKuaishouEticketApiResult(payload)
|
const normalized = normalizeKuaishouEticketApiResult(payload)
|
||||||
const detail = mapKuaishouEticketDetailPayload(payload?.data?.eTicket)
|
const detail = mapKuaishouEticketDetailPayload(payload?.data?.eTicket)
|
||||||
const goods = mapKuaishouEticketGoodsPayload(payload?.data?.goods)
|
const goods = mapKuaishouEticketGoodsPayload(payload?.data?.goods)
|
||||||
@@ -71,7 +84,7 @@ export function mapKuaishouEticketDetailResult(payload = {}, context = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapKuaishouEticketConsumeResult(payload = {}, context = {}) {
|
export function mapKuaishouEticketConsumeResult(payload: JsonObject = {}, context: JsonObject = {}) {
|
||||||
const normalized = normalizeKuaishouEticketApiResult(payload)
|
const normalized = normalizeKuaishouEticketApiResult(payload)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -93,7 +106,10 @@ export function mapKuaishouEticketConsumeResult(payload = {}, context = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveKuaishouEticketConsumePayload(payload = {}, detailResult = null) {
|
export function resolveKuaishouEticketConsumePayload(
|
||||||
|
payload: JsonObject = {},
|
||||||
|
detailResult: KuaishouEticketDetailResult | null = null,
|
||||||
|
) {
|
||||||
const detail = detailResult?.detail || null
|
const detail = detailResult?.detail || null
|
||||||
const eTicketId = pickFirstNonEmpty([payload.eTicketId, detail?.eTicketId])
|
const eTicketId = pickFirstNonEmpty([payload.eTicketId, detail?.eTicketId])
|
||||||
const oid = pickFirstNonEmpty([payload.oid, detail?.oid])
|
const oid = pickFirstNonEmpty([payload.oid, detail?.oid])
|
||||||
@@ -131,7 +147,7 @@ export function resolveKuaishouEticketConsumePayload(payload = {}, detailResult
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeKuaishouEticketApiResult(payload = {}) {
|
export function normalizeKuaishouEticketApiResult(payload: JsonObject = {}) {
|
||||||
const result = Number(payload?.result || 0)
|
const result = Number(payload?.result || 0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -143,7 +159,7 @@ export function normalizeKuaishouEticketApiResult(payload = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapKuaishouEticketDetailPayload(value = {}) {
|
export function mapKuaishouEticketDetailPayload(value: unknown): KuaishouEticketDetailPayload | null {
|
||||||
if (!isPlainObject(value)) {
|
if (!isPlainObject(value)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -164,7 +180,7 @@ export function mapKuaishouEticketDetailPayload(value = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapKuaishouEticketGoodsPayload(value = {}) {
|
export function mapKuaishouEticketGoodsPayload(value: unknown) {
|
||||||
if (!isPlainObject(value)) {
|
if (!isPlainObject(value)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -179,7 +195,7 @@ export function mapKuaishouEticketGoodsPayload(value = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveKuaishouEticketActionContext(payload = {}) {
|
function resolveKuaishouEticketActionContext(payload: JsonObject = {}) {
|
||||||
const config = resolveKuaishouEticketConfig(payload)
|
const config = resolveKuaishouEticketConfig(payload)
|
||||||
const eTicketId = String(payload.eTicketId || '').trim()
|
const eTicketId = String(payload.eTicketId || '').trim()
|
||||||
|
|
||||||
@@ -203,11 +219,11 @@ function resolveKuaishouEticketActionContext(payload = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldResolveConsumeDetail(payload = {}) {
|
function shouldResolveConsumeDetail(payload: JsonObject = {}): boolean {
|
||||||
return !String(payload.oid || '').trim() || !String(payload.formToken || '').trim()
|
return !String(payload.oid || '').trim() || !String(payload.formToken || '').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickFirstNonEmpty(values) {
|
function pickFirstNonEmpty(values: unknown[]): string {
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
const normalized = String(value || '').trim()
|
const normalized = String(value || '').trim()
|
||||||
if (normalized) {
|
if (normalized) {
|
||||||
@@ -218,16 +234,16 @@ function pickFirstNonEmpty(values) {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePositiveInteger(value, fallback) {
|
function normalizePositiveInteger(value: unknown, fallback: number): number {
|
||||||
const parsed = Number(value)
|
const parsed = Number(value)
|
||||||
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeInteger(value, fallback) {
|
function normalizeInteger(value: unknown, fallback: number): number {
|
||||||
const parsed = Number(value)
|
const parsed = Number(value)
|
||||||
return Number.isInteger(parsed) ? parsed : fallback
|
return Number.isInteger(parsed) ? parsed : fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value: unknown): value is JsonObject {
|
||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
+39
-10
@@ -1,14 +1,35 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import http from 'node:http'
|
import http from 'node:http'
|
||||||
import https from 'node:https'
|
import https from 'node:https'
|
||||||
|
import type { IncomingHttpHeaders } from 'node:http'
|
||||||
|
|
||||||
import { parseJsonObject } from '../../../utils/json.js'
|
import { parseJsonObject } from '../../../utils/json.js'
|
||||||
import { createHttpError } from '../../../utils/http.js'
|
import { createHttpError } from '../../../utils/http.js'
|
||||||
import { logInfo } from '../../../utils/logger.js'
|
import { logInfo } from '../../../utils/logger.js'
|
||||||
import { buildKuaishouEticketHeaders, buildKuaishouEticketUrl, resolveKuaishouEticketConfig } from './shared.js'
|
import { buildKuaishouEticketHeaders, buildKuaishouEticketUrl, resolveKuaishouEticketConfig } from './shared.js'
|
||||||
|
|
||||||
export async function kuaishouEticketRequest(pathname, options = {}) {
|
type JsonObject = Record<string, any>
|
||||||
|
|
||||||
|
type KuaishouEticketRequestOptions = JsonObject & {
|
||||||
|
method?: string
|
||||||
|
body?: unknown
|
||||||
|
headers?: Record<string, unknown>
|
||||||
|
timeoutMs?: number | string
|
||||||
|
cookie?: string
|
||||||
|
contentType?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeaderAdapter = {
|
||||||
|
get(name: string): string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeHttpResponse = {
|
||||||
|
ok: boolean
|
||||||
|
status: number
|
||||||
|
headers: HeaderAdapter
|
||||||
|
bodyText: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function kuaishouEticketRequest(pathname: string, options: KuaishouEticketRequestOptions = {}) {
|
||||||
const config = resolveKuaishouEticketConfig(options)
|
const config = resolveKuaishouEticketConfig(options)
|
||||||
const url = buildKuaishouEticketUrl(config.baseUrl, pathname)
|
const url = buildKuaishouEticketUrl(config.baseUrl, pathname)
|
||||||
const timeoutMs = Number(options.timeoutMs || config.timeoutMs || 8000)
|
const timeoutMs = Number(options.timeoutMs || config.timeoutMs || 8000)
|
||||||
@@ -69,7 +90,7 @@ export async function kuaishouEticketRequest(pathname, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferContentType(body) {
|
function inferContentType(body: unknown): string {
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -81,7 +102,7 @@ function inferContentType(body) {
|
|||||||
return 'application/json'
|
return 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeRequestBody(body, contentType) {
|
function normalizeRequestBody(body: unknown, contentType: unknown): string | undefined {
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -97,7 +118,15 @@ function normalizeRequestBody(body, contentType) {
|
|||||||
return String(body)
|
return String(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function requestViaNodeHttp(url, { method, headers, body, signal }) {
|
async function requestViaNodeHttp(
|
||||||
|
url: URL,
|
||||||
|
{ method, headers, body, signal }: {
|
||||||
|
method: string
|
||||||
|
headers: Record<string, string>
|
||||||
|
body?: string
|
||||||
|
signal: AbortSignal
|
||||||
|
},
|
||||||
|
): Promise<NodeHttpResponse> {
|
||||||
const isHttps = url.protocol === 'https:'
|
const isHttps = url.protocol === 'https:'
|
||||||
const transport = isHttps ? https : http
|
const transport = isHttps ? https : http
|
||||||
|
|
||||||
@@ -107,7 +136,7 @@ async function requestViaNodeHttp(url, { method, headers, body, signal }) {
|
|||||||
headers,
|
headers,
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
const chunks = []
|
const chunks: Buffer[] = []
|
||||||
|
|
||||||
response.on('data', (chunk) => {
|
response.on('data', (chunk) => {
|
||||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
|
||||||
@@ -148,8 +177,8 @@ async function requestViaNodeHttp(url, { method, headers, body, signal }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHeaderAdapter(headers) {
|
function createHeaderAdapter(headers: IncomingHttpHeaders): HeaderAdapter {
|
||||||
const normalized = new Map()
|
const normalized = new Map<string, string>()
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(headers || {})) {
|
for (const [key, value] of Object.entries(headers || {})) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
@@ -163,7 +192,7 @@ function createHeaderAdapter(headers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get(name) {
|
get(name: string) {
|
||||||
return normalized.get(String(name || '').toLowerCase()) || null
|
return normalized.get(String(name || '').toLowerCase()) || null
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
+6
-6
@@ -1,10 +1,10 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import { createHttpError } from '../../../utils/http.js'
|
import { createHttpError } from '../../../utils/http.js'
|
||||||
import { kuaishouEticketRequest } from './http-client.js'
|
import { kuaishouEticketRequest } from './http-client.js'
|
||||||
import { resolveKuaishouEticketConfig } from './shared.js'
|
import { resolveKuaishouEticketConfig } from './shared.js'
|
||||||
|
|
||||||
export async function queryKuaishouEticketStableInfo(payload = {}) {
|
type JsonObject = Record<string, any>
|
||||||
|
|
||||||
|
export async function queryKuaishouEticketStableInfo(payload: JsonObject = {}) {
|
||||||
const config = resolveKuaishouEticketConfig(payload)
|
const config = resolveKuaishouEticketConfig(payload)
|
||||||
const cookie = String(payload.cookie || config.cookie || '').trim()
|
const cookie = String(payload.cookie || config.cookie || '').trim()
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export async function queryKuaishouEticketStableInfo(payload = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapKuaishouEticketStableInfoResult(payload = {}, context = {}) {
|
export function mapKuaishouEticketStableInfoResult(payload: JsonObject = {}, context: JsonObject = {}) {
|
||||||
const result = Number(payload?.result || 0)
|
const result = Number(payload?.result || 0)
|
||||||
const userInfo = isPlainObject(payload?.data?.userInfo) ? payload.data.userInfo : {}
|
const userInfo = isPlainObject(payload?.data?.userInfo) ? payload.data.userInfo : {}
|
||||||
const bizStatus = isPlainObject(payload?.data?.bizStatus) ? payload.data.bizStatus : {}
|
const bizStatus = isPlainObject(payload?.data?.bizStatus) ? payload.data.bizStatus : {}
|
||||||
@@ -51,11 +51,11 @@ export function mapKuaishouEticketStableInfoResult(payload = {}, context = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeInteger(value, fallback) {
|
function normalizeInteger(value: unknown, fallback: number): number {
|
||||||
const parsed = Number(value)
|
const parsed = Number(value)
|
||||||
return Number.isInteger(parsed) ? parsed : fallback
|
return Number.isInteger(parsed) ? parsed : fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value: unknown): value is JsonObject {
|
||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
+40
-9
@@ -1,10 +1,41 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
const DEFAULT_BASE_URL = 'https://s.kwaixiaodian.com'
|
const DEFAULT_BASE_URL = 'https://s.kwaixiaodian.com'
|
||||||
const DEFAULT_REFERER_PATH = '/zone/industry/voucher/verify-list'
|
const DEFAULT_REFERER_PATH = '/zone/industry/voucher/verify-list'
|
||||||
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36'
|
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36'
|
||||||
|
|
||||||
export function resolveKuaishouEticketConfig(overrides = {}) {
|
export type KuaishouEticketConfigInput = {
|
||||||
|
enabled?: boolean
|
||||||
|
baseUrl?: string
|
||||||
|
cookie?: string
|
||||||
|
timeoutMs?: number | string
|
||||||
|
infoPath?: string
|
||||||
|
detailPath?: string
|
||||||
|
consumePath?: string
|
||||||
|
refererPath?: string
|
||||||
|
userAgent?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KuaishouEticketConfig = {
|
||||||
|
enabled: boolean
|
||||||
|
baseUrl: string
|
||||||
|
cookie: string
|
||||||
|
timeoutMs: number
|
||||||
|
infoPath: string
|
||||||
|
detailPath: string
|
||||||
|
consumePath: string
|
||||||
|
refererPath: string
|
||||||
|
userAgent: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeaderInput = {
|
||||||
|
baseUrl?: string
|
||||||
|
refererPath?: string
|
||||||
|
userAgent?: string
|
||||||
|
cookie?: string
|
||||||
|
contentType?: string
|
||||||
|
extra?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveKuaishouEticketConfig(overrides: KuaishouEticketConfigInput = {}): KuaishouEticketConfig {
|
||||||
return {
|
return {
|
||||||
enabled: overrides.enabled !== false,
|
enabled: overrides.enabled !== false,
|
||||||
baseUrl: normalizeBaseUrl(overrides.baseUrl || DEFAULT_BASE_URL) || DEFAULT_BASE_URL,
|
baseUrl: normalizeBaseUrl(overrides.baseUrl || DEFAULT_BASE_URL) || DEFAULT_BASE_URL,
|
||||||
@@ -18,7 +49,7 @@ export function resolveKuaishouEticketConfig(overrides = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildKuaishouEticketUrl(baseUrl, pathname) {
|
export function buildKuaishouEticketUrl(baseUrl: unknown, pathname: unknown): URL {
|
||||||
return new URL(normalizePath(pathname, '/'), normalizeBaseUrl(baseUrl) || DEFAULT_BASE_URL)
|
return new URL(normalizePath(pathname, '/'), normalizeBaseUrl(baseUrl) || DEFAULT_BASE_URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +60,7 @@ export function buildKuaishouEticketHeaders({
|
|||||||
cookie = '',
|
cookie = '',
|
||||||
contentType = 'application/json',
|
contentType = 'application/json',
|
||||||
extra = {},
|
extra = {},
|
||||||
} = {}) {
|
}: HeaderInput = {}): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
accept: 'application/json',
|
accept: 'application/json',
|
||||||
'accept-language': 'zh-CN,zh;q=0.9',
|
'accept-language': 'zh-CN,zh;q=0.9',
|
||||||
@@ -44,11 +75,11 @@ export function buildKuaishouEticketHeaders({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeBaseUrl(value) {
|
function normalizeBaseUrl(value: unknown): string {
|
||||||
return String(value || '').trim().replace(/\/+$/, '')
|
return String(value || '').trim().replace(/\/+$/, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePath(value, fallback) {
|
function normalizePath(value: unknown, fallback: string): string {
|
||||||
const normalized = String(value || '').trim()
|
const normalized = String(value || '').trim()
|
||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
return fallback
|
return fallback
|
||||||
@@ -57,12 +88,12 @@ function normalizePath(value, fallback) {
|
|||||||
return normalized.startsWith('/') ? normalized : `/${normalized}`
|
return normalized.startsWith('/') ? normalized : `/${normalized}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePositiveInteger(value, fallback) {
|
function normalizePositiveInteger(value: unknown, fallback: number): number {
|
||||||
const parsed = Number(value)
|
const parsed = Number(value)
|
||||||
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeHeaderMap(extra) {
|
function normalizeHeaderMap(extra: unknown): Record<string, string> {
|
||||||
if (!extra || typeof extra !== 'object') {
|
if (!extra || typeof extra !== 'object') {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
+32
-18
@@ -1,5 +1,3 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
@@ -8,26 +6,42 @@ import { PROJECT_ROOT } from '../../../config/runtime.js'
|
|||||||
const KUAISHOU_ETICKET_SOURCE_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'kuaishou-eticket-source.json')
|
const KUAISHOU_ETICKET_SOURCE_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'kuaishou-eticket-source.json')
|
||||||
const DEFAULT_BASE_URL = 'https://s.kwaixiaodian.com'
|
const DEFAULT_BASE_URL = 'https://s.kwaixiaodian.com'
|
||||||
|
|
||||||
export function getKuaishouEticketSourceFilePath() {
|
type JsonObject = Record<string, any>
|
||||||
|
|
||||||
|
export type KuaishouEticketShopConfig = {
|
||||||
|
shopId: string
|
||||||
|
kshopName: string
|
||||||
|
cookie: string
|
||||||
|
userAvatar: string
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KuaishouEticketSourceConfig = {
|
||||||
|
enabled: boolean
|
||||||
|
baseUrl: string
|
||||||
|
shops: KuaishouEticketShopConfig[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKuaishouEticketSourceFilePath(): string {
|
||||||
return KUAISHOU_ETICKET_SOURCE_FILE_PATH
|
return KUAISHOU_ETICKET_SOURCE_FILE_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getKuaishouEticketSourceConfig() {
|
export function getKuaishouEticketSourceConfig(): KuaishouEticketSourceConfig {
|
||||||
return loadKuaishouEticketSourceConfigFromFile()
|
return loadKuaishouEticketSourceConfigFromFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveKuaishouEticketSourceConfig(rawValue) {
|
export function saveKuaishouEticketSourceConfig(rawValue: unknown): KuaishouEticketSourceConfig {
|
||||||
const normalized = normalizeKuaishouEticketSourceConfig(rawValue)
|
const normalized = normalizeKuaishouEticketSourceConfig(rawValue)
|
||||||
fs.mkdirSync(path.dirname(KUAISHOU_ETICKET_SOURCE_FILE_PATH), { recursive: true })
|
fs.mkdirSync(path.dirname(KUAISHOU_ETICKET_SOURCE_FILE_PATH), { recursive: true })
|
||||||
fs.writeFileSync(KUAISHOU_ETICKET_SOURCE_FILE_PATH, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8')
|
fs.writeFileSync(KUAISHOU_ETICKET_SOURCE_FILE_PATH, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8')
|
||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listKuaishouEticketShopConfigs(source = getKuaishouEticketSourceConfig()) {
|
export function listKuaishouEticketShopConfigs(source: KuaishouEticketSourceConfig = getKuaishouEticketSourceConfig()): KuaishouEticketShopConfig[] {
|
||||||
return Array.isArray(source?.shops) ? source.shops : []
|
return Array.isArray(source?.shops) ? source.shops : []
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findKuaishouEticketShopConfig(shopId, source = getKuaishouEticketSourceConfig()) {
|
export function findKuaishouEticketShopConfig(shopId: unknown, source: KuaishouEticketSourceConfig = getKuaishouEticketSourceConfig()): KuaishouEticketShopConfig | null {
|
||||||
const normalizedShopId = String(shopId || '').trim()
|
const normalizedShopId = String(shopId || '').trim()
|
||||||
if (!normalizedShopId) {
|
if (!normalizedShopId) {
|
||||||
return null
|
return null
|
||||||
@@ -36,7 +50,7 @@ export function findKuaishouEticketShopConfig(shopId, source = getKuaishouEticke
|
|||||||
return listKuaishouEticketShopConfigs(source).find((item) => String(item.shopId || '').trim() === normalizedShopId) || null
|
return listKuaishouEticketShopConfigs(source).find((item) => String(item.shopId || '').trim() === normalizedShopId) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findKuaishouEticketShopConfigByName(kshopName, source = getKuaishouEticketSourceConfig()) {
|
export function findKuaishouEticketShopConfigByName(kshopName: unknown, source: KuaishouEticketSourceConfig = getKuaishouEticketSourceConfig()): KuaishouEticketShopConfig | null {
|
||||||
const normalizedName = String(kshopName || '').trim()
|
const normalizedName = String(kshopName || '').trim()
|
||||||
if (!normalizedName) {
|
if (!normalizedName) {
|
||||||
return null
|
return null
|
||||||
@@ -46,9 +60,9 @@ export function findKuaishouEticketShopConfigByName(kshopName, source = getKuais
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveKuaishouEticketShopConfig(
|
export function resolveKuaishouEticketShopConfig(
|
||||||
{ shopId = '', kshopName = '', shopName = '' } = {},
|
{ shopId = '', kshopName = '', shopName = '' }: { shopId?: unknown, kshopName?: unknown, shopName?: unknown } = {},
|
||||||
source = getKuaishouEticketSourceConfig(),
|
source: KuaishouEticketSourceConfig = getKuaishouEticketSourceConfig(),
|
||||||
) {
|
): KuaishouEticketShopConfig | null {
|
||||||
const byId = findKuaishouEticketShopConfig(shopId, source)
|
const byId = findKuaishouEticketShopConfig(shopId, source)
|
||||||
if (byId) {
|
if (byId) {
|
||||||
return byId
|
return byId
|
||||||
@@ -62,11 +76,11 @@ export function resolveKuaishouEticketShopConfig(
|
|||||||
return findKuaishouEticketShopConfigByName(normalizedName, source)
|
return findKuaishouEticketShopConfigByName(normalizedName, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFirstAvailableKuaishouEticketShop(source = getKuaishouEticketSourceConfig()) {
|
export function getFirstAvailableKuaishouEticketShop(source: KuaishouEticketSourceConfig = getKuaishouEticketSourceConfig()): KuaishouEticketShopConfig | null {
|
||||||
return listKuaishouEticketShopConfigs(source).find((item) => item.enabled !== false && String(item.cookie || '').trim()) || null
|
return listKuaishouEticketShopConfigs(source).find((item) => item.enabled !== false && String(item.cookie || '').trim()) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadKuaishouEticketSourceConfigFromFile() {
|
function loadKuaishouEticketSourceConfigFromFile(): KuaishouEticketSourceConfig {
|
||||||
if (!fs.existsSync(KUAISHOU_ETICKET_SOURCE_FILE_PATH)) {
|
if (!fs.existsSync(KUAISHOU_ETICKET_SOURCE_FILE_PATH)) {
|
||||||
return createDefaultKuaishouEticketSourceConfig()
|
return createDefaultKuaishouEticketSourceConfig()
|
||||||
}
|
}
|
||||||
@@ -79,7 +93,7 @@ function loadKuaishouEticketSourceConfigFromFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeKuaishouEticketSourceConfig(rawValue) {
|
function normalizeKuaishouEticketSourceConfig(rawValue: unknown): KuaishouEticketSourceConfig {
|
||||||
const source = isPlainObject(rawValue) ? rawValue : {}
|
const source = isPlainObject(rawValue) ? rawValue : {}
|
||||||
const shops = Array.isArray(source.shops)
|
const shops = Array.isArray(source.shops)
|
||||||
? source.shops
|
? source.shops
|
||||||
@@ -94,7 +108,7 @@ function normalizeKuaishouEticketSourceConfig(rawValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeKuaishouEticketShopConfig(rawValue) {
|
function normalizeKuaishouEticketShopConfig(rawValue: unknown): KuaishouEticketShopConfig | null {
|
||||||
if (!isPlainObject(rawValue)) {
|
if (!isPlainObject(rawValue)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -118,7 +132,7 @@ function normalizeKuaishouEticketShopConfig(rawValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLegacySingleShopList(source) {
|
function buildLegacySingleShopList(source: JsonObject): JsonObject[] {
|
||||||
const cookie = String(source.cookie || '').trim()
|
const cookie = String(source.cookie || '').trim()
|
||||||
if (!cookie) {
|
if (!cookie) {
|
||||||
return []
|
return []
|
||||||
@@ -135,7 +149,7 @@ function buildLegacySingleShopList(source) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDefaultKuaishouEticketSourceConfig() {
|
function createDefaultKuaishouEticketSourceConfig(): KuaishouEticketSourceConfig {
|
||||||
return {
|
return {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
baseUrl: DEFAULT_BASE_URL,
|
baseUrl: DEFAULT_BASE_URL,
|
||||||
@@ -143,6 +157,6 @@ function createDefaultKuaishouEticketSourceConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value: unknown): value is JsonObject {
|
||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
@@ -795,6 +795,19 @@
|
|||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test` 共 139 个用例通过
|
- `npm test` 共 139 个用例通过
|
||||||
204. `src/services/admin` 目录下非测试 `.js` 服务文件已全部迁移为 `.ts`
|
204. `src/services/admin` 目录下非测试 `.js` 服务文件已全部迁移为 `.ts`
|
||||||
|
205. 快手电子券平台服务迁移到 `.ts`:
|
||||||
|
- `src/services/platforms/kuaishou-eticket/shared.ts`
|
||||||
|
- `src/services/platforms/kuaishou-eticket/http-client.ts`
|
||||||
|
- `src/services/platforms/kuaishou-eticket/source-config-service.ts`
|
||||||
|
- `src/services/platforms/kuaishou-eticket/info-service.ts`
|
||||||
|
- `src/services/platforms/kuaishou-eticket/consume-service.ts`
|
||||||
|
206. 快手小店核销配置、Node HTTP 请求、店铺来源配置读写、店铺稳定信息查询、电子券详情查询与核销 payload / result 映射已进入 TS 编译链路;config、headers、HTTP response、source/shop config、detail result 与动态 payload 补齐类型
|
||||||
|
207. Docker 内验证通过:
|
||||||
|
- `src/services/platforms/kuaishou-eticket/*.test.js` 共 4 个用例通过
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npm test` 共 139 个用例通过
|
||||||
|
208. `src/services` 目录下非测试 `.js` 服务文件已全部迁移为 `.ts`
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user