From b31591e04c71f05459ae4e11bb295ed86b9b1b87 Mon Sep 17 00:00:00 2001 From: wurenzhi Date: Wed, 18 Mar 2026 13:38:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=A4=9A=E5=95=86?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E4=B8=8E=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor: 优化服务层代码并修复类型问题 docs: 更新开发进度文档 feat(merchant): 新增商户监控与数据统计服务 feat(dashboard): 添加商户管理前端页面与服务 fix: 修复类型转换与可选参数处理 feat: 实现商户订单、店铺与结算管理功能 refactor: 重构审计日志格式与服务调用 feat: 新增商户入驻与身份注册功能 fix(controller): 修复路由参数类型问题 feat: 添加商户排名与统计报告功能 chore: 更新模拟数据与服务配置 --- dashboard/src/.umi/appData.json | 15846 +++++++++++++++- dashboard/src/.umi/core/route.tsx | 9 +- .../src/pages/Blacklist/BlacklistManage.tsx | 829 + dashboard/src/pages/Blacklist/RiskMonitor.tsx | 705 + .../src/pages/Merchant/MerchantManage.tsx | 237 + .../pages/Merchant/MerchantOrderManage.tsx | 273 + .../Merchant/MerchantSettlementManage.tsx | 315 + .../src/pages/Merchant/MerchantShopManage.tsx | 358 + dashboard/src/pages/Merchant/index.ts | 18 + .../src/services/merchantOrderService.ts | 135 + dashboard/src/services/merchantService.ts | 137 + .../src/services/merchantSettlementService.ts | 133 + dashboard/src/services/merchantShopService.ts | 139 + docs/00_Business/Task_Overview.md | 54 +- docs/Development_Progress.md | 109 +- server/src/api/controllers/AdOpsController.ts | 4 +- .../api/controllers/BizStrategyController.ts | 183 +- server/src/api/controllers/OrderController.ts | 28 +- .../src/api/controllers/ProductController.ts | 50 +- .../src/api/controllers/ReportController.ts | 2 +- .../api/controllers/SettlementController.ts | 2 +- .../api/controllers/TelemetryController.ts | 34 +- server/src/api/controllers/TraceController.ts | 6 +- server/src/api/routes/sovereignty.ts | 9 +- server/src/api/routes/trade.ts | 39 +- server/src/config/database.ts | 2 +- server/src/core/ai/MerchantAnalysisService.ts | 293 + .../src/core/ai/MerchantPredictionService.ts | 402 + server/src/core/ai/ReputationZKPService.ts | 34 + .../core/ai/SettlementOptimizationService.ts | 435 + .../src/core/security/DIDHandshakeService.ts | 23 + .../security/ProofOfComputationService.ts | 26 + .../core/telemetry/NetworkTopologyService.ts | 2 +- server/src/domains/Billing/BillingService.ts | 22 + .../src/domains/Finance/OrderProfitService.ts | 28 + .../domains/Trade/SourcingRoutingService.ts | 4 +- server/src/services/AutonomousEcoService.ts | 26 +- .../src/services/AutonomousSandboxService.ts | 18 + .../src/services/AutonomousSourcingService.ts | 23 +- .../src/services/BlacklistAnalysisService.ts | 695 + server/src/services/CompetitorPulseService.ts | 13 +- server/src/services/CrawlerService.ts | 28 + server/src/services/DynamicPricingService.ts | 24 + server/src/services/FinanceService.ts | 35 + .../src/services/InventoryForecastService.ts | 15 + .../src/services/MerchantAnalysisService.ts | 765 + .../MerchantBehaviorAnalysisService.ts | 259 + .../services/MerchantDataStatisticsService.ts | 204 + .../src/services/MerchantIntegrationTest.ts | 268 + server/src/services/MerchantMonitorService.ts | 199 + .../src/services/MerchantPerformanceTest.ts | 277 + server/src/services/OrderService.ts | 2 +- server/src/services/ServiceHealthCheck.ts | 275 + .../services/SovereigntyGovernanceService.ts | 61 +- .../services/SovereigntyIdentityService.ts | 48 +- .../services/SovereigntyReputationService.ts | 16 +- .../services/SovereigntySettlementService.ts | 36 +- 57 files changed, 24055 insertions(+), 157 deletions(-) create mode 100644 dashboard/src/pages/Blacklist/BlacklistManage.tsx create mode 100644 dashboard/src/pages/Blacklist/RiskMonitor.tsx create mode 100644 dashboard/src/pages/Merchant/MerchantManage.tsx create mode 100644 dashboard/src/pages/Merchant/MerchantOrderManage.tsx create mode 100644 dashboard/src/pages/Merchant/MerchantSettlementManage.tsx create mode 100644 dashboard/src/pages/Merchant/MerchantShopManage.tsx create mode 100644 dashboard/src/pages/Merchant/index.ts create mode 100644 dashboard/src/services/merchantOrderService.ts create mode 100644 dashboard/src/services/merchantService.ts create mode 100644 dashboard/src/services/merchantSettlementService.ts create mode 100644 dashboard/src/services/merchantShopService.ts create mode 100644 server/src/core/ai/MerchantAnalysisService.ts create mode 100644 server/src/core/ai/MerchantPredictionService.ts create mode 100644 server/src/core/ai/ReputationZKPService.ts create mode 100644 server/src/core/ai/SettlementOptimizationService.ts create mode 100644 server/src/services/BlacklistAnalysisService.ts create mode 100644 server/src/services/CrawlerService.ts create mode 100644 server/src/services/MerchantAnalysisService.ts create mode 100644 server/src/services/MerchantBehaviorAnalysisService.ts create mode 100644 server/src/services/MerchantDataStatisticsService.ts create mode 100644 server/src/services/MerchantIntegrationTest.ts create mode 100644 server/src/services/MerchantMonitorService.ts create mode 100644 server/src/services/MerchantPerformanceTest.ts create mode 100644 server/src/services/ServiceHealthCheck.ts diff --git a/dashboard/src/.umi/appData.json b/dashboard/src/.umi/appData.json index f0f6a98..993e5b5 100644 --- a/dashboard/src/.umi/appData.json +++ b/dashboard/src/.umi/appData.json @@ -101,6 +101,13 @@ "time": { "hooks": { "modifyRoutes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, 0 ] }, @@ -116,7 +123,27 @@ "key": "aiDev", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onDevCompileDone": [ + 63, + 27, + 9, + 75, + 6, + 4, + 4, + 8, + 136, + 9, + 5, + 7, + 437, + 3, + 5, + 6, + 7 + ] + }, "register": 4 }, "enableBy": "register" @@ -132,6 +159,16 @@ "hooks": { "modifyAppData": [ 353 + ], + "onGenerateFiles": [ + 10, + 21, + 83, + 21, + 30, + 114, + 26, + 63 ] }, "register": 54 @@ -146,7 +183,18 @@ "key": "umiInfo", "config": {}, "time": { - "hooks": {}, + "hooks": { + "addEntryCode": [ + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 1 }, "enableBy": "register" @@ -165,6 +213,47 @@ ], "onCheck": [ 1 + ], + "onPrepareBuildSuccess": [ + 0, + 0, + 0, + 0 + ], + "onCheckCode": [ + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] }, "register": 2 @@ -1095,7 +1184,11 @@ "key": "devTool", "config": {}, "time": { - "hooks": {}, + "hooks": { + "addBeforeMiddlewares": [ + 12 + ] + }, "register": 5 }, "enableBy": "register" @@ -1150,6 +1243,23 @@ "hooks": { "modifyAppData": [ 0 + ], + "addBeforeMiddlewares": [ + 0 + ], + "modifyHTMLFavicon": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] }, "register": 2 @@ -1164,7 +1274,28 @@ "key": "helmet", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 3, + 1, + 1, + 1, + 1, + 2, + 1, + 1 + ], + "addRuntimePlugin": [ + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 1 }, "enableBy": "register" @@ -1193,6 +1324,9 @@ "hooks": { "onStart": [ 3 + ], + "addBeforeMiddlewares": [ + 2 ] }, "register": 125 @@ -1231,7 +1365,36 @@ "key": "overrides", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "addEntryImports": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 1 }, "enableBy": "register" @@ -1260,6 +1423,34 @@ "hooks": { "modifyConfig": [ 1 + ], + "onGenerateFiles": [ + 345, + 67, + 150, + 53, + 46, + 67, + 54, + 291 + ], + "addPolyfillImports": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ] }, "register": 4 @@ -1273,7 +1464,26 @@ "key": "publicPathPolyfill", "config": {}, "time": { - "hooks": {}, + "hooks": { + "addPolyfillImports": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 2 }, "enableBy": "register" @@ -1286,7 +1496,18 @@ "key": "prepare", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 547, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 7 }, "enableBy": "register" @@ -1325,7 +1546,21 @@ "key": "terminal", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 2, + 0, + 5, + 1, + 0, + 1, + 1, + 0 + ], + "addBeforeMiddlewares": [ + 1 + ] + }, "register": 2 }, "enableBy": "register" @@ -1338,7 +1573,26 @@ "key": "tmpFiles", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 98, + 12, + 244, + 2, + 2709, + 46, + 277, + 3, + 433, + 2, + 8841, + 37, + 977, + 3, + 7476, + 5 + ] + }, "register": 25 }, "enableBy": "register" @@ -1364,7 +1618,21 @@ "key": "routeProps", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1 + ], + "onBeforeCompiler": [ + 13 + ] + }, "register": 1 } }, @@ -1389,7 +1657,18 @@ "key": "configTypes", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 1149, + 38, + 198, + 20, + 35, + 131, + 23, + 97 + ] + }, "register": 9 }, "enableBy": "register" @@ -1402,7 +1681,11 @@ "key": "transform", "config": {}, "time": { - "hooks": {}, + "hooks": { + "addBeforeBabelPresets": [ + 0 + ] + }, "register": 15 }, "enableBy": "register" @@ -1466,7 +1749,18 @@ "key": "test", "config": {}, "time": { - "hooks": {}, + "hooks": { + "onGenerateFiles": [ + 4, + 2, + 1, + 2, + 2, + 61, + 2, + 1 + ] + }, "register": 3 } }, @@ -1595,7 +1889,22 @@ "key": "routePreloadOnLoad", "config": {}, "time": { - "hooks": {}, + "hooks": { + "addHTMLHeadScripts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, "register": 108 } }, @@ -1620,7 +1929,11 @@ "key": "preset-umi:bundler", "config": {}, "time": { - "hooks": {}, + "hooks": { + "modifyUniBundler": [ + 1403 + ] + }, "register": 5 }, "enableBy": "register" @@ -2002,6 +2315,15 @@ "__isJSFile": true, "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Compliance/CertificateExpiryReminder.tsx" }, + "Merchant/MerchantSettlementManage": { + "path": "Merchant/MerchantSettlementManage", + "id": "Merchant/MerchantSettlementManage", + "file": "Merchant/MerchantSettlementManage.tsx", + "absPath": "/Merchant/MerchantSettlementManage", + "__content": "import React, { useState, useEffect } from 'react';\nimport { Table, Input, Button, Select, DatePicker, message, Card, Typography, Tag, Modal, Form } from 'antd';\nimport { SearchOutlined, EyeOutlined, DownloadOutlined, ReloadOutlined } from '@ant-design/icons';\nimport { getMerchantSettlements, processSettlement } from '../../services/merchantSettlementService';\n\nconst { Option } = Select;\nconst { Title, Text } = Typography;\nconst { RangePicker } = DatePicker;\nconst { Item } = Form;\n\ninterface Settlement {\n id: string;\n merchantId: string;\n merchantName: string;\n periodStart: string;\n periodEnd: string;\n totalAmount: number;\n status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';\n createdAt: string;\n updatedAt: string;\n}\n\nconst MerchantSettlementManage: React.FC = () => {\n const [settlements, setSettlements] = useState([]);\n const [merchants, setMerchants] = useState<{ id: string; companyName: string }[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchText, setSearchText] = useState('');\n const [merchantFilter, setMerchantFilter] = useState('');\n const [statusFilter, setStatusFilter] = useState('');\n const [dateRange, setDateRange] = useState<[string, string] | null>(null);\n const [isModalVisible, setIsModalVisible] = useState(false);\n const [selectedSettlement, setSelectedSettlement] = useState(null);\n const [form] = Form.useForm();\n\n const fetchSettlements = async () => {\n setLoading(true);\n try {\n const response = await getMerchantSettlements();\n setSettlements(response.data);\n } catch (error) {\n message.error('获取结算列表失败');\n console.error('获取结算列表失败:', error);\n } finally {\n setLoading(false);\n }\n };\n\n const fetchMerchants = async () => {\n try {\n // 实际项目中应该调用获取商户列表的API\n // 这里模拟数据\n setMerchants([\n { id: '1', companyName: '商户A' },\n { id: '2', companyName: '商户B' },\n { id: '3', companyName: '商户C' },\n ]);\n } catch (error) {\n console.error('获取商户列表失败:', error);\n }\n };\n\n useEffect(() => {\n fetchMerchants();\n fetchSettlements();\n }, []);\n\n const handleSearch = (value: string) => {\n setSearchText(value);\n };\n\n const handleMerchantFilter = (value: string) => {\n setMerchantFilter(value);\n };\n\n const handleStatusFilter = (value: string) => {\n setStatusFilter(value);\n };\n\n const handleDateRangeChange = (dates: any) => {\n if (dates) {\n setDateRange([dates[0].format('YYYY-MM-DD'), dates[1].format('YYYY-MM-DD')]);\n } else {\n setDateRange(null);\n }\n };\n\n const filteredSettlements = settlements.filter(settlement => {\n const matchesSearch = settlement.id.includes(searchText) ||\n settlement.merchantName.toLowerCase().includes(searchText.toLowerCase());\n const matchesMerchant = merchantFilter ? settlement.merchantId === merchantFilter : true;\n const matchesStatus = statusFilter ? settlement.status === statusFilter : true;\n return matchesSearch && matchesMerchant && matchesStatus;\n });\n\n const handleView = (settlement: Settlement) => {\n setSelectedSettlement(settlement);\n setIsModalVisible(true);\n };\n\n const handleProcess = (settlement: Settlement) => {\n // 调用API处理结算\n message.info(`处理结算: ${settlement.id}`);\n };\n\n const handleDownload = (settlement: Settlement) => {\n // 下载结算单\n message.info(`下载结算单: ${settlement.id}`);\n };\n\n const handleRefresh = () => {\n fetchSettlements();\n };\n\n const getStatusTag = (status: string) => {\n const statusConfig: Record = {\n PENDING: { color: 'blue', text: '待处理' },\n PROCESSING: { color: 'orange', text: '处理中' },\n COMPLETED: { color: 'green', text: '已完成' },\n FAILED: { color: 'red', text: '失败' },\n };\n const config = statusConfig[status] || { color: 'default', text: status };\n return {config.text};\n };\n\n const columns = [\n {\n title: '结算单ID',\n dataIndex: 'id',\n key: 'id',\n ellipsis: true,\n },\n {\n title: '商户',\n dataIndex: 'merchantName',\n key: 'merchantName',\n ellipsis: true,\n },\n {\n title: '结算周期',\n key: 'period',\n render: (_, settlement: Settlement) => (\n \n {settlement.periodStart} 至 {settlement.periodEnd}\n \n ),\n },\n {\n title: '结算金额',\n dataIndex: 'totalAmount',\n key: 'totalAmount',\n render: (amount: number) => ${amount.toFixed(2)},\n },\n {\n title: '状态',\n dataIndex: 'status',\n key: 'status',\n render: (status: string) => getStatusTag(status),\n },\n {\n title: '创建时间',\n dataIndex: 'createdAt',\n key: 'createdAt',\n ellipsis: true,\n },\n {\n title: '操作',\n key: 'action',\n render: (_: any, settlement: Settlement) => (\n
\n \n \n {settlement.status === 'PENDING' && (\n \n )}\n
\n ),\n },\n ];\n\n return (\n \n
\n 多商户结算管理\n \n
\n\n
\n }\n style={{ width: 300 }}\n onChange={(e) => handleSearch(e.target.value)}\n />\n \n {merchants.map(merchant => (\n \n ))}\n \n \n \n \n \n \n \n \n
\n\n \n\n setIsModalVisible(false)}\n footer={[\n ,\n ]}\n width={600}\n >\n {selectedSettlement && (\n
\n
\n 结算单ID: {selectedSettlement.id}\n
\n
\n 商户: {selectedSettlement.merchantName}\n
\n
\n 结算周期: {selectedSettlement.periodStart} 至 {selectedSettlement.periodEnd}\n
\n
\n 结算金额: ${selectedSettlement.totalAmount.toFixed(2)}\n
\n
\n 状态: {getStatusTag(selectedSettlement.status)}\n
\n
\n 创建时间: {selectedSettlement.createdAt}\n
\n
\n 更新时间: {selectedSettlement.updatedAt}\n
\n
\n 结算明细\n `$${amount.toFixed(2)}` },\n ]}\n dataSource={[\n { key: '1', item: '商品销售', amount: selectedSettlement.totalAmount * 0.9 },\n { key: '2', item: '平台服务费', amount: selectedSettlement.totalAmount * 0.1 },\n ]}\n pagination={false}\n />\n
\n
\n )}\n \n
\n );\n};\n\nexport default MerchantSettlementManage;", + "__isJSFile": true, + "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Merchant/MerchantSettlementManage.tsx" + }, "Compliance/CertificateManage": { "path": "Compliance/CertificateManage", "id": "Compliance/CertificateManage", @@ -2011,6 +2333,24 @@ "__isJSFile": true, "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Compliance/CertificateManage.tsx" }, + "Merchant/MerchantOrderManage": { + "path": "Merchant/MerchantOrderManage", + "id": "Merchant/MerchantOrderManage", + "file": "Merchant/MerchantOrderManage.tsx", + "absPath": "/Merchant/MerchantOrderManage", + "__content": "import React, { useState, useEffect } from 'react';\nimport { Table, Input, Button, Select, DatePicker, message, Card, Typography, Tag } from 'antd';\nimport { SearchOutlined, EyeOutlined, ReloadOutlined } from '@ant-design/icons';\nimport { getMerchantOrders, updateOrderStatus } from '../../services/merchantOrderService';\n\nconst { Option } = Select;\nconst { Title, Text } = Typography;\nconst { RangePicker } = DatePicker;\n\ninterface Order {\n id: string;\n merchantId: string;\n merchantName: string;\n orderId: string;\n platform: string;\n totalAmount: number;\n status: 'PENDING' | 'PROCESSING' | 'SHIPPED' | 'DELIVERED' | 'CANCELLED' | 'REFUNDED';\n createdAt: string;\n updatedAt: string;\n}\n\nconst MerchantOrderManage: React.FC = () => {\n const [orders, setOrders] = useState([]);\n const [merchants, setMerchants] = useState<{ id: string; companyName: string }[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchText, setSearchText] = useState('');\n const [merchantFilter, setMerchantFilter] = useState('');\n const [platformFilter, setPlatformFilter] = useState('');\n const [statusFilter, setStatusFilter] = useState('');\n const [dateRange, setDateRange] = useState<[string, string] | null>(null);\n\n const fetchOrders = async () => {\n setLoading(true);\n try {\n const response = await getMerchantOrders();\n setOrders(response.data);\n } catch (error) {\n message.error('获取订单列表失败');\n console.error('获取订单列表失败:', error);\n } finally {\n setLoading(false);\n }\n };\n\n const fetchMerchants = async () => {\n try {\n // 实际项目中应该调用获取商户列表的API\n // 这里模拟数据\n setMerchants([\n { id: '1', companyName: '商户A' },\n { id: '2', companyName: '商户B' },\n { id: '3', companyName: '商户C' },\n ]);\n } catch (error) {\n console.error('获取商户列表失败:', error);\n }\n };\n\n useEffect(() => {\n fetchMerchants();\n fetchOrders();\n }, []);\n\n const handleSearch = (value: string) => {\n setSearchText(value);\n };\n\n const handleMerchantFilter = (value: string) => {\n setMerchantFilter(value);\n };\n\n const handlePlatformFilter = (value: string) => {\n setPlatformFilter(value);\n };\n\n const handleStatusFilter = (value: string) => {\n setStatusFilter(value);\n };\n\n const handleDateRangeChange = (dates: any) => {\n if (dates) {\n setDateRange([dates[0].format('YYYY-MM-DD'), dates[1].format('YYYY-MM-DD')]);\n } else {\n setDateRange(null);\n }\n };\n\n const filteredOrders = orders.filter(order => {\n const matchesSearch = order.orderId.includes(searchText) ||\n order.merchantName.toLowerCase().includes(searchText.toLowerCase());\n const matchesMerchant = merchantFilter ? order.merchantId === merchantFilter : true;\n const matchesPlatform = platformFilter ? order.platform === platformFilter : true;\n const matchesStatus = statusFilter ? order.status === statusFilter : true;\n return matchesSearch && matchesMerchant && matchesPlatform && matchesStatus;\n });\n\n const handleView = (order: Order) => {\n // 跳转到订单详情页面\n message.info(`查看订单详情: ${order.orderId}`);\n };\n\n const handleStatusUpdate = (order: Order, newStatus: Order['status']) => {\n // 调用API更新订单状态\n message.info(`更新订单 ${order.orderId} 状态为: ${newStatus}`);\n };\n\n const handleRefresh = () => {\n fetchOrders();\n };\n\n const getStatusTag = (status: string) => {\n const statusConfig: Record = {\n PENDING: { color: 'blue', text: '待处理' },\n PROCESSING: { color: 'orange', text: '处理中' },\n SHIPPED: { color: 'purple', text: '已发货' },\n DELIVERED: { color: 'green', text: '已送达' },\n CANCELLED: { color: 'gray', text: '已取消' },\n REFUNDED: { color: 'red', text: '已退款' },\n };\n const config = statusConfig[status] || { color: 'default', text: status };\n return {config.text};\n };\n\n const columns = [\n {\n title: '订单ID',\n dataIndex: 'id',\n key: 'id',\n ellipsis: true,\n },\n {\n title: '平台订单号',\n dataIndex: 'orderId',\n key: 'orderId',\n ellipsis: true,\n },\n {\n title: '商户',\n dataIndex: 'merchantName',\n key: 'merchantName',\n ellipsis: true,\n },\n {\n title: '平台',\n dataIndex: 'platform',\n key: 'platform',\n ellipsis: true,\n },\n {\n title: '总金额',\n dataIndex: 'totalAmount',\n key: 'totalAmount',\n render: (amount: number) => ${amount.toFixed(2)},\n },\n {\n title: '状态',\n dataIndex: 'status',\n key: 'status',\n render: (status: string) => getStatusTag(status),\n },\n {\n title: '创建时间',\n dataIndex: 'createdAt',\n key: 'createdAt',\n ellipsis: true,\n },\n {\n title: '操作',\n key: 'action',\n render: (_: any, order: Order) => (\n
\n \n handleStatusUpdate(order, value)}\n >\n \n \n \n \n \n \n \n
\n ),\n },\n ];\n\n return (\n \n
\n 多商户订单管理\n \n
\n\n
\n }\n style={{ width: 300 }}\n onChange={(e) => handleSearch(e.target.value)}\n />\n \n {merchants.map(merchant => (\n \n ))}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n\n \n
\n );\n};\n\nexport default MerchantOrderManage;", + "__isJSFile": true, + "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Merchant/MerchantOrderManage.tsx" + }, + "Merchant/MerchantShopManage": { + "path": "Merchant/MerchantShopManage", + "id": "Merchant/MerchantShopManage", + "file": "Merchant/MerchantShopManage.tsx", + "absPath": "/Merchant/MerchantShopManage", + "__content": "import React, { useState, useEffect } from 'react';\nimport { Table, Input, Button, Select, message, Card, Typography, Form, Modal } from 'antd';\nimport { SearchOutlined, PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';\nimport { getMerchantShops, createMerchantShop, updateMerchantShop, deleteMerchantShop } from '../../services/merchantShopService';\n\nconst { Option } = Select;\nconst { Title, Text } = Typography;\nconst { Item } = Form;\n\ninterface Shop {\n id: string;\n merchantId: string;\n shopName: string;\n platform: string;\n shopUrl: string;\n status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';\n createdAt: string;\n updatedAt: string;\n}\n\nconst MerchantShopManage: React.FC = () => {\n const [shops, setShops] = useState([]);\n const [merchants, setMerchants] = useState<{ id: string; companyName: string }[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchText, setSearchText] = useState('');\n const [merchantFilter, setMerchantFilter] = useState('');\n const [platformFilter, setPlatformFilter] = useState('');\n const [statusFilter, setStatusFilter] = useState('');\n const [isModalVisible, setIsModalVisible] = useState(false);\n const [form] = Form.useForm();\n const [editingShop, setEditingShop] = useState(null);\n\n const fetchShops = async () => {\n setLoading(true);\n try {\n const response = await getMerchantShops();\n setShops(response.data);\n } catch (error) {\n message.error('获取店铺列表失败');\n console.error('获取店铺列表失败:', error);\n } finally {\n setLoading(false);\n }\n };\n\n const fetchMerchants = async () => {\n try {\n // 实际项目中应该调用获取商户列表的API\n // 这里模拟数据\n setMerchants([\n { id: '1', companyName: '商户A' },\n { id: '2', companyName: '商户B' },\n { id: '3', companyName: '商户C' },\n ]);\n } catch (error) {\n console.error('获取商户列表失败:', error);\n }\n };\n\n useEffect(() => {\n fetchMerchants();\n fetchShops();\n }, []);\n\n const handleSearch = (value: string) => {\n setSearchText(value);\n };\n\n const handleMerchantFilter = (value: string) => {\n setMerchantFilter(value);\n };\n\n const handlePlatformFilter = (value: string) => {\n setPlatformFilter(value);\n };\n\n const handleStatusFilter = (value: string) => {\n setStatusFilter(value);\n };\n\n const filteredShops = shops.filter(shop => {\n const matchesSearch = shop.shopName.toLowerCase().includes(searchText.toLowerCase()) ||\n shop.shopUrl.toLowerCase().includes(searchText.toLowerCase());\n const matchesMerchant = merchantFilter ? shop.merchantId === merchantFilter : true;\n const matchesPlatform = platformFilter ? shop.platform === platformFilter : true;\n const matchesStatus = statusFilter ? shop.status === statusFilter : true;\n return matchesSearch && matchesMerchant && matchesPlatform && matchesStatus;\n });\n\n const handleCreate = () => {\n setEditingShop(null);\n form.resetFields();\n setIsModalVisible(true);\n };\n\n const handleEdit = (shop: Shop) => {\n setEditingShop(shop);\n form.setFieldsValue(shop);\n setIsModalVisible(true);\n };\n\n const handleDelete = (shopId: string) => {\n // 确认删除后调用API\n message.info(`删除店铺: ${shopId}`);\n };\n\n const handleView = (shop: Shop) => {\n // 跳转到店铺详情页面\n message.info(`查看店铺详情: ${shop.shopName}`);\n };\n\n const handleOk = async () => {\n try {\n const values = await form.validateFields();\n if (editingShop) {\n // 编辑店铺\n await updateMerchantShop(editingShop.id, values);\n message.success('店铺更新成功');\n } else {\n // 创建店铺\n await createMerchantShop(values);\n message.success('店铺创建成功');\n }\n setIsModalVisible(false);\n fetchShops();\n } catch (error) {\n message.error('操作失败');\n console.error('操作失败:', error);\n }\n };\n\n const handleCancel = () => {\n setIsModalVisible(false);\n };\n\n const columns = [\n {\n title: '店铺ID',\n dataIndex: 'id',\n key: 'id',\n ellipsis: true,\n },\n {\n title: '商户',\n dataIndex: 'merchantId',\n key: 'merchantId',\n render: (merchantId: string) => {\n const merchant = merchants.find(m => m.id === merchantId);\n return {merchant?.companyName || merchantId};\n },\n },\n {\n title: '店铺名称',\n dataIndex: 'shopName',\n key: 'shopName',\n ellipsis: true,\n },\n {\n title: '平台',\n dataIndex: 'platform',\n key: 'platform',\n ellipsis: true,\n },\n {\n title: '店铺链接',\n dataIndex: 'shopUrl',\n key: 'shopUrl',\n ellipsis: true,\n render: (shopUrl: string) => (\n \n {shopUrl}\n \n ),\n },\n {\n title: '状态',\n dataIndex: 'status',\n key: 'status',\n render: (status: string) => {\n const statusMap: Record = {\n ACTIVE: '活跃',\n INACTIVE: '未激活',\n SUSPENDED: '暂停',\n };\n return {statusMap[status] || status};\n },\n },\n {\n title: '创建时间',\n dataIndex: 'createdAt',\n key: 'createdAt',\n ellipsis: true,\n },\n {\n title: '操作',\n key: 'action',\n render: (_: any, shop: Shop) => (\n
\n \n \n \n
\n ),\n },\n ];\n\n return (\n \n
\n 商户店铺管理\n \n
\n\n
\n }\n style={{ width: 300 }}\n onChange={(e) => handleSearch(e.target.value)}\n />\n \n {merchants.map(merchant => (\n \n ))}\n \n \n \n \n \n \n \n \n \n \n \n \n
\n\n \n\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n );\n};\n\nexport default MerchantShopManage;", + "__isJSFile": true, + "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Merchant/MerchantShopManage.tsx" + }, "AfterSales/CustomerService": { "path": "AfterSales/CustomerService", "id": "AfterSales/CustomerService", @@ -2038,6 +2378,15 @@ "__isJSFile": true, "__absFile": "D:/trae_projects/makemd/makemd/dashboard/src/pages/Product/ProductPublishForm.tsx" }, + "Blacklist/BlacklistManage": { + "path": "Blacklist/BlacklistManage", + "id": "Blacklist/BlacklistManage", + "file": "Blacklist/BlacklistManage.tsx", + "absPath": "/Blacklist/BlacklistManage", + "__content": "import React, { useState, useEffect } from 'react';\nimport {\n Card,\n Table,\n Button,\n Space,\n Modal,\n Form,\n Input,\n Select,\n DatePicker,\n InputNumber,\n Descriptions,\n Divider,\n message,\n Tag,\n Tabs,\n Row,\n Col,\n Statistic,\n Alert,\n Tooltip,\n Badge,\n Upload,\n} from 'antd';\nimport {\n UserOutlined,\n PlusOutlined,\n EyeOutlined,\n EditOutlined,\n DeleteOutlined,\n SearchOutlined,\n WarningOutlined,\n CheckCircleOutlined,\n CloseCircleOutlined,\n StopOutlined,\n ExclamationCircleOutlined,\n UploadOutlined,\n DownloadOutlined,\n} from '@ant-design/icons';\nimport type { ColumnsType } from 'antd/es/table';\nimport dayjs from 'dayjs';\n\nconst { Option } = Select;\nconst { TabPane } = Tabs;\nconst { TextArea } = Input;\n\ninterface BlacklistRecord {\n id: string;\n tenant_id: string;\n shop_id: string;\n buyer_id: string;\n buyer_name: string;\n buyer_email: string;\n buyer_phone?: string;\n platform: string;\n platform_buyer_id: string;\n blacklist_reason: string;\n blacklist_type: 'FRAUD' | 'CHARGEBACK' | 'ABUSE' | 'OTHER';\n risk_score: number;\n blacklist_date: string;\n expiry_date?: string;\n status: 'ACTIVE' | 'INACTIVE' | 'EXPIRED';\n evidence?: string;\n created_by: string;\n trace_id: string;\n created_at: string;\n updated_at: string;\n}\n\nconst BLACKLIST_TYPES = [\n { value: 'FRAUD', label: 'Fraud', color: 'red' },\n { value: 'CHARGEBACK', label: 'Chargeback', color: 'orange' },\n { value: 'ABUSE', label: 'Abuse', color: 'purple' },\n { value: 'OTHER', label: 'Other', color: 'default' },\n];\n\nconst STATUS_CONFIG: Record = {\n ACTIVE: { color: 'error', text: 'Active', icon: },\n INACTIVE: { color: 'default', text: 'Inactive', icon: },\n EXPIRED: { color: 'default', text: 'Expired', icon: },\n};\n\nconst PLATFORMS = [\n 'Amazon',\n 'eBay',\n 'Shopify',\n 'Walmart',\n 'TikTok Shop',\n 'Temu',\n 'Alibaba',\n 'Other',\n];\n\nconst MOCK_BLACKLISTS: BlacklistRecord[] = [\n {\n id: '1',\n tenant_id: 'tenant-001',\n shop_id: 'shop-001',\n buyer_id: 'buyer-001',\n buyer_name: 'John Smith',\n buyer_email: 'john.smith@example.com',\n buyer_phone: '+1-555-0101',\n platform: 'Amazon',\n platform_buyer_id: 'AMZ-001',\n blacklist_reason: 'Multiple fraudulent transactions',\n blacklist_type: 'FRAUD',\n risk_score: 85,\n blacklist_date: '2026-01-15',\n expiry_date: '2027-01-15',\n status: 'ACTIVE',\n evidence: 'Transaction records, IP logs',\n created_by: 'admin',\n trace_id: 'trace-001',\n created_at: '2026-01-15 10:00:00',\n updated_at: '2026-01-15 10:00:00',\n },\n {\n id: '2',\n tenant_id: 'tenant-001',\n shop_id: 'shop-001',\n buyer_id: 'buyer-002',\n buyer_name: 'Jane Doe',\n buyer_email: 'jane.doe@example.com',\n platform: 'eBay',\n platform_buyer_id: 'EBAY-002',\n blacklist_reason: 'Excessive chargebacks',\n blacklist_type: 'CHARGEBACK',\n risk_score: 75,\n blacklist_date: '2026-02-20',\n expiry_date: '2026-08-20',\n status: 'ACTIVE',\n evidence: 'Chargeback documentation',\n created_by: 'admin',\n trace_id: 'trace-002',\n created_at: '2026-02-20 14:30:00',\n updated_at: '2026-02-20 14:30:00',\n },\n {\n id: '3',\n tenant_id: 'tenant-001',\n shop_id: 'shop-001',\n buyer_id: 'buyer-003',\n buyer_name: 'Mike Johnson',\n buyer_email: 'mike.j@example.com',\n platform: 'Amazon',\n platform_buyer_id: 'AMZ-003',\n blacklist_reason: 'Abusive behavior towards support',\n blacklist_type: 'ABUSE',\n risk_score: 60,\n blacklist_date: '2026-03-01',\n expiry_date: '2026-06-01',\n status: 'ACTIVE',\n evidence: 'Support chat logs',\n created_by: 'admin',\n trace_id: 'trace-003',\n created_at: '2026-03-01 09:15:00',\n updated_at: '2026-03-01 09:15:00',\n },\n {\n id: '4',\n tenant_id: 'tenant-001',\n shop_id: 'shop-001',\n buyer_id: 'buyer-004',\n buyer_name: 'Sarah Wilson',\n buyer_email: 'sarah.w@example.com',\n platform: 'Shopify',\n platform_buyer_id: 'SHOPIFY-004',\n blacklist_reason: 'Suspicious ordering pattern',\n blacklist_type: 'OTHER',\n risk_score: 50,\n blacklist_date: '2026-02-10',\n expiry_date: '2026-05-10',\n status: 'INACTIVE',\n evidence: 'Order history analysis',\n created_by: 'admin',\n trace_id: 'trace-004',\n created_at: '2026-02-10 11:00:00',\n updated_at: '2026-03-01 16:00:00',\n },\n];\n\nconst BlacklistManage: React.FC = () => {\n const [form] = Form.useForm();\n const [loading, setLoading] = useState(false);\n const [blacklists, setBlacklists] = useState(MOCK_BLACKLISTS);\n const [createModalVisible, setCreateModalVisible] = useState(false);\n const [editModalVisible, setEditModalVisible] = useState(false);\n const [detailModalVisible, setDetailModalVisible] = useState(false);\n const [selectedBlacklist, setSelectedBlacklist] = useState(null);\n const [activeTab, setActiveTab] = useState('all');\n const [searchText, setSearchText] = useState('');\n const [platformFilter, setPlatformFilter] = useState('');\n const [typeFilter, setTypeFilter] = useState('');\n const [selectedRowKeys, setSelectedRowKeys] = useState([]);\n\n useEffect(() => {\n fetchBlacklists();\n }, []);\n\n const fetchBlacklists = async () => {\n setLoading(true);\n try {\n setBlacklists(MOCK_BLACKLISTS);\n } finally {\n setLoading(false);\n }\n };\n\n const handleCreate = async (values: any) => {\n setLoading(true);\n try {\n const newBlacklist: BlacklistRecord = {\n id: `${Date.now()}`,\n tenant_id: 'tenant-001',\n shop_id: 'shop-001',\n buyer_id: `buyer-${Date.now()}`,\n buyer_name: values.buyer_name,\n buyer_email: values.buyer_email,\n buyer_phone: values.buyer_phone,\n platform: values.platform,\n platform_buyer_id: values.platform_buyer_id,\n blacklist_reason: values.blacklist_reason,\n blacklist_type: values.blacklist_type,\n risk_score: values.risk_score,\n blacklist_date: dayjs().format('YYYY-MM-DD'),\n expiry_date: values.expiry_date ? values.expiry_date.format('YYYY-MM-DD') : undefined,\n status: 'ACTIVE',\n evidence: values.evidence,\n created_by: 'admin',\n trace_id: `trace-${Date.now()}`,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n };\n\n setBlacklists([newBlacklist, ...blacklists]);\n message.success('Buyer added to blacklist successfully');\n setCreateModalVisible(false);\n form.resetFields();\n } finally {\n setLoading(false);\n }\n };\n\n const handleEdit = async (values: any) => {\n if (!selectedBlacklist) return;\n\n setLoading(true);\n try {\n const updatedBlacklists = blacklists.map(bl => \n bl.id === selectedBlacklist.id \n ? {\n ...bl,\n buyer_name: values.buyer_name,\n buyer_email: values.buyer_email,\n buyer_phone: values.buyer_phone,\n blacklist_reason: values.blacklist_reason,\n blacklist_type: values.blacklist_type,\n risk_score: values.risk_score,\n expiry_date: values.expiry_date ? values.expiry_date.format('YYYY-MM-DD') : undefined,\n status: values.status,\n evidence: values.evidence,\n updated_at: new Date().toISOString(),\n }\n : bl\n );\n\n setBlacklists(updatedBlacklists);\n message.success('Blacklist record updated successfully');\n setEditModalVisible(false);\n form.resetFields();\n setSelectedBlacklist(null);\n } finally {\n setLoading(false);\n }\n };\n\n const handleDelete = (id: string) => {\n Modal.confirm({\n title: 'Remove from Blacklist',\n content: 'Are you sure you want to remove this buyer from the blacklist?',\n okType: 'danger',\n onOk: () => {\n const updatedBlacklists = blacklists.map(bl => \n bl.id === id ? { ...bl, status: 'INACTIVE' as const, updated_at: new Date().toISOString() } : bl\n );\n setBlacklists(updatedBlacklists);\n message.success('Buyer removed from blacklist');\n },\n });\n };\n\n const handleBatchDelete = () => {\n if (selectedRowKeys.length === 0) {\n message.warning('Please select records to delete');\n return;\n }\n\n Modal.confirm({\n title: 'Batch Remove from Blacklist',\n content: `Are you sure you want to remove ${selectedRowKeys.length} buyers from the blacklist?`,\n okType: 'danger',\n onOk: () => {\n const updatedBlacklists = blacklists.map(bl => \n selectedRowKeys.includes(bl.id) ? { ...bl, status: 'INACTIVE' as const, updated_at: new Date().toISOString() } : bl\n );\n setBlacklists(updatedBlacklists);\n setSelectedRowKeys([]);\n message.success(`${selectedRowKeys.length} buyers removed from blacklist`);\n },\n });\n };\n\n const handleViewDetail = (blacklist: BlacklistRecord) => {\n setSelectedBlacklist(blacklist);\n setDetailModalVisible(true);\n };\n\n const handleEditClick = (blacklist: BlacklistRecord) => {\n setSelectedBlacklist(blacklist);\n form.setFieldsValue({\n buyer_name: blacklist.buyer_name,\n buyer_email: blacklist.buyer_email,\n buyer_phone: blacklist.buyer_phone,\n platform: blacklist.platform,\n platform_buyer_id: blacklist.platform_buyer_id,\n blacklist_reason: blacklist.blacklist_reason,\n blacklist_type: blacklist.blacklist_type,\n risk_score: blacklist.risk_score,\n expiry_date: blacklist.expiry_date ? dayjs(blacklist.expiry_date) : null,\n status: blacklist.status,\n evidence: blacklist.evidence,\n });\n setEditModalVisible(true);\n };\n\n const getFilteredBlacklists = () => {\n let filtered = blacklists;\n\n if (activeTab !== 'all') {\n filtered = filtered.filter(bl => bl.status === activeTab.toUpperCase());\n }\n\n if (searchText) {\n filtered = filtered.filter(bl => \n bl.buyer_name.toLowerCase().includes(searchText.toLowerCase()) ||\n bl.buyer_email.toLowerCase().includes(searchText.toLowerCase()) ||\n bl.platform_buyer_id.toLowerCase().includes(searchText.toLowerCase())\n );\n }\n\n if (platformFilter) {\n filtered = filtered.filter(bl => bl.platform === platformFilter);\n }\n\n if (typeFilter) {\n filtered = filtered.filter(bl => bl.blacklist_type === typeFilter);\n }\n\n return filtered;\n };\n\n const getRiskLevel = (score: number): { level: string; color: string } => {\n if (score >= 80) return { level: 'Critical', color: '#ff4d4f' };\n if (score >= 60) return { level: 'High', color: '#ff7a45' };\n if (score >= 40) return { level: 'Medium', color: '#faad14' };\n return { level: 'Low', color: '#52c41a' };\n };\n\n const columns: ColumnsType = [\n {\n title: 'Buyer Name',\n dataIndex: 'buyer_name',\n key: 'buyer_name',\n width: 150,\n render: (name: string, record: BlacklistRecord) => (\n handleViewDetail(record)}>{name}\n ),\n },\n {\n title: 'Email',\n dataIndex: 'buyer_email',\n key: 'buyer_email',\n width: 200,\n ellipsis: true,\n },\n {\n title: 'Platform',\n dataIndex: 'platform',\n key: 'platform',\n width: 100,\n },\n {\n title: 'Platform Buyer ID',\n dataIndex: 'platform_buyer_id',\n key: 'platform_buyer_id',\n width: 150,\n },\n {\n title: 'Type',\n dataIndex: 'blacklist_type',\n key: 'blacklist_type',\n width: 100,\n render: (type: string) => {\n const config = BLACKLIST_TYPES.find(t => t.value === type);\n return {type};\n },\n },\n {\n title: 'Risk Score',\n dataIndex: 'risk_score',\n key: 'risk_score',\n width: 120,\n render: (score: number) => {\n const { level, color } = getRiskLevel(score);\n return (\n \n {score} - {level}\n \n );\n },\n sorter: (a, b) => a.risk_score - b.risk_score,\n },\n {\n title: 'Blacklist Date',\n dataIndex: 'blacklist_date',\n key: 'blacklist_date',\n width: 120,\n },\n {\n title: 'Expiry Date',\n dataIndex: 'expiry_date',\n key: 'expiry_date',\n width: 120,\n render: (date: string) => {\n if (!date) return '-';\n const daysUntilExpiry = dayjs(date).diff(dayjs(), 'day');\n const isExpiringSoon = daysUntilExpiry > 0 && daysUntilExpiry <= 30;\n return (\n \n \n {date}\n \n \n );\n },\n },\n {\n title: 'Status',\n dataIndex: 'status',\n key: 'status',\n width: 100,\n render: (status: string) => {\n const config = STATUS_CONFIG[status];\n return {config.text};\n },\n },\n {\n title: 'Actions',\n key: 'actions',\n width: 150,\n render: (_, record: BlacklistRecord) => (\n \n \n \n )}\n \n \n }>\n
\n }\n style={{ width: 300 }}\n onChange={(e) => setSearchText(e.target.value)}\n />\n \n {PLATFORMS.map(platform => (\n \n ))}\n \n \n {BLACKLIST_TYPES.map(type => (\n \n ))}\n \n
\n\n \n \n \n \n \n \n\n ({\n disabled: record.status !== 'ACTIVE',\n }),\n }}\n pagination={{ pageSize: 10 }}\n />\n \n\n setCreateModalVisible(false)}\n footer={null}\n width={700}\n >\n
\n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n\n \n