From 92d819c6161c9175b0571f46a4ea95ae9196676f Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 7 Jul 2026 14:02:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0feifei=E5=B1=A5=E7=BA=A6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/src/config/defaults.ts | 1 + .../src/routes/admin/platform-config.ts | 2 + .../admin/platform-config/kuaishou-feifei.ts | 62 +++ .../kuaishou-feifei-service.ts | 70 +++ .../platforms/kuaishou-feifei/config.ts | 59 ++- .../kuaishou-feifei/product-rule-service.ts | 25 +- .../source-config-service.test.ts | 40 ++ .../kuaishou-feifei/source-config-service.ts | 129 ++++++ apps/backend/src/types/runtime-config.ts | 4 + apps/frontend/src/components.d.ts | 2 + .../services/admin/platform-config/index.ts | 1 + .../admin/platform-config/kuaishou-feifei.ts | 26 ++ apps/frontend/src/types/admin/index.ts | 6 + .../src/types/admin/platform-config/index.ts | 9 + .../admin/platform-config/kuaishou-feifei.ts | 50 ++ .../AdminFulfillmentConfigHubView.vue | 20 +- .../AdminKuaishouFeifeiFulfillmentView.vue | 432 ++++++++++++++++++ 17 files changed, 927 insertions(+), 11 deletions(-) create mode 100644 apps/backend/src/routes/admin/platform-config/kuaishou-feifei.ts create mode 100644 apps/backend/src/services/admin/platform-config/kuaishou-feifei-service.ts create mode 100644 apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.test.ts create mode 100644 apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.ts create mode 100644 apps/frontend/src/services/admin/platform-config/kuaishou-feifei.ts create mode 100644 apps/frontend/src/types/admin/platform-config/kuaishou-feifei.ts create mode 100644 apps/frontend/src/views/admin/fulfillment/kuaishou-feifei/AdminKuaishouFeifeiFulfillmentView.vue diff --git a/apps/backend/src/config/defaults.ts b/apps/backend/src/config/defaults.ts index 22f8d2de..a4d390ac 100644 --- a/apps/backend/src/config/defaults.ts +++ b/apps/backend/src/config/defaults.ts @@ -58,6 +58,7 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig { version: "1", }, kuaishouFeifei: { + enabled: true, baseUrl: "http://skin-exchange.yiquyou.icu", appKey: "", appSecret: "", diff --git a/apps/backend/src/routes/admin/platform-config.ts b/apps/backend/src/routes/admin/platform-config.ts index c8555125..0fa896f2 100644 --- a/apps/backend/src/routes/admin/platform-config.ts +++ b/apps/backend/src/routes/admin/platform-config.ts @@ -2,6 +2,7 @@ import { Router } from "express"; import { requireAdminRoles } from "./session.js"; import cloudtentaclesRouter from "./platform-config/cloudtentacles.js"; +import kuaishouFeifeiRouter from "./platform-config/kuaishou-feifei.js"; import kuaishouEticketRouter from "./platform-config/kuaishou-eticket.js"; import ninetyoneRouter from "./platform-config/ninetyone.js"; import notificationsRouter from "./platform-config/notifications.js"; @@ -11,6 +12,7 @@ const router = Router(); router.use("/platform-config", requireAdminRoles(["admin"])); router.use("/platform-config", notificationsRouter); router.use("/platform-config", kuaishouEticketRouter); +router.use("/platform-config", kuaishouFeifeiRouter); router.use("/platform-config", ninetyoneRouter); router.use("/platform-config", cloudtentaclesRouter); diff --git a/apps/backend/src/routes/admin/platform-config/kuaishou-feifei.ts b/apps/backend/src/routes/admin/platform-config/kuaishou-feifei.ts new file mode 100644 index 00000000..56390a85 --- /dev/null +++ b/apps/backend/src/routes/admin/platform-config/kuaishou-feifei.ts @@ -0,0 +1,62 @@ +import { Router } from "express"; + +import { + getAdminKuaishouFeifeiConfig, + matchAdminKuaishouFeifeiProduct, + updateAdminKuaishouFeifeiConfig, +} from "../../../services/admin/platform-config/kuaishou-feifei-service.js"; +import { createJsonHandler } from "../session.js"; +import type { JsonRecord } from "../../../types/json.js"; + +const router = Router(); + +router.get( + "/kuaishou-feifei", + createJsonHandler(() => getAdminKuaishouFeifeiConfig(), { + successMessage: "ok", + errorMessage: "读取 kuaishou-feifei 配置失败", + scope: "[admin/platform-config/kuaishou-feifei]", + }) +); + +router.post( + "/kuaishou-feifei", + createJsonHandler( + (req) => updateAdminKuaishouFeifeiConfig(req.body as JsonRecord), + { + successMessage: "kuaishou-feifei 配置已保存", + errorMessage: "保存 kuaishou-feifei 配置失败", + scope: "[admin/platform-config/kuaishou-feifei]", + audit: (_req, data) => { + const result = data as JsonRecord; + const source = result.source as JsonRecord | undefined; + return { + action: "platform_kuaishou_feifei_config_updated", + targetType: "platform_config", + targetId: "kuaishou_feifei", + data: { + filePath: String(result.filePath || "").trim(), + enabled: source?.enabled !== false, + ruleCount: Array.isArray(source?.productRules) + ? source.productRules.length + : 0, + }, + }; + }, + } + ) +); + +router.post( + "/kuaishou-feifei/match", + createJsonHandler( + (req) => matchAdminKuaishouFeifeiProduct(req.body as JsonRecord), + { + successMessage: "ok", + errorMessage: "匹配 kuaishou-feifei 商品失败", + scope: "[admin/platform-config/kuaishou-feifei/match]", + } + ) +); + +export default router; diff --git a/apps/backend/src/services/admin/platform-config/kuaishou-feifei-service.ts b/apps/backend/src/services/admin/platform-config/kuaishou-feifei-service.ts new file mode 100644 index 00000000..ef45adff --- /dev/null +++ b/apps/backend/src/services/admin/platform-config/kuaishou-feifei-service.ts @@ -0,0 +1,70 @@ +import { getKuaishouFeifeiConfig } from '../../platforms/kuaishou-feifei/config.js' +import { resolveKuaishouFeifeiProductByName } from '../../platforms/kuaishou-feifei/product-rule-service.js' +import { + getKuaishouFeifeiConfigFilePath, + getKuaishouFeifeiSourceConfig, + hasKuaishouFeifeiConfigFile, + normalizeKuaishouFeifeiSourceConfig, + saveKuaishouFeifeiSourceConfig, +} from '../../platforms/kuaishou-feifei/source-config-service.js' +import { normalizeCloudtentaclesMatchName } from '../../order/cloudtentacles-match-utils.js' + +type JsonObject = Record + +export function getAdminKuaishouFeifeiConfig() { + const source = getAdminEditableKuaishouFeifeiConfig() + const effective = getKuaishouFeifeiConfig() + + return { + filePath: getKuaishouFeifeiConfigFilePath(), + source, + effective: mapEffectiveKuaishouFeifeiConfig(effective), + } +} + +export function updateAdminKuaishouFeifeiConfig(payload: JsonObject = {}) { + const saved = saveKuaishouFeifeiSourceConfig(payload) + const effective = getKuaishouFeifeiConfig() + + return { + filePath: getKuaishouFeifeiConfigFilePath(), + source: saved, + effective: mapEffectiveKuaishouFeifeiConfig(effective), + } +} + +export function matchAdminKuaishouFeifeiProduct(payload: JsonObject = {}) { + const productName = String(payload.productName || '').trim() + const source = getAdminEditableKuaishouFeifeiConfig() + const match = resolveKuaishouFeifeiProductByName(productName, { + enabled: source.enabled, + rules: source.productRules, + }) + + return { + productName, + normalizedProductName: normalizeCloudtentaclesMatchName(productName), + matched: Boolean(match), + match, + } +} + +function getAdminEditableKuaishouFeifeiConfig() { + if (hasKuaishouFeifeiConfigFile()) { + return getKuaishouFeifeiSourceConfig() + } + + return normalizeKuaishouFeifeiSourceConfig(getKuaishouFeifeiConfig()) +} + +function mapEffectiveKuaishouFeifeiConfig(config: ReturnType) { + return { + enabled: config.enabled !== false, + baseUrl: config.baseUrl, + timeoutMs: config.timeoutMs, + notifyUrl: config.notifyUrl, + hasAppKey: Boolean(config.appKey), + hasAppSecret: Boolean(config.appSecret), + productRuleCount: config.productRules.length, + } +} diff --git a/apps/backend/src/services/platforms/kuaishou-feifei/config.ts b/apps/backend/src/services/platforms/kuaishou-feifei/config.ts index 7c1de846..0f91dbca 100644 --- a/apps/backend/src/services/platforms/kuaishou-feifei/config.ts +++ b/apps/backend/src/services/platforms/kuaishou-feifei/config.ts @@ -1,6 +1,10 @@ import { runtimeConfig } from '../../../config/runtime.js' import { createHttpError } from '../../../utils/http.js' import type { RuntimeConfig } from '../../../types/runtime-config.js' +import { + getKuaishouFeifeiSourceConfig, + hasKuaishouFeifeiConfigFile, +} from './source-config-service.js' export const KUAISHOU_FEIFEI_EXECUTOR_KEY = 'kuaishou_feifei' export const KUAISHOU_FEIFEI_PROFILE_KEY = 'kuaishou_feifei' @@ -8,12 +12,14 @@ export const KUAISHOU_FEIFEI_PROFILE_KEY = 'kuaishou_feifei' type KuaishouFeifeiRuntimeConfig = RuntimeConfig['platforms']['kuaishouFeifei'] export function getKuaishouFeifeiConfig(overrides: Partial = {}) { - const config = { - ...(runtimeConfig.platforms?.kuaishouFeifei || {}), - ...overrides, - } + const runtimeValue = runtimeConfig.platforms?.kuaishouFeifei || {} + const savedValue = hasKuaishouFeifeiConfigFile() + ? getKuaishouFeifeiSourceConfig() + : null + const config = mergeKuaishouFeifeiConfig(runtimeValue, savedValue, overrides) return { + enabled: config.enabled !== false, baseUrl: String(config.baseUrl || 'http://skin-exchange.yiquyou.icu').trim().replace(/\/+$/, ''), appKey: String(config.appKey || '').trim(), appSecret: String(config.appSecret || '').trim(), @@ -26,6 +32,13 @@ export function getKuaishouFeifeiConfig(overrides: Partial, + savedValue: Partial | null, + overrides: Partial, +): KuaishouFeifeiRuntimeConfig { + const base: KuaishouFeifeiRuntimeConfig = savedValue + ? { + enabled: savedValue.enabled !== false, + baseUrl: savedValue.baseUrl || runtimeValue.baseUrl || 'http://skin-exchange.yiquyou.icu', + appKey: savedValue.appKey || runtimeValue.appKey || '', + appSecret: savedValue.appSecret || runtimeValue.appSecret || '', + timeoutMs: savedValue.timeoutMs || runtimeValue.timeoutMs || 10000, + notifyUrl: savedValue.notifyUrl || runtimeValue.notifyUrl || '', + productRules: Array.isArray(savedValue.productRules) + ? savedValue.productRules + : runtimeValue.productRules || [], + } + : { + enabled: runtimeValue.enabled !== false, + baseUrl: runtimeValue.baseUrl || 'http://skin-exchange.yiquyou.icu', + appKey: runtimeValue.appKey || '', + appSecret: runtimeValue.appSecret || '', + timeoutMs: runtimeValue.timeoutMs || 10000, + notifyUrl: runtimeValue.notifyUrl || '', + productRules: runtimeValue.productRules || [], + } + + return { + enabled: overrides.enabled ?? base.enabled, + baseUrl: overrides.baseUrl ?? base.baseUrl, + appKey: overrides.appKey ?? base.appKey, + appSecret: overrides.appSecret ?? base.appSecret, + timeoutMs: overrides.timeoutMs ?? base.timeoutMs, + notifyUrl: overrides.notifyUrl ?? base.notifyUrl, + productRules: overrides.productRules ?? base.productRules, + } +} diff --git a/apps/backend/src/services/platforms/kuaishou-feifei/product-rule-service.ts b/apps/backend/src/services/platforms/kuaishou-feifei/product-rule-service.ts index 0c3ee39e..5f4db9f5 100644 --- a/apps/backend/src/services/platforms/kuaishou-feifei/product-rule-service.ts +++ b/apps/backend/src/services/platforms/kuaishou-feifei/product-rule-service.ts @@ -1,5 +1,5 @@ -import { runtimeConfig } from '../../../config/runtime.js' import { normalizeCloudtentaclesMatchName } from '../../order/cloudtentacles-match-utils.js' +import { getKuaishouFeifeiConfig } from './config.js' import type { KuaishouFeifeiProductRule } from '../../../types/runtime-config.js' export type KuaishouFeifeiProductMatch = { @@ -12,15 +12,19 @@ export type KuaishouFeifeiProductMatch = { export function resolveKuaishouFeifeiProductByName( productName: unknown, + options: { + rules?: KuaishouFeifeiProductRule[] + enabled?: boolean + } = {}, ): KuaishouFeifeiProductMatch | null { const normalizedProductName = normalizeCloudtentaclesMatchName(productName) if (!normalizedProductName) { return null } - const rules = listKuaishouFeifeiProductRules() + const rules = listKuaishouFeifeiProductRules(options) const matched = rules.find((rule) => - normalizeCloudtentaclesMatchName(rule.productName) === normalizedProductName, + normalizeCloudtentaclesMatchName(rule.productName || rule.normalizedProductName) === normalizedProductName, ) if (!matched) { @@ -41,14 +45,25 @@ export function resolveKuaishouFeifeiProductByName( } } -function listKuaishouFeifeiProductRules(): KuaishouFeifeiProductRule[] { - const rules = runtimeConfig.platforms?.kuaishouFeifei?.productRules +function listKuaishouFeifeiProductRules(options: { + rules?: KuaishouFeifeiProductRule[] + enabled?: boolean +}): KuaishouFeifeiProductRule[] { + const config = options.rules ? null : getKuaishouFeifeiConfig() + if (options.enabled === false || config?.enabled === false) { + return [] + } + + const rules = options.rules || config?.productRules return (Array.isArray(rules) ? rules : []) .map((rule) => ({ + id: String(rule.id || '').trim(), productName: String(rule.productName || '').trim(), + normalizedProductName: String(rule.normalizedProductName || '').trim(), productCode: String(rule.productCode || '').trim(), skuName: String(rule.skuName || '').trim(), enabled: rule.enabled !== false, + notes: String(rule.notes || '').trim(), })) .filter((rule) => rule.enabled && rule.productName && rule.productCode) } diff --git a/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.test.ts b/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.test.ts new file mode 100644 index 00000000..abcfcccf --- /dev/null +++ b/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.test.ts @@ -0,0 +1,40 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { normalizeKuaishouFeifeiSourceConfig } from './source-config-service.js' + +test('normalizeKuaishouFeifeiSourceConfig keeps valid product rules and drops invalid ones', () => { + const config = normalizeKuaishouFeifeiSourceConfig({ + enabled: true, + baseUrl: ' https://feifei.example.com/ ', + appKey: ' app-key ', + appSecret: ' secret ', + timeoutMs: 0, + productRules: [ + { + productName: '套装-Alan Walker', + productCode: 'FF-1001', + skuName: 'Alan Walker 套装', + notes: '测试规则', + }, + { + productName: '套装-Alan Walker', + productCode: 'FF-1002', + }, + { + productName: '缺少编码', + productCode: '', + }, + ], + }) + + assert.equal(config.enabled, true) + assert.equal(config.baseUrl, 'https://feifei.example.com/') + assert.equal(config.appKey, 'app-key') + assert.equal(config.appSecret, 'secret') + assert.equal(config.timeoutMs, 10000) + assert.equal(config.productRules.length, 1) + assert.equal(config.productRules[0].productName, '套装-Alan Walker') + assert.equal(config.productRules[0].productCode, 'FF-1002') + assert.equal(config.productRules[0].normalizedProductName, '套装 alan walker') +}) diff --git a/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.ts b/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.ts new file mode 100644 index 00000000..88040569 --- /dev/null +++ b/apps/backend/src/services/platforms/kuaishou-feifei/source-config-service.ts @@ -0,0 +1,129 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { PROJECT_ROOT } from '../../../config/runtime.js' +import { readJsonFile, writeJsonFile } from '../../../utils/json-file-store.js' +import { normalizeCloudtentaclesMatchName } from '../../order/cloudtentacles-match-utils.js' +import type { KuaishouFeifeiProductRule } from '../../../types/runtime-config.js' + +const KUAISHOU_FEIFEI_CONFIG_FILE_PATH = path.join( + PROJECT_ROOT, + 'data', + 'kuaishou-feifei-config.json', +) + +type JsonObject = Record + +export type KuaishouFeifeiSourceConfig = { + enabled: boolean + baseUrl: string + appKey: string + appSecret: string + timeoutMs: number + notifyUrl: string + productRules: KuaishouFeifeiProductRule[] +} + +export function getKuaishouFeifeiConfigFilePath() { + return KUAISHOU_FEIFEI_CONFIG_FILE_PATH +} + +export function hasKuaishouFeifeiConfigFile() { + return fs.existsSync(KUAISHOU_FEIFEI_CONFIG_FILE_PATH) +} + +export function getKuaishouFeifeiSourceConfig(): KuaishouFeifeiSourceConfig { + return readJsonFile( + KUAISHOU_FEIFEI_CONFIG_FILE_PATH, + createDefaultKuaishouFeifeiSourceConfig, + normalizeKuaishouFeifeiSourceConfig, + ) +} + +export function saveKuaishouFeifeiSourceConfig(rawValue: unknown): KuaishouFeifeiSourceConfig { + return writeJsonFile( + KUAISHOU_FEIFEI_CONFIG_FILE_PATH, + rawValue, + normalizeKuaishouFeifeiSourceConfig, + ) +} + +export function normalizeKuaishouFeifeiSourceConfig(rawValue: unknown): KuaishouFeifeiSourceConfig { + const source = isPlainObject(rawValue) ? rawValue : {} + + return { + enabled: source.enabled !== false, + baseUrl: String(source.baseUrl || 'http://skin-exchange.yiquyou.icu').trim(), + appKey: String(source.appKey || '').trim(), + appSecret: String(source.appSecret || '').trim(), + timeoutMs: normalizePositiveInteger(source.timeoutMs, 10000), + notifyUrl: String(source.notifyUrl || '').trim(), + productRules: normalizeKuaishouFeifeiProductRules(source.productRules), + } +} + +export function normalizeKuaishouFeifeiProductRules(value: unknown): KuaishouFeifeiProductRule[] { + const rules = Array.isArray(value) ? value : [] + const normalizedRules = rules + .map((item) => normalizeKuaishouFeifeiProductRule(item)) + .filter((item): item is KuaishouFeifeiProductRule => Boolean(item)) + + return dedupeProductRules(normalizedRules) +} + +function normalizeKuaishouFeifeiProductRule(rawValue: unknown): KuaishouFeifeiProductRule | null { + const source = isPlainObject(rawValue) ? rawValue : {} + const productName = String(source.productName || source.name || '').trim() + const normalizedProductName = normalizeCloudtentaclesMatchName(productName) + const productCode = String(source.productCode || source.code || '').trim() + + if (!productName || !normalizedProductName || !productCode) { + return null + } + + return { + id: String(source.id || normalizedProductName).trim() || normalizedProductName, + enabled: source.enabled !== false, + productName, + normalizedProductName, + productCode, + skuName: String(source.skuName || source.productName || productName).trim(), + notes: String(source.notes || '').trim(), + } +} + +function dedupeProductRules(rules: KuaishouFeifeiProductRule[]) { + const map = new Map() + + for (const rule of rules) { + const key = normalizeCloudtentaclesMatchName(rule.productName) + if (!key) { + continue + } + + map.set(key, rule) + } + + return Array.from(map.values()) +} + +function createDefaultKuaishouFeifeiSourceConfig(): KuaishouFeifeiSourceConfig { + return { + enabled: true, + baseUrl: 'http://skin-exchange.yiquyou.icu', + appKey: '', + appSecret: '', + timeoutMs: 10000, + notifyUrl: '', + productRules: [], + } +} + +function normalizePositiveInteger(value: unknown, fallback: number) { + const parsed = Number(value) + return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback +} + +function isPlainObject(value: unknown): value is JsonObject { + return Object.prototype.toString.call(value) === '[object Object]' +} diff --git a/apps/backend/src/types/runtime-config.ts b/apps/backend/src/types/runtime-config.ts index b37dd05b..07cdb5f5 100644 --- a/apps/backend/src/types/runtime-config.ts +++ b/apps/backend/src/types/runtime-config.ts @@ -6,10 +6,13 @@ export type AdminDefaultUser = { }; export type KuaishouFeifeiProductRule = { + id?: string; productName: string; + normalizedProductName?: string; productCode: string; skuName?: string; enabled?: boolean; + notes?: string; }; export type RuntimeConfig = { @@ -98,6 +101,7 @@ export type RuntimeConfig = { version: string; }; kuaishouFeifei: { + enabled: boolean; baseUrl: string; appKey: string; appSecret: string; diff --git a/apps/frontend/src/components.d.ts b/apps/frontend/src/components.d.ts index 16d855cf..16e1ba1f 100644 --- a/apps/frontend/src/components.d.ts +++ b/apps/frontend/src/components.d.ts @@ -47,6 +47,8 @@ declare module 'vue' { ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTabPane: typeof import('element-plus/es')['ElTabPane'] + ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/apps/frontend/src/services/admin/platform-config/index.ts b/apps/frontend/src/services/admin/platform-config/index.ts index 73bc8a95..9f73f850 100644 --- a/apps/frontend/src/services/admin/platform-config/index.ts +++ b/apps/frontend/src/services/admin/platform-config/index.ts @@ -2,4 +2,5 @@ export * from './notifications' export * from './scheduled-jobs' export * from './ninetyone' export * from './kuaishou-eticket' +export * from './kuaishou-feifei' export * from './cloudtentacles' diff --git a/apps/frontend/src/services/admin/platform-config/kuaishou-feifei.ts b/apps/frontend/src/services/admin/platform-config/kuaishou-feifei.ts new file mode 100644 index 00000000..2ec372c1 --- /dev/null +++ b/apps/frontend/src/services/admin/platform-config/kuaishou-feifei.ts @@ -0,0 +1,26 @@ +import { apiGet, apiPost } from '@/lib/http' +import type { + AdminKuaishouFeifeiConfig, + AdminKuaishouFeifeiConfigResponse, + AdminKuaishouFeifeiMatchResult, +} from '@/types/admin' + +export function fetchAdminKuaishouFeifeiConfig() { + return apiGet( + '/api/v1/admin/platform-config/kuaishou-feifei', + ) +} + +export function saveAdminKuaishouFeifeiConfig(payload: AdminKuaishouFeifeiConfig) { + return apiPost( + '/api/v1/admin/platform-config/kuaishou-feifei', + payload, + ) +} + +export function matchAdminKuaishouFeifeiProduct(productName: string) { + return apiPost( + '/api/v1/admin/platform-config/kuaishou-feifei/match', + { productName }, + ) +} diff --git a/apps/frontend/src/types/admin/index.ts b/apps/frontend/src/types/admin/index.ts index 7153287b..26efc0a2 100644 --- a/apps/frontend/src/types/admin/index.ts +++ b/apps/frontend/src/types/admin/index.ts @@ -50,6 +50,12 @@ export type { AdminKuaishouEticketShopInfoResult, AdminKuaishouEticketDetailResult, AdminKuaishouEticketConsumeResult, + AdminKuaishouFeifeiProductRule, + AdminKuaishouFeifeiConfig, + AdminKuaishouFeifeiEffectiveConfig, + AdminKuaishouFeifeiConfigResponse, + AdminKuaishouFeifeiProductMatch, + AdminKuaishouFeifeiMatchResult, AdminCloudtentaclesSourceItem, AdminCloudtentaclesSourcesConfig, AdminCloudtentaclesSourceConfigResponse, diff --git a/apps/frontend/src/types/admin/platform-config/index.ts b/apps/frontend/src/types/admin/platform-config/index.ts index 7927b341..87e8e93d 100644 --- a/apps/frontend/src/types/admin/platform-config/index.ts +++ b/apps/frontend/src/types/admin/platform-config/index.ts @@ -30,6 +30,15 @@ export type { AdminKuaishouEticketConsumeResult, } from './kuaishou-eticket' +export type { + AdminKuaishouFeifeiProductRule, + AdminKuaishouFeifeiConfig, + AdminKuaishouFeifeiEffectiveConfig, + AdminKuaishouFeifeiConfigResponse, + AdminKuaishouFeifeiProductMatch, + AdminKuaishouFeifeiMatchResult, +} from './kuaishou-feifei' + export type { AdminCloudtentaclesSourceItem, AdminCloudtentaclesSourcesConfig, diff --git a/apps/frontend/src/types/admin/platform-config/kuaishou-feifei.ts b/apps/frontend/src/types/admin/platform-config/kuaishou-feifei.ts new file mode 100644 index 00000000..d7c8434e --- /dev/null +++ b/apps/frontend/src/types/admin/platform-config/kuaishou-feifei.ts @@ -0,0 +1,50 @@ +export interface AdminKuaishouFeifeiProductRule { + id: string + enabled: boolean + productName: string + normalizedProductName: string + productCode: string + skuName: string + notes: string +} + +export interface AdminKuaishouFeifeiConfig { + enabled: boolean + baseUrl: string + appKey: string + appSecret: string + timeoutMs: number + notifyUrl: string + productRules: AdminKuaishouFeifeiProductRule[] +} + +export interface AdminKuaishouFeifeiEffectiveConfig { + enabled: boolean + baseUrl: string + timeoutMs: number + notifyUrl: string + hasAppKey: boolean + hasAppSecret: boolean + productRuleCount: number +} + +export interface AdminKuaishouFeifeiConfigResponse { + filePath: string + source: AdminKuaishouFeifeiConfig + effective: AdminKuaishouFeifeiEffectiveConfig +} + +export interface AdminKuaishouFeifeiProductMatch { + matchMode: string + productName: string + normalizedProductName: string + productCode: string + skuName: string +} + +export interface AdminKuaishouFeifeiMatchResult { + productName: string + normalizedProductName: string + matched: boolean + match: AdminKuaishouFeifeiProductMatch | null +} diff --git a/apps/frontend/src/views/admin/fulfillment/AdminFulfillmentConfigHubView.vue b/apps/frontend/src/views/admin/fulfillment/AdminFulfillmentConfigHubView.vue index e4e151b6..b51c35c8 100644 --- a/apps/frontend/src/views/admin/fulfillment/AdminFulfillmentConfigHubView.vue +++ b/apps/frontend/src/views/admin/fulfillment/AdminFulfillmentConfigHubView.vue @@ -1,15 +1,27 @@ @@ -18,4 +30,8 @@ import AdminKuaishouCloudFulfillmentView from './kuaishou-cloud/AdminKuaishouClo display: grid; gap: var(--space-4); } + +.fulfillment-tabs { + min-width: 0; +} diff --git a/apps/frontend/src/views/admin/fulfillment/kuaishou-feifei/AdminKuaishouFeifeiFulfillmentView.vue b/apps/frontend/src/views/admin/fulfillment/kuaishou-feifei/AdminKuaishouFeifeiFulfillmentView.vue new file mode 100644 index 00000000..e5bbd84f --- /dev/null +++ b/apps/frontend/src/views/admin/fulfillment/kuaishou-feifei/AdminKuaishouFeifeiFulfillmentView.vue @@ -0,0 +1,432 @@ + + + + +