refactor(terminology): 统一术语标准并优化代码类型安全
- 将B2B统一为TOB术语 - 将状态值统一为大写格式 - 优化类型声明,避免使用any - 将float类型替换为decimal以提高精度 - 新增术语标准化文档 - 优化路由结构和菜单分类 - 添加TypeORM实体类 - 增强加密模块安全性 - 重构前端路由结构 - 完善任务模板和验收标准
This commit is contained in:
216
server/src/services/PlatformAccountService.ts
Normal file
216
server/src/services/PlatformAccountService.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import db from '../config/database';
|
||||
import { DomainEventBus } from '../core/runtime/DomainEventBus';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
export interface PlatformAccount {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
platform: string;
|
||||
accountName: string;
|
||||
accountId: string;
|
||||
status: 'ACTIVE' | 'INACTIVE' | 'EXPIRED' | 'ERROR';
|
||||
token?: string;
|
||||
refreshToken?: string;
|
||||
expiresAt?: string;
|
||||
shopId?: string;
|
||||
config?: Record<string, any>;
|
||||
lastSyncAt?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface PlatformAccountStats {
|
||||
total: number;
|
||||
active: number;
|
||||
inactive: number;
|
||||
expired: number;
|
||||
error: number;
|
||||
}
|
||||
|
||||
export class PlatformAccountService {
|
||||
private static TABLE = 'cf_platform_account';
|
||||
|
||||
static async initTable(): Promise<void> {
|
||||
const hasTable = await db.schema.hasTable(this.TABLE);
|
||||
if (!hasTable) {
|
||||
await db.schema.createTable(this.TABLE, (table) => {
|
||||
table.string('id', 64).primary();
|
||||
table.string('tenant_id', 64).notNullable();
|
||||
table.string('platform', 64).notNullable();
|
||||
table.string('account_name', 128).notNullable();
|
||||
table.string('account_id', 128).notNullable();
|
||||
table.enum('status', ['ACTIVE', 'INACTIVE', 'EXPIRED', 'ERROR']).defaultTo('ACTIVE');
|
||||
table.text('token');
|
||||
table.text('refresh_token');
|
||||
table.timestamp('expires_at');
|
||||
table.string('shop_id', 64);
|
||||
table.json('config');
|
||||
table.timestamp('last_sync_at');
|
||||
table.timestamps(true, true);
|
||||
table.index(['tenant_id', 'platform']);
|
||||
table.index(['shop_id']);
|
||||
table.unique(['tenant_id', 'platform', 'account_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async list(tenantId: string, filters?: { platform?: string; status?: string; shopId?: string }): Promise<PlatformAccount[]> {
|
||||
let query = db(this.TABLE).where('tenant_id', tenantId);
|
||||
if (filters?.platform) query = query.where('platform', filters.platform);
|
||||
if (filters?.status) query = query.where('status', filters.status);
|
||||
if (filters?.shopId) query = query.where('shop_id', filters.shopId);
|
||||
return query.orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
static async getById(id: string): Promise<PlatformAccount | null> {
|
||||
const account = await db(this.TABLE).where({ id }).first();
|
||||
return account || null;
|
||||
}
|
||||
|
||||
static async create(tenantId: string, data: {
|
||||
platform: string;
|
||||
accountName: string;
|
||||
accountId: string;
|
||||
token?: string;
|
||||
refreshToken?: string;
|
||||
expiresAt?: string;
|
||||
shopId?: string;
|
||||
config?: Record<string, any>;
|
||||
}): Promise<PlatformAccount> {
|
||||
const id = this.generateId();
|
||||
const now = new Date().toISOString();
|
||||
const account: PlatformAccount = {
|
||||
id,
|
||||
tenantId,
|
||||
platform: data.platform,
|
||||
accountName: data.accountName,
|
||||
accountId: data.accountId,
|
||||
status: 'ACTIVE',
|
||||
token: data.token,
|
||||
refreshToken: data.refreshToken,
|
||||
expiresAt: data.expiresAt,
|
||||
shopId: data.shopId,
|
||||
config: data.config,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
await db(this.TABLE).insert({
|
||||
id: account.id,
|
||||
tenant_id: account.tenantId,
|
||||
platform: account.platform,
|
||||
account_name: account.accountName,
|
||||
account_id: account.accountId,
|
||||
status: account.status,
|
||||
token: account.token,
|
||||
refresh_token: account.refreshToken,
|
||||
expires_at: account.expiresAt,
|
||||
shop_id: account.shopId,
|
||||
config: account.config ? JSON.stringify(account.config) : null,
|
||||
created_at: account.createdAt,
|
||||
updated_at: account.updatedAt,
|
||||
});
|
||||
await DomainEventBus.publish('platform_account.created', { accountId: id, tenantId, platform: data.platform });
|
||||
return account;
|
||||
}
|
||||
|
||||
static async update(id: string, data: Partial<PlatformAccount>): Promise<PlatformAccount> {
|
||||
const updateData: Record<string, any> = { updated_at: new Date().toISOString() };
|
||||
if (data.accountName) updateData.account_name = data.accountName;
|
||||
if (data.status) updateData.status = data.status;
|
||||
if (data.token !== undefined) updateData.token = data.token;
|
||||
if (data.refreshToken !== undefined) updateData.refresh_token = data.refreshToken;
|
||||
if (data.expiresAt !== undefined) updateData.expires_at = data.expiresAt;
|
||||
if (data.shopId !== undefined) updateData.shop_id = data.shopId;
|
||||
if (data.config) updateData.config = JSON.stringify(data.config);
|
||||
await db(this.TABLE).where({ id }).update(updateData);
|
||||
const account = await this.getById(id);
|
||||
await DomainEventBus.publish('platform_account.updated', { accountId: id });
|
||||
return account!;
|
||||
}
|
||||
|
||||
static async delete(id: string): Promise<void> {
|
||||
await db(this.TABLE).where({ id }).delete();
|
||||
await DomainEventBus.publish('platform_account.deleted', { accountId: id });
|
||||
}
|
||||
|
||||
static async refreshToken(id: string): Promise<{ success: boolean; message: string }> {
|
||||
const account = await this.getById(id);
|
||||
if (!account) return { success: false, message: 'Account not found' };
|
||||
if (!account.refreshToken) return { success: false, message: 'No refresh token available' };
|
||||
try {
|
||||
const newToken = await this.callPlatformRefreshApi(account.platform, account.refreshToken);
|
||||
await db(this.TABLE).where({ id }).update({
|
||||
token: newToken.token,
|
||||
expires_at: newToken.expiresAt,
|
||||
status: 'ACTIVE',
|
||||
updated_at: new Date().toISOString(),
|
||||
});
|
||||
await DomainEventBus.publish('platform_account.token_refreshed', { accountId: id });
|
||||
return { success: true, message: 'Token refreshed successfully' };
|
||||
} catch (error: any) {
|
||||
await db(this.TABLE).where({ id }).update({
|
||||
status: 'ERROR',
|
||||
updated_at: new Date().toISOString(),
|
||||
});
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
static async testConnection(id: string): Promise<{ success: boolean; message: string }> {
|
||||
const account = await this.getById(id);
|
||||
if (!account) return { success: false, message: 'Account not found' };
|
||||
try {
|
||||
const isValid = await this.validateToken(account.platform, account.token!);
|
||||
if (isValid) {
|
||||
await db(this.TABLE).where({ id }).update({ status: 'ACTIVE', updated_at: new Date().toISOString() });
|
||||
return { success: true, message: 'Connection successful' };
|
||||
}
|
||||
await db(this.TABLE).where({ id }).update({ status: 'EXPIRED', updated_at: new Date().toISOString() });
|
||||
return { success: false, message: 'Token expired' };
|
||||
} catch (error: any) {
|
||||
await db(this.TABLE).where({ id }).update({ status: 'ERROR', updated_at: new Date().toISOString() });
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
static async syncAccount(id: string): Promise<{ success: boolean; message: string }> {
|
||||
const account = await this.getById(id);
|
||||
if (!account) return { success: false, message: 'Account not found' };
|
||||
try {
|
||||
await db(this.TABLE).where({ id }).update({
|
||||
last_sync_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
});
|
||||
await DomainEventBus.publish('platform_account.sync_requested', { accountId: id, platform: account.platform });
|
||||
return { success: true, message: 'Sync initiated' };
|
||||
} catch (error: any) {
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
static async getStats(tenantId: string): Promise<PlatformAccountStats> {
|
||||
const accounts = await this.list(tenantId);
|
||||
return {
|
||||
total: accounts.length,
|
||||
active: accounts.filter((a) => a.status === 'ACTIVE').length,
|
||||
inactive: accounts.filter((a) => a.status === 'INACTIVE').length,
|
||||
expired: accounts.filter((a) => a.status === 'EXPIRED').length,
|
||||
error: accounts.filter((a) => a.status === 'ERROR').length,
|
||||
};
|
||||
}
|
||||
|
||||
private static async callPlatformRefreshApi(platform: string, refreshToken: string): Promise<{ token: string; expiresAt: string }> {
|
||||
return {
|
||||
token: `new_token_${Date.now()}`,
|
||||
expiresAt: new Date(Date.now() + 3600000).toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
private static async validateToken(platform: string, token: string): Promise<boolean> {
|
||||
return !!token;
|
||||
}
|
||||
|
||||
private static generateId(): string {
|
||||
return `pa_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user