更新服务器地址和项目名称,移除不必要的客户端日志相关代码,添加阻止交易模型,调整端口号及相关文档内容

This commit is contained in:
richarjiang
2025-08-13 17:31:21 +08:00
parent 4f9d648a50
commit 3b1af29c63
20 changed files with 138 additions and 786 deletions

View File

@@ -12,32 +12,18 @@ import { InjectModel, InjectConnection } from '@nestjs/sequelize';
import { Gender, User } from './models/user.model';
import { UserResponseDto } from './dto/user-response.dto';
import { ResponseCode } from 'src/base.dto';
import { UserRelationInfo } from './models/user-relation-info.model';
import { UserRelationInfoDto, UserRelationInfoResponseDto } from './dto/user-relation-info.dto';
import { TopicLibrary } from './models/topic-library.model';
import { Transaction, Op } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { UpdateUserDto, UpdateUserResponseDto } from './dto/update-user.dto';
import { TopicCategory } from './models/topic-category.model';
import { UserPurchase, PurchaseType, PurchaseStatus, PurchasePlatform } from './models/user-purchase.model';
import { ApplePurchaseService } from './services/apple-purchase.service';
import * as dayjs from 'dayjs';
import { UpdateMembershipDto, UpdateMembershipResponseDto } from './dto/membership.dto';
import { ClientLog } from './models/client-log.model';
import {
CreateClientLogDto,
CreateBatchClientLogDto,
CreateClientLogResponseDto,
CreateBatchClientLogResponseDto,
} from './dto/client-log.dto';
import { EncryptionService } from 'src/common/encryption.service';
import { AccessTokenPayload, AppleAuthService, AppleTokenPayload } from './services/apple-auth.service';
import { AppleLoginDto, AppleLoginResponseDto, RefreshTokenDto, RefreshTokenResponseDto } from './dto/apple-login.dto';
import { DeleteAccountDto, DeleteAccountResponseDto } from './dto/delete-account.dto';
import { GuestLoginDto, GuestLoginResponseDto, RefreshGuestTokenDto, RefreshGuestTokenResponseDto } from './dto/guest-login.dto';
import { AppStoreServerNotificationDto, ProcessNotificationResponseDto, NotificationType } from './dto/app-store-notification.dto';
import { TopicService } from './topic.service';
import { RevenueCatEvent } from './models/revenue-cat-event.model';
import { RevenueCatWebhookDto, RevenueCatEventType } from './dto/revenue-cat-webhook.dto';
import { RestorePurchaseDto, RestorePurchaseResponseDto, RestoredPurchaseInfo, ActiveEntitlement, NonSubscriptionTransaction } from './dto/restore-purchase.dto';
@@ -53,26 +39,16 @@ export class UsersService {
@Inject(WINSTON_MODULE_PROVIDER) private readonly winstonLogger: WinstonLogger,
@InjectModel(User)
private userModel: typeof User,
@InjectModel(UserRelationInfo)
private userRelationInfoModel: typeof UserRelationInfo,
@InjectModel(TopicLibrary)
private topicLibraryModel: typeof TopicLibrary,
@InjectModel(TopicCategory)
private topicCategoryModel: typeof TopicCategory,
@InjectModel(UserPurchase)
private userPurchaseModel: typeof UserPurchase,
@InjectModel(ClientLog)
private clientLogModel: typeof ClientLog,
@InjectModel(RevenueCatEvent)
private revenueCatEventModel: typeof RevenueCatEvent,
@InjectModel(PurchaseRestoreLog)
private purchaseRestoreLogModel: typeof PurchaseRestoreLog,
@InjectModel(BlockedTransaction)
private blockedTransactionModel: typeof BlockedTransaction,
private encryptionService: EncryptionService,
private appleAuthService: AppleAuthService,
private applePurchaseService: ApplePurchaseService,
private topicService: TopicService,
@InjectModel(BlockedTransaction)
private blockedTransactionModel: typeof BlockedTransaction,
@InjectConnection()
private sequelize: Sequelize,
) { }
@@ -101,13 +77,9 @@ export class UsersService {
};
}
// 查询已收藏的话题数量
const favoriteTopicCount = await this.topicService.getFavoriteTopicCount(existingUser.id);
const returnData = {
...existingUser.toJSON(),
maxUsageCount: DEFAULT_FREE_USAGE_COUNT,
favoriteTopicCount,
isVip: existingUser.isVip,
}
@@ -176,192 +148,6 @@ export class UsersService {
};
}
async updateOrCreateRelationInfo(relationInfoDto: UserRelationInfoDto): Promise<UserRelationInfoResponseDto> {
try {
this.logger.log(`updateOrCreateRelationInfo: ${JSON.stringify(relationInfoDto, null, 2)}`);
// 检查userId是否存在于用户表中
const user = await this.userModel.findByPk(relationInfoDto.userId);
if (!user) {
return {
code: ResponseCode.ERROR,
message: `ID为${relationInfoDto.userId}的用户不存在`,
data: null as any,
};
}
// 查找是否已存在关系信息
const existingInfo = await this.userRelationInfoModel.findOne({
where: { userId: relationInfoDto.userId },
});
if (existingInfo) {
// 存在则更新
await existingInfo.update(relationInfoDto, {
where: { userId: relationInfoDto.userId },
});
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: existingInfo,
};
} else {
// 不存在则创建
const newRelationInfo = await this.userRelationInfoModel.create({
...relationInfoDto,
});
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: newRelationInfo,
};
}
} catch (error) {
this.logger.error(`更新或创建关系信息失败: ${error instanceof Error ? error.message : '未知错误'}`);
if (error instanceof NotFoundException) {
throw error;
}
return {
code: ResponseCode.ERROR,
message: `更新或创建关系信息失败: ${error instanceof Error ? error.message : '未知错误'}`,
data: null as any,
};
}
}
async getRelationInfo(userId: string): Promise<UserRelationInfoResponseDto> {
try {
const relationInfos = await this.userRelationInfoModel.findAll({
where: { userId },
});
if (!relationInfos.length) {
const newRelationInfo = await this.userRelationInfoModel.create({
userId,
});
return {
code: ResponseCode.ERROR,
message: '关系信息不存在',
data: newRelationInfo,
};
}
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: relationInfos[0],
};
} catch (error) {
this.logger.error(`获取关系信息失败: ${error instanceof Error ? error.message : '未知错误'}`);
return {
code: ResponseCode.ERROR,
message: `获取关系信息失败: ${error instanceof Error ? error.message : '未知错误'}`,
data: null as any,
};
}
}
// 内部方法:计算订阅过期时间, 根据会员当前的有效期累加新的有效期
private calculateExpirationDate(purchaseType: PurchaseType, currentExpiration: Date): Date {
this.logger.log(`calculateExpirationDate purchaseType: ${purchaseType}, currentExpiration: ${currentExpiration}`);
switch (purchaseType) {
case PurchaseType.WEEKLY:
return dayjs(currentExpiration ?? new Date()).add(7, 'day').toDate();
case PurchaseType.QUARTERLY:
return dayjs(currentExpiration ?? new Date()).add(90, 'day').toDate();
case PurchaseType.LIFETIME:
return dayjs(currentExpiration ?? new Date()).add(365 * 100, 'day').toDate();
default:
throw new BadRequestException('无效的购买类型');
}
}
// 创建客户端日志
async createClientLog(createClientLogDto: CreateClientLogDto): Promise<CreateClientLogResponseDto> {
try {
this.logger.log(`createClientLog: ${JSON.stringify(createClientLogDto, null, 2)}`);
const { userId, logContent, logLevel, clientVersion, deviceModel, iosVersion, clientTimestamp } = createClientLogDto;
// 检查用户是否存在
const user = await this.userModel.findByPk(userId);
if (!user) {
return {
code: ResponseCode.ERROR,
message: `ID为${userId}的用户不存在`,
data: null as any,
};
}
// 创建日志记录
const clientLog = await this.clientLogModel.create({
userId,
logContent,
logLevel,
clientVersion,
deviceModel,
iosVersion,
clientTimestamp: clientTimestamp ? new Date(clientTimestamp) : null,
});
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: clientLog.toJSON(),
};
} catch (error) {
this.logger.error(`创建客户端日志失败: ${error instanceof Error ? error.message : '未知错误'}`);
return {
code: ResponseCode.ERROR,
message: `创建客户端日志失败: ${error instanceof Error ? error.message : '未知错误'}`,
data: null as any,
};
}
}
// 批量创建客户端日志
async createBatchClientLog(createBatchClientLogDto: CreateBatchClientLogDto): Promise<CreateBatchClientLogResponseDto> {
try {
this.logger.log(`createBatchClientLog: ${JSON.stringify(createBatchClientLogDto, null, 2)}`);
const { userId, logs } = createBatchClientLogDto;
// 检查用户是否存在
const user = await this.userModel.findByPk(userId);
if (!user) {
return {
code: ResponseCode.ERROR,
message: `ID为${userId}的用户不存在`,
data: null as any,
};
}
// 批量创建日志记录
const clientLogs = await this.clientLogModel.bulkCreate(
logs.map(log => ({
userId,
logContent: log.logContent,
logLevel: log.logLevel,
clientVersion: log.clientVersion,
deviceModel: log.deviceModel,
iosVersion: log.iosVersion,
clientTimestamp: log.clientTimestamp ? new Date(log.clientTimestamp) : null,
}))
);
return {
code: ResponseCode.SUCCESS,
message: 'success',
data: clientLogs.map(log => log.toJSON()),
};
} catch (error) {
this.logger.error(`批量创建客户端日志失败: ${error instanceof Error ? error.message : '未知错误'}`);
return {
code: ResponseCode.ERROR,
message: `批量创建客户端日志失败: ${error instanceof Error ? error.message : '未知错误'}`,
data: null as any,
};
}
}
/**
* Apple 登录
@@ -406,8 +192,6 @@ export class UsersService {
isNewUser = true;
this.logger.log(`创建新的Apple用户: ${userId}`);
// 创建三条话题,不消耗次数
await this.topicService.generateTopic('初识破冰', userId, false, true, 3);
} else {
// 更新现有用户的登录时间
user.lastLogin = new Date();
@@ -415,8 +199,6 @@ export class UsersService {
this.logger.log(`Apple用户登录: ${userId}`);
}
const favoriteTopicCount = await this.topicService.getFavoriteTopicCount(userId);
// 生成访问令牌和刷新令牌
const accessToken = this.appleAuthService.generateAccessToken(userId, user.mail);
const refreshToken = this.appleAuthService.generateRefreshToken(userId);
@@ -427,7 +209,6 @@ export class UsersService {
isNew: isNewUser,
isVip: user.isVip,
maxUsageCount: DEFAULT_FREE_USAGE_COUNT,
favoriteTopicCount,
};
return {
@@ -508,41 +289,19 @@ export class UsersService {
}
// 开始删除用户相关数据(使用事务确保数据一致性)
// 1. 删除用户关系信息
await this.userRelationInfoModel.destroy({
where: { userId },
transaction,
});
// 2. 删除用户购买记录
// 1. 删除用户购买记录
await this.userPurchaseModel.destroy({
where: { userId },
transaction,
});
// 3. 删除用户客户端日志
await this.clientLogModel.destroy({
where: { userId },
transaction,
});
// 4. 删除用户的个人话题
// 删除收藏
await this.topicService.deleteFavoriteTopics(userId, transaction);
await this.topicLibraryModel.destroy({
where: { userId },
transaction,
});
// 5. 最后删除用户本身
// 最后删除用户本身
await this.userModel.destroy({
where: { id: userId },
transaction,
});
// 提交事务
await transaction.commit();
@@ -605,8 +364,6 @@ export class UsersService {
isNewUser = true;
this.logger.log(`创建新的游客用户: ${guestUserId}`);
// 创建三条话题,不消耗次数
await this.topicService.generateTopic('初识破冰', guestUserId, false, true, 3);
} else {
// 更新现有游客用户的登录时间和设备信息
user.lastLogin = new Date();
@@ -627,7 +384,6 @@ export class UsersService {
isVip: user.membershipExpiration ? dayjs(user.membershipExpiration).isAfter(dayjs()) : false,
isGuest: true,
maxUsageCount: DEFAULT_FREE_USAGE_COUNT,
favoriteTopicCount: 0,
};
return {