refactor: 统一实体类命名规范并完善模板执行进度查询功能
- 统一所有实体类添加Entity后缀保持命名一致性 - 重命名User->UserEntity, PlatformUser->PlatformUserEntity等 - 更新所有实体间关联引用和Repository注入 - 新增模板执行进度查询接口GET /templates/execution/:taskId/progress - 新增用户执行任务列表查询接口GET /templates/executions/user/:userId - 完善数据库配置支持环境变量动态配置 - 添加class-validator和class-transformer依赖支持数据验证
This commit is contained in:
17
.env.example
Normal file
17
.env.example
Normal file
@@ -0,0 +1,17 @@
|
||||
# 数据库配置
|
||||
DB_HOST=mysql-6bc9094abd49-public.rds.volces.com
|
||||
DB_PORT=53305
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=WsJWXfwFx0bq6DE2kmB6
|
||||
DB_DATABASE=nano_camera_miniapp
|
||||
DB_SYNCHRONIZE=false
|
||||
DB_LOGGING=true
|
||||
DB_MIGRATIONS_RUN=true
|
||||
|
||||
# 应用配置
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
|
||||
# N8N配置
|
||||
N8N_BASE_URL=http://localhost:5678
|
||||
N8N_API_KEY=your_n8n_api_key
|
||||
@@ -1,17 +1,41 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AppController } from './app.controller';
|
||||
import { TemplateService } from './templates/index';
|
||||
import { TemplateManager } from './templates/types';
|
||||
import { typeOrmConfig } from './config/database.config';
|
||||
import { databaseConfig } from './config/database.config';
|
||||
import { N8nTemplateEntity } from './entities/n8n-template.entity';
|
||||
import { TemplateExecutionEntity } from './entities/template-execution.entity';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { PlatformUserEntity } from './entities/platform-user.entity';
|
||||
import { ExtensionDataEntity } from './entities/extension-data.entity';
|
||||
import { UserCreditEntity } from './entities/user-credit.entity';
|
||||
import { UserSubscriptionEntity } from './entities/user-subscription.entity';
|
||||
import { AdWatchEntity } from './entities/ad-watch.entity';
|
||||
import { N8nTemplateFactoryService } from './services/n8n-template-factory.service';
|
||||
import { TemplateController } from './controllers/template.controller';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot(typeOrmConfig),
|
||||
TypeOrmModule.forFeature([N8nTemplateEntity])
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: ['.env.local', '.env'],
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => databaseConfig(configService),
|
||||
}),
|
||||
TypeOrmModule.forFeature([
|
||||
N8nTemplateEntity,
|
||||
TemplateExecutionEntity,
|
||||
UserEntity,
|
||||
PlatformUserEntity,
|
||||
ExtensionDataEntity,
|
||||
UserCreditEntity,
|
||||
UserSubscriptionEntity,
|
||||
AdWatchEntity
|
||||
])
|
||||
],
|
||||
controllers: [AppController, TemplateController],
|
||||
providers: [
|
||||
|
||||
@@ -3,33 +3,16 @@ import { ConfigService } from '@nestjs/config';
|
||||
|
||||
export const databaseConfig = (configService: ConfigService): TypeOrmModuleOptions => ({
|
||||
type: 'mysql',
|
||||
host: 'mysql-6bc9094abd49-public.rds.volces.com',
|
||||
port: 53305,
|
||||
username: 'root',
|
||||
password: 'WsJWXfwFx0bq6DE2kmB6',
|
||||
database: 'nano_camera_miniapp',
|
||||
host: configService.get<string>('DB_HOST', 'mysql-6bc9094abd49-public.rds.volces.com'),
|
||||
port: configService.get<number>('DB_PORT', 53305),
|
||||
username: configService.get<string>('DB_USERNAME', 'root'),
|
||||
password: configService.get<string>('DB_PASSWORD', 'WsJWXfwFx0bq6DE2kmB6'),
|
||||
database: configService.get<string>('DB_DATABASE', 'nano_camera_miniapp'),
|
||||
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
|
||||
migrations: [__dirname + '/../migrations/*{.ts,.js}'],
|
||||
synchronize: false, // 生产环境建议设为false
|
||||
logging: process.env.NODE_ENV === 'development',
|
||||
migrationsRun: true, // 自动运行迁移
|
||||
synchronize: configService.get<boolean>('DB_SYNCHRONIZE', false), // 生产环境建议设为false
|
||||
logging: configService.get<boolean>('DB_LOGGING', process.env.NODE_ENV === 'development'),
|
||||
migrationsRun: configService.get<boolean>('DB_MIGRATIONS_RUN', true), // 自动运行迁移
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00',
|
||||
});
|
||||
|
||||
// 也可以直接导出配置对象
|
||||
export const typeOrmConfig: TypeOrmModuleOptions = {
|
||||
type: 'mysql',
|
||||
host: 'mysql-6bc9094abd49-public.rds.volces.com',
|
||||
port: 53305,
|
||||
username: 'root',
|
||||
password: 'WsJWXfwFx0bq6DE2kmB6',
|
||||
database: 'nano_camera_miniapp',
|
||||
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
|
||||
migrations: [__dirname + '/../migrations/*{.ts,.js}'],
|
||||
synchronize: false,
|
||||
logging: process.env.NODE_ENV === 'development',
|
||||
migrationsRun: true,
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00',
|
||||
};
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { N8nTemplateFactoryService } from '../services/n8n-template-factory.service';
|
||||
import { N8nTemplateEntity, TemplateType } from '../entities/n8n-template.entity';
|
||||
import { CreateTemplateDto, UpdateTemplateDto } from '../dto/template.dto';
|
||||
import { TemplateExecutionEntity } from '../entities/template-execution.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@@ -26,7 +27,9 @@ export class TemplateController {
|
||||
constructor(
|
||||
private readonly templateFactory: N8nTemplateFactoryService,
|
||||
@InjectRepository(N8nTemplateEntity)
|
||||
private readonly templateRepository: Repository<N8nTemplateEntity>
|
||||
private readonly templateRepository: Repository<N8nTemplateEntity>,
|
||||
@InjectRepository(TemplateExecutionEntity)
|
||||
private readonly executionRepository: Repository<TemplateExecutionEntity>
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -287,6 +290,112 @@ export class TemplateController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询模板执行进度
|
||||
* @param taskId 任务ID
|
||||
* @returns 执行进度信息
|
||||
*/
|
||||
@Get('execution/:taskId/progress')
|
||||
async getExecutionProgress(@Param('taskId', ParseIntPipe) taskId: number) {
|
||||
try {
|
||||
const execution = await this.executionRepository.findOne({
|
||||
where: { id: taskId },
|
||||
relations: ['template']
|
||||
});
|
||||
|
||||
if (!execution) {
|
||||
throw new HttpException('Execution task not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
taskId: execution.id,
|
||||
templateId: execution.templateId,
|
||||
templateName: execution.template?.name,
|
||||
userId: execution.userId,
|
||||
platform: execution.platform,
|
||||
type: execution.type,
|
||||
status: execution.status,
|
||||
progress: execution.progress,
|
||||
inputImageUrl: execution.inputImageUrl,
|
||||
outputUrl: execution.outputUrl,
|
||||
thumbnailUrl: execution.thumbnailUrl,
|
||||
errorMessage: execution.errorMessage,
|
||||
creditCost: execution.creditCost,
|
||||
startedAt: execution.startedAt,
|
||||
completedAt: execution.completedAt,
|
||||
executionDuration: execution.executionDuration,
|
||||
createdAt: execution.createdAt,
|
||||
updatedAt: execution.updatedAt
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to fetch execution progress',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户的执行任务列表
|
||||
* @param userId 用户ID
|
||||
* @param status 状态筛选
|
||||
* @param limit 数量限制
|
||||
* @returns 用户执行任务列表
|
||||
*/
|
||||
@Get('executions/user/:userId')
|
||||
async getUserExecutions(
|
||||
@Param('userId') userId: string,
|
||||
@Query('status') status?: string,
|
||||
@Query('limit') limit: number = 20
|
||||
) {
|
||||
try {
|
||||
const queryBuilder = this.executionRepository
|
||||
.createQueryBuilder('execution')
|
||||
.leftJoinAndSelect('execution.template', 'template')
|
||||
.where('execution.userId = :userId', { userId })
|
||||
.orderBy('execution.createdAt', 'DESC')
|
||||
.limit(limit);
|
||||
|
||||
if (status) {
|
||||
queryBuilder.andWhere('execution.status = :status', { status });
|
||||
}
|
||||
|
||||
const executions = await queryBuilder.getMany();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: executions.map(execution => ({
|
||||
taskId: execution.id,
|
||||
templateId: execution.templateId,
|
||||
templateName: execution.template?.name,
|
||||
templateCode: execution.template?.code,
|
||||
type: execution.type,
|
||||
status: execution.status,
|
||||
progress: execution.progress,
|
||||
inputImageUrl: execution.inputImageUrl,
|
||||
outputUrl: execution.outputUrl,
|
||||
thumbnailUrl: execution.thumbnailUrl,
|
||||
creditCost: execution.creditCost,
|
||||
startedAt: execution.startedAt,
|
||||
completedAt: execution.completedAt,
|
||||
executionDuration: execution.executionDuration,
|
||||
createdAt: execution.createdAt
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to fetch user executions',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新模板
|
||||
* @param createTemplateDto 创建模板的数据传输对象
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { PlatformType } from './platform-user.entity';
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ export enum AdStatus {
|
||||
* 用于积分奖励、数据统计和用户行为分析
|
||||
*/
|
||||
@Entity('ad_watches')
|
||||
export class AdWatch {
|
||||
export class AdWatchEntity {
|
||||
/** 主键 - 广告观看记录的唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -89,7 +89,7 @@ export class AdWatch {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 用户关系 - 与用户实体的多对一关系,级联删除 */
|
||||
@ManyToOne(() => User, user => user.adWatches, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => UserEntity, user => user.adWatches, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserEntity;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { PlatformType } from './platform-user.entity';
|
||||
|
||||
/**
|
||||
@@ -8,7 +8,7 @@ import { PlatformType } from './platform-user.entity';
|
||||
* 提供灵活的JSON存储机制,支持动态数据结构
|
||||
*/
|
||||
@Entity('extension_data')
|
||||
export class ExtensionData {
|
||||
export class ExtensionDataEntity {
|
||||
/** 主键 - 扩展数据记录的唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -53,7 +53,7 @@ export class ExtensionData {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 用户关系 - 与用户实体的多对一关系,级联删除 */
|
||||
@ManyToOne(() => User, user => user.extensionData, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => UserEntity, user => user.extensionData, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserEntity;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
|
||||
/**
|
||||
* 平台类型枚举
|
||||
@@ -27,7 +27,7 @@ export enum PlatformType {
|
||||
*/
|
||||
@Entity('platform_users')
|
||||
@Index(['platform', 'platformUserId'], { unique: true }) // 确保同一平台下的用户ID唯一
|
||||
export class PlatformUser {
|
||||
export class PlatformUserEntity {
|
||||
/** 主键 - 平台用户记录的唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -72,7 +72,7 @@ export class PlatformUser {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 用户关系 - 与统一用户实体的多对一关系,级联删除 */
|
||||
@ManyToOne(() => User, user => user.platformUsers, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => UserEntity, user => user.platformUsers, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserEntity;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { PlatformType } from './platform-user.entity';
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ export enum CreditSource {
|
||||
* 支持多平台积分系统和业务关联
|
||||
*/
|
||||
@Entity('user_credits')
|
||||
export class UserCredit {
|
||||
export class UserCreditEntity {
|
||||
/** 主键 - 积分记录的唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -91,7 +91,7 @@ export class UserCredit {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 用户关系 - 与用户实体的多对一关系,级联删除 */
|
||||
@ManyToOne(() => User, user => user.credits, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => UserEntity, user => user.credits, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserEntity;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { PlatformType } from './platform-user.entity';
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ export enum SubscriptionStatus {
|
||||
* 提供积分配额和特权功能管理
|
||||
*/
|
||||
@Entity('user_subscriptions')
|
||||
export class UserSubscription {
|
||||
export class UserSubscriptionEntity {
|
||||
/** 主键 - 订阅记录的唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -103,7 +103,7 @@ export class UserSubscription {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 用户关系 - 与用户实体的多对一关系,级联删除 */
|
||||
@ManyToOne(() => User, user => user.subscriptions, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => UserEntity, user => user.subscriptions, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserEntity;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
|
||||
import { PlatformUser } from './platform-user.entity';
|
||||
import { ExtensionData } from './extension-data.entity';
|
||||
import { UserCredit } from './user-credit.entity';
|
||||
import { PlatformUserEntity } from './platform-user.entity';
|
||||
import { ExtensionDataEntity } from './extension-data.entity';
|
||||
import { UserCreditEntity } from './user-credit.entity';
|
||||
import { TemplateExecutionEntity } from './template-execution.entity';
|
||||
import { UserSubscription } from './user-subscription.entity';
|
||||
import { AdWatch } from './ad-watch.entity';
|
||||
import { UserSubscriptionEntity } from './user-subscription.entity';
|
||||
import { AdWatchEntity } from './ad-watch.entity';
|
||||
|
||||
/**
|
||||
* 用户实体类
|
||||
@@ -13,7 +13,7 @@ import { AdWatch } from './ad-watch.entity';
|
||||
* 支持跨平台用户身份统一管理
|
||||
*/
|
||||
@Entity('users')
|
||||
export class User {
|
||||
export class UserEntity {
|
||||
/** 主键 - 用户的内部唯一标识符 */
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
@@ -51,26 +51,26 @@ export class User {
|
||||
updatedAt: Date;
|
||||
|
||||
/** 平台用户列表 - 用户在各个小程序平台的身份信息和授权令牌 */
|
||||
@OneToMany(() => PlatformUser, platformUser => platformUser.user)
|
||||
platformUsers: PlatformUser[];
|
||||
@OneToMany(() => PlatformUserEntity, platformUser => platformUser.user)
|
||||
platformUsers: PlatformUserEntity[];
|
||||
|
||||
/** 扩展数据列表 - 用户在各平台的特有业务数据和第三方集成数据 */
|
||||
@OneToMany(() => ExtensionData, extensionData => extensionData.user)
|
||||
extensionData: ExtensionData[];
|
||||
@OneToMany(() => ExtensionDataEntity, extensionData => extensionData.user)
|
||||
extensionData: ExtensionDataEntity[];
|
||||
|
||||
/** 积分记录列表 - 用户的所有积分变动明细和交易流水 */
|
||||
@OneToMany(() => UserCredit, userCredit => userCredit.user)
|
||||
credits: UserCredit[];
|
||||
@OneToMany(() => UserCreditEntity, userCredit => userCredit.user)
|
||||
credits: UserCreditEntity[];
|
||||
|
||||
/** 模板执行列表 - 用户使用AI模板生成内容的历史记录 */
|
||||
@OneToMany(() => TemplateExecutionEntity, execution => execution.userId)
|
||||
templateExecutions: TemplateExecutionEntity[];
|
||||
|
||||
/** 订阅列表 - 用户的会员订阅服务信息和历史记录 */
|
||||
@OneToMany(() => UserSubscription, userSubscription => userSubscription.user)
|
||||
subscriptions: UserSubscription[];
|
||||
@OneToMany(() => UserSubscriptionEntity, userSubscription => userSubscription.user)
|
||||
subscriptions: UserSubscriptionEntity[];
|
||||
|
||||
/** 广告观看列表 - 用户观看广告的完整记录和奖励明细 */
|
||||
@OneToMany(() => AdWatch, adWatch => adWatch.user)
|
||||
adWatches: AdWatch[];
|
||||
@OneToMany(() => AdWatchEntity, adWatch => adWatch.user)
|
||||
adWatches: AdWatchEntity[];
|
||||
}
|
||||
191
src/platform/adapters/base.adapter.ts
Normal file
191
src/platform/adapters/base.adapter.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Repository } from 'typeorm';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { UserEntity } from '../../entities/user.entity';
|
||||
import { PlatformUserEntity, PlatformType } from '../../entities/platform-user.entity';
|
||||
import {
|
||||
IPlatformAdapter,
|
||||
PlatformLoginData,
|
||||
PlatformRegisterData,
|
||||
UserAuthResult,
|
||||
PlatformUserInfo,
|
||||
TokenRefreshResult,
|
||||
UnifiedUserInfo
|
||||
} from '../interfaces/platform.interface';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseAdapter implements IPlatformAdapter {
|
||||
abstract platform: PlatformType;
|
||||
|
||||
constructor(
|
||||
protected readonly httpService: HttpService,
|
||||
protected readonly configService: ConfigService,
|
||||
protected readonly userRepository: Repository<UserEntity>,
|
||||
protected readonly platformUserRepository: Repository<PlatformUserEntity>,
|
||||
protected readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 查找或创建统一用户
|
||||
* 根据平台用户信息查找现有用户,如不存在则创建新用户
|
||||
*/
|
||||
async findOrCreateUnifiedUser(platformUserData: PlatformUserInfo): Promise<UserEntity> {
|
||||
// 查找是否已存在平台用户绑定
|
||||
const existingPlatformUser = await this.platformUserRepository.findOne({
|
||||
where: {
|
||||
platform: this.platform,
|
||||
platformUserId: platformUserData.platformUserId,
|
||||
},
|
||||
relations: ['user'],
|
||||
});
|
||||
|
||||
if (existingPlatformUser) {
|
||||
// 更新平台用户信息
|
||||
await this.updatePlatformUserInfo(existingPlatformUser, platformUserData);
|
||||
return existingPlatformUser.user;
|
||||
}
|
||||
|
||||
// 创建新的统一用户
|
||||
const unifiedUser = await this.createUnifiedUser(platformUserData);
|
||||
await this.createPlatformUser(unifiedUser.id, platformUserData);
|
||||
|
||||
return unifiedUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建统一用户记录
|
||||
*/
|
||||
protected async createUnifiedUser(platformData: PlatformUserInfo): Promise<UserEntity> {
|
||||
const user = new UserEntity();
|
||||
user.unifiedUserId = this.generateUnifiedUserId();
|
||||
user.nickname = platformData.nickname;
|
||||
user.avatarUrl = platformData.avatarUrl;
|
||||
user.phone = platformData.phone;
|
||||
user.email = platformData.email;
|
||||
user.status = 1; // 正常状态
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建平台用户绑定记录
|
||||
*/
|
||||
protected async createPlatformUser(userId: string, platformData: PlatformUserInfo, authData?: any): Promise<PlatformUserEntity> {
|
||||
const platformUser = new PlatformUserEntity();
|
||||
platformUser.userId = userId;
|
||||
platformUser.platform = this.platform;
|
||||
platformUser.platformUserId = platformData.platformUserId;
|
||||
platformUser.platformData = platformData;
|
||||
|
||||
if (authData) {
|
||||
platformUser.accessToken = authData.access_token;
|
||||
platformUser.refreshToken = authData.refresh_token;
|
||||
platformUser.expiresAt = authData.expires_at ? new Date(authData.expires_at * 1000) : null;
|
||||
}
|
||||
|
||||
return this.platformUserRepository.save(platformUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新平台用户信息
|
||||
*/
|
||||
protected async updatePlatformUserInfo(platformUser: PlatformUserEntity, newData: PlatformUserInfo): Promise<void> {
|
||||
platformUser.platformData = { ...platformUser.platformData, ...newData };
|
||||
await this.platformUserRepository.save(platformUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新平台用户认证数据
|
||||
*/
|
||||
protected async updatePlatformUserData(userId: string, authData: any, userInfo?: any): Promise<void> {
|
||||
const platformUser = await this.platformUserRepository.findOne({
|
||||
where: { userId, platform: this.platform }
|
||||
});
|
||||
|
||||
if (platformUser) {
|
||||
// 统一存储session_key到accessToken字段
|
||||
platformUser.accessToken = authData.session_key;
|
||||
platformUser.refreshToken = null; // 两平台都不支持refresh
|
||||
platformUser.expiresAt = null; // session_key无固定过期时间
|
||||
|
||||
if (userInfo) {
|
||||
platformUser.platformData = { ...platformUser.platformData, ...userInfo };
|
||||
}
|
||||
|
||||
await this.platformUserRepository.save(platformUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成统一用户ID
|
||||
*/
|
||||
protected generateUnifiedUserId(): string {
|
||||
return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成JWT令牌
|
||||
*/
|
||||
protected async generateTokens(user: UserEntity, platform: PlatformType): Promise<{
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
expiresAt: Date;
|
||||
}> {
|
||||
const payload = {
|
||||
userId: user.id,
|
||||
unifiedUserId: user.unifiedUserId,
|
||||
platform,
|
||||
};
|
||||
|
||||
const accessToken = this.jwtService.sign(payload, { expiresIn: '24h' });
|
||||
const refreshToken = this.jwtService.sign(payload, { expiresIn: '30d' });
|
||||
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24小时后
|
||||
|
||||
return { accessToken, refreshToken, expiresAt };
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化统一用户信息
|
||||
*/
|
||||
protected formatUnifiedUserInfo(user: UserEntity): UnifiedUserInfo {
|
||||
return {
|
||||
id: user.id,
|
||||
unifiedUserId: user.unifiedUserId,
|
||||
nickname: user.nickname,
|
||||
avatarUrl: user.avatarUrl,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
status: user.status,
|
||||
platforms: user.platformUsers?.map(pu => pu.platform) || [this.platform],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一错误处理
|
||||
*/
|
||||
protected handlePlatformError(error: any, platform: string): Error {
|
||||
// 统一不同平台的错误格式
|
||||
if (error.response?.data) {
|
||||
const errorData = error.response.data;
|
||||
// 微信格式: {errcode, errmsg}
|
||||
if (errorData.errcode) {
|
||||
return new Error(`${platform}错误[${errorData.errcode}]: ${errorData.errmsg}`);
|
||||
}
|
||||
// 抖音格式: {err_no, err_tips}
|
||||
if (errorData.err_no !== undefined) {
|
||||
return new Error(`${platform}错误[${errorData.err_no}]: ${errorData.err_tips}`);
|
||||
}
|
||||
}
|
||||
return new Error(`${platform}API调用失败: ${error.message}`);
|
||||
}
|
||||
|
||||
// 抽象方法 - 子类必须实现
|
||||
abstract login(loginData: PlatformLoginData): Promise<UserAuthResult>;
|
||||
abstract getUserInfo(token: string, platformUserId: string): Promise<PlatformUserInfo>;
|
||||
abstract refreshToken(refreshToken: string): Promise<TokenRefreshResult>;
|
||||
abstract register(registerData: PlatformRegisterData): Promise<UserAuthResult>;
|
||||
abstract updateUserInfo(userId: string, updateData: Partial<PlatformUserInfo>): Promise<void>;
|
||||
abstract validateToken(token: string): Promise<boolean>;
|
||||
abstract revokeToken(token: string): Promise<void>;
|
||||
}
|
||||
Reference in New Issue
Block a user