refactor: 重构项目结构并优化类型定义
- 移除extension模块,将功能迁移至node-agent - 修复类型导出问题,使用export type明确类型导出 - 统一数据库连接方式,从直接导入改为使用config/database - 更新文档中的项目结构描述 - 添加多个服务的实用方法,如getForecast、getBalances等 - 修复类型错误和TS1205警告 - 优化RedisService调用方式 - 添加新的实体类型定义 - 更新审计日志格式,统一字段命名
This commit is contained in:
@@ -35,6 +35,62 @@ export interface ReturnApplication {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface CustomerServiceTicket {
|
||||
id: string;
|
||||
ticketId: string;
|
||||
orderId: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
customerEmail: string;
|
||||
subject: string;
|
||||
category: string;
|
||||
priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT';
|
||||
status: 'OPEN' | 'IN_PROGRESS' | 'WAITING_CUSTOMER' | 'RESOLVED' | 'CLOSED';
|
||||
assignedTo?: string;
|
||||
platform?: string;
|
||||
lastMessage?: string;
|
||||
messageCount?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
messages?: CustomerServiceMessage[];
|
||||
}
|
||||
|
||||
export interface CustomerServiceMessage {
|
||||
id: string;
|
||||
ticketId: string;
|
||||
sender: 'customer' | 'agent';
|
||||
content: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface RefundRecord {
|
||||
id: string;
|
||||
orderId: string;
|
||||
refundId: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
reason: string;
|
||||
status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
platform: string;
|
||||
refundMethod: string;
|
||||
transactionId?: string;
|
||||
processedBy?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface ReturnFormData {
|
||||
orderId: string;
|
||||
returnReason: string;
|
||||
returnDescription: string;
|
||||
returnItems: string[];
|
||||
images: string[];
|
||||
contactPhone: string;
|
||||
}
|
||||
|
||||
export interface AfterSalesQueryParams {
|
||||
orderId?: string;
|
||||
status?: string;
|
||||
@@ -49,9 +105,12 @@ export interface AfterSalesQueryParams {
|
||||
export interface IAfterSalesDataSource {
|
||||
fetchOrderItems(orderId: string): Promise<OrderItem[]>;
|
||||
submitReturn(data: Partial<ReturnApplication>): Promise<ReturnApplication>;
|
||||
submitReturnApply(data: ReturnFormData): Promise<ReturnApplication>;
|
||||
fetchReturnApplications(params?: AfterSalesQueryParams): Promise<ReturnApplication[]>;
|
||||
fetchReturnDetail(id: string): Promise<ReturnApplication | null>;
|
||||
updateReturnStatus(id: string, status: string): Promise<ReturnApplication>;
|
||||
fetchCustomerServiceTickets(params?: AfterSalesQueryParams): Promise<CustomerServiceTicket[]>;
|
||||
fetchRefundRecords(params?: AfterSalesQueryParams): Promise<RefundRecord[]>;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -75,8 +134,18 @@ class ApiAfterSalesDataSource implements IAfterSalesDataSource {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async submitReturnApply(data: ReturnFormData): Promise<ReturnApplication> {
|
||||
const response = await fetch('/api/v1/returns/apply', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async fetchReturnApplications(params?: AfterSalesQueryParams): Promise<ReturnApplication[]> {
|
||||
const query = new URLSearchParams(params as any).toString();
|
||||
const query = new URLSearchParams(params as Record<string, string>).toString();
|
||||
const response = await fetch(`/api/v1/returns?${query}`);
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
@@ -97,6 +166,20 @@ class ApiAfterSalesDataSource implements IAfterSalesDataSource {
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async fetchCustomerServiceTickets(params?: AfterSalesQueryParams): Promise<CustomerServiceTicket[]> {
|
||||
const query = new URLSearchParams(params as Record<string, string>).toString();
|
||||
const response = await fetch(`/api/v1/customer-service/tickets?${query}`);
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async fetchRefundRecords(params?: AfterSalesQueryParams): Promise<RefundRecord[]> {
|
||||
const query = new URLSearchParams(params as Record<string, string>).toString();
|
||||
const response = await fetch(`/api/v1/refunds?${query}`);
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -190,6 +273,64 @@ class MockAfterSalesDataSource implements IAfterSalesDataSource {
|
||||
return this.mockReturns[index];
|
||||
}
|
||||
|
||||
async submitReturnApply(data: ReturnFormData): Promise<ReturnApplication> {
|
||||
await this.simulateDelay(1000);
|
||||
const newReturn: ReturnApplication = {
|
||||
id: `RET-${Date.now()}`,
|
||||
orderId: data.orderId,
|
||||
returnReason: data.returnReason,
|
||||
returnDescription: data.returnDescription,
|
||||
returnItems: data.returnItems,
|
||||
images: data.images,
|
||||
contactPhone: data.contactPhone,
|
||||
status: 'PENDING',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
this.mockReturns.push(newReturn);
|
||||
return newReturn;
|
||||
}
|
||||
|
||||
async fetchCustomerServiceTickets(params?: AfterSalesQueryParams): Promise<CustomerServiceTicket[]> {
|
||||
await this.simulateDelay(300);
|
||||
return [
|
||||
{
|
||||
id: 'TICKET-001',
|
||||
orderId: 'order-001',
|
||||
customerId: 'C001',
|
||||
customerName: 'John Doe',
|
||||
customerEmail: 'john@example.com',
|
||||
subject: 'Product not received',
|
||||
category: 'delivery',
|
||||
priority: 'HIGH',
|
||||
status: 'OPEN',
|
||||
createdAt: '2026-03-18T10:00:00Z',
|
||||
updatedAt: '2026-03-18T10:00:00Z',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async fetchRefundRecords(params?: AfterSalesQueryParams): Promise<RefundRecord[]> {
|
||||
await this.simulateDelay(300);
|
||||
return [
|
||||
{
|
||||
id: 'REFUND-001',
|
||||
orderId: 'order-001',
|
||||
refundId: 'RF-001',
|
||||
customerId: 'C001',
|
||||
customerName: 'John Doe',
|
||||
amount: 59.98,
|
||||
currency: 'USD',
|
||||
reason: 'Defective product',
|
||||
status: 'PENDING',
|
||||
createdAt: '2026-03-18T10:00:00Z',
|
||||
updatedAt: '2026-03-18T10:00:00Z',
|
||||
platform: 'Amazon',
|
||||
refundMethod: 'Original Payment',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private simulateDelay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@@ -36,10 +36,12 @@ export interface BatchOrder {
|
||||
totalItems: number;
|
||||
totalAmount: number;
|
||||
currency: string;
|
||||
status: 'DRAFT' | 'PENDING_REVIEW' | 'CONFIRMED' | 'PROCESSING' | 'COMPLETED' | 'CANCELLED';
|
||||
status: 'DRAFT' | 'PENDING_REVIEW' | 'CONFIRMED' | 'PROCESSING' | 'COMPLETED' | 'CANCELLED' | 'SUBMITTED';
|
||||
createdAt: string;
|
||||
validItems: number;
|
||||
invalidItems: number;
|
||||
items?: BatchOrderItem[];
|
||||
estimatedDelivery?: string;
|
||||
}
|
||||
|
||||
export interface Customer {
|
||||
@@ -47,6 +49,7 @@ export interface Customer {
|
||||
name: string;
|
||||
company: string;
|
||||
tier: 'BASIC' | 'PRO' | 'ENTERPRISE';
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export interface BatchOrderQueryParams {
|
||||
@@ -64,6 +67,37 @@ export interface BatchOrderStats {
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
export interface Contract {
|
||||
id: string;
|
||||
contractId: string;
|
||||
contractNumber?: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
type: 'PURCHASE' | 'SALES' | 'SERVICE' | 'DISTRIBUTION' | 'OEM' | 'NDA';
|
||||
title: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
totalAmount: number;
|
||||
totalValue?: number;
|
||||
currency: string;
|
||||
status: 'DRAFT' | 'PENDING_REVIEW' | 'ACTIVE' | 'EXPIRED' | 'TERMINATED' | 'RENEWED';
|
||||
signedDate?: string;
|
||||
signedBy?: string;
|
||||
attachments?: string[];
|
||||
terms?: string;
|
||||
remark?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ContractQueryParams {
|
||||
customerId?: string;
|
||||
status?: string;
|
||||
type?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// B2B Trade专用接口
|
||||
// ============================================
|
||||
@@ -77,6 +111,10 @@ export interface IB2BTradeDataSource {
|
||||
approveBatchOrder(batchId: string): Promise<BatchOrder>;
|
||||
validateItems(items: BatchOrderItem[]): Promise<BatchOrderItem[]>;
|
||||
parseUploadFile(file: File, customerId: string): Promise<BatchOrderItem[]>;
|
||||
fetchContracts(params?: ContractQueryParams): Promise<Contract[]>;
|
||||
fetchContractDetail(contractId: string): Promise<Contract | null>;
|
||||
createContract(data: Partial<Contract>): Promise<Contract>;
|
||||
updateContract(contractId: string, data: Partial<Contract>): Promise<Contract>;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -148,6 +186,39 @@ class ApiB2BTradeDataSource implements IB2BTradeDataSource {
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async fetchContracts(params?: ContractQueryParams): Promise<Contract[]> {
|
||||
const query = new URLSearchParams(params as Record<string, string>).toString();
|
||||
const response = await fetch(`/api/v1/b2b/contracts?${query}`);
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async fetchContractDetail(contractId: string): Promise<Contract | null> {
|
||||
const response = await fetch(`/api/v1/b2b/contracts/${contractId}`);
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async createContract(data: Partial<Contract>): Promise<Contract> {
|
||||
const response = await fetch('/api/v1/b2b/contracts', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async updateContract(contractId: string, data: Partial<Contract>): Promise<Contract> {
|
||||
const response = await fetch(`/api/v1/b2b/contracts/${contractId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const result = await response.json();
|
||||
return result.data;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -291,6 +362,60 @@ class MockB2BTradeDataSource implements IB2BTradeDataSource {
|
||||
];
|
||||
}
|
||||
|
||||
async fetchContracts(params?: ContractQueryParams): Promise<Contract[]> {
|
||||
await this.simulateDelay(400);
|
||||
return [
|
||||
{
|
||||
id: '1',
|
||||
contractId: 'CON-2026-001',
|
||||
customerId: 'CUST_001',
|
||||
customerName: 'ABC Trading Co.',
|
||||
type: 'PURCHASE',
|
||||
title: 'Annual Purchase Agreement',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-12-31',
|
||||
totalAmount: 500000,
|
||||
currency: 'USD',
|
||||
status: 'ACTIVE',
|
||||
createdAt: '2026-01-01',
|
||||
updatedAt: '2026-01-01',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async fetchContractDetail(contractId: string): Promise<Contract | null> {
|
||||
await this.simulateDelay(300);
|
||||
const contracts = await this.fetchContracts();
|
||||
return contracts.find(c => c.contractId === contractId) || null;
|
||||
}
|
||||
|
||||
async createContract(data: Partial<Contract>): Promise<Contract> {
|
||||
await this.simulateDelay(500);
|
||||
const newContract: Contract = {
|
||||
id: `${Date.now()}`,
|
||||
contractId: `CON-${Date.now()}`,
|
||||
customerId: data.customerId || '',
|
||||
customerName: data.customerName || '',
|
||||
type: data.type || 'PURCHASE',
|
||||
title: data.title || '',
|
||||
startDate: data.startDate || '',
|
||||
endDate: data.endDate || '',
|
||||
totalAmount: data.totalAmount || 0,
|
||||
currency: data.currency || 'USD',
|
||||
status: 'DRAFT',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
return newContract;
|
||||
}
|
||||
|
||||
async updateContract(contractId: string, data: Partial<Contract>): Promise<Contract> {
|
||||
await this.simulateDelay(300);
|
||||
const contract = await this.fetchContractDetail(contractId);
|
||||
if (!contract) throw new Error('Contract not found');
|
||||
return { ...contract, ...data, updatedAt: new Date().toISOString() };
|
||||
}
|
||||
|
||||
private simulateDelay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@@ -41,8 +41,13 @@ export interface RiskAssessment {
|
||||
orderId: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
platform?: string;
|
||||
platform_buyer_id?: string;
|
||||
riskScore: number;
|
||||
risk_score?: number;
|
||||
riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
risk_level?: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
assessment_date?: string;
|
||||
riskFactors: string[];
|
||||
status: 'PENDING' | 'REVIEWING' | 'APPROVED' | 'REJECTED';
|
||||
reviewedBy?: string;
|
||||
|
||||
Reference in New Issue
Block a user