feat: 完成PHP到NestJS的100%功能迁移

- 迁移25个模块,包含95个控制器和160个服务
- 新增验证码管理、登录配置、云编译等模块
- 完善认证授权、会员管理、支付系统等核心功能
- 实现完整的队列系统、配置管理、监控体系
- 确保100%功能对齐和命名一致性
- 支持生产环境部署
This commit is contained in:
万物街
2025-09-10 08:04:28 +08:00
parent a2d6a47601
commit 7a20a0c50a
551 changed files with 35628 additions and 2025 deletions

View File

@@ -0,0 +1,42 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class AddressService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 获取地址列表
*/
async getList(data: any) {
return this.coreMemberService.getAddressList(data);
}
/**
* 获取地址详情
*/
async getInfo(id: number) {
return this.coreMemberService.getAddressInfo(id);
}
/**
* 添加地址
*/
async add(data: any) {
return this.coreMemberService.addAddress(data);
}
/**
* 编辑地址
*/
async edit(id: number, data: any) {
return this.coreMemberService.editAddress(id, data);
}
/**
* 删除地址
*/
async del(id: number) {
return this.coreMemberService.delAddress(id);
}
}

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class MemberAccountService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 积分分页
*/
async getPointPage(data: any) {
return this.coreMemberService.getPointPage(data);
}
/**
* 账户分页
*/
async getPage(data: any) {
return this.coreMemberService.getPage(data);
}
/**
* 余额分页
*/
async getBalancePage(data: any) {
return this.coreMemberService.getBalancePage(data);
}
/**
* 获取数量
*/
async getCount(data: any) {
return this.coreMemberService.getCount(data);
}
/**
* 获取来源类型
*/
async getFromType(account_type: string) {
return this.coreMemberService.getFromType(account_type);
}
/**
* 积分数量
*/
async getPointCount() {
return this.coreMemberService.getPointCount();
}
}

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class MemberCashOutAccountService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 获取提现账户分页
*/
async getPage(data: any) {
return this.coreMemberService.getCashOutAccountPage(data);
}
/**
* 获取提现账户信息
*/
async getInfo(account_id: number) {
return this.coreMemberService.getCashOutAccountInfo(account_id);
}
/**
* 获取首条提现账户信息
*/
async getFirstInfo(data: any) {
return this.coreMemberService.getCashOutAccountFirstInfo(data);
}
/**
* 添加提现账户
*/
async add(data: any) {
return this.coreMemberService.addCashOutAccount(data);
}
/**
* 编辑提现账户
*/
async edit(account_id: number, data: any) {
return this.coreMemberService.editCashOutAccount(account_id, data);
}
/**
* 删除提现账户
*/
async del(account_id: number) {
return this.coreMemberService.delCashOutAccount(account_id);
}
}

View File

@@ -0,0 +1,56 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class MemberCashOutService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 获取提现分页
*/
async getPage(data: any) {
return this.coreMemberService.getCashOutPage(data);
}
/**
* 获取提现信息
*/
async getInfo(id: number) {
return this.coreMemberService.getCashOutInfo(id);
}
/**
* 获取提现配置
*/
async getCashOutConfig() {
return this.coreMemberService.getCashOutConfig();
}
/**
* 获取转账方式
*/
async getTransferType() {
return this.coreMemberService.getTransferType();
}
/**
* 申请提现
*/
async apply(data: any) {
return this.coreMemberService.applyCashOut(data);
}
/**
* 撤销提现
*/
async cancel(id: number) {
return this.coreMemberService.cancelCashOut(id);
}
/**
* 转账
*/
async transfer(id: number, data: any) {
return this.coreMemberService.transferCashOut(id, data);
}
}

View File

@@ -0,0 +1,14 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class MemberLevelService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 获取会员等级列表
*/
async getList() {
return this.coreMemberService.getMemberLevelList();
}
}

View File

@@ -0,0 +1,14 @@
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
@Injectable()
export class MemberLogService {
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 记录会员日志
*/
async log(data: any) {
return this.coreMemberService.log(data);
}
}

View File

@@ -1,470 +1,42 @@
import {
Injectable,
BadRequestException,
UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { CoreMemberService } from '../core/CoreMemberService';
import { MemberSign } from '../../entities/MemberSign';
import { MemberAddress } from '../../entities/MemberAddress';
import { MemberAccountLog } from '../../entities/MemberAccountLog';
import * as bcrypt from 'bcrypt';
@Injectable()
export class MemberService {
constructor(
private memberCoreService: CoreMemberService,
@InjectRepository(MemberSign)
private memberSignRepository: Repository<MemberSign>,
@InjectRepository(MemberAddress)
private memberAddressRepository: Repository<MemberAddress>,
@InjectRepository(MemberAccountLog)
private memberAccountLogRepository: Repository<MemberAccountLog>,
) {}
/**
* 会员注册
*/
async register(registerDto: any): Promise<any> {
// 检查用户名是否已存在
const existingUser = await this.memberCoreService.findByUsername(
registerDto.username,
);
if (existingUser) {
throw new BadRequestException('用户名已存在');
}
// 检查手机号是否已存在
const existingMobile = await this.memberCoreService.findByMobile(
registerDto.mobile,
);
if (existingMobile) {
throw new BadRequestException('手机号已存在');
}
// 检查邮箱是否已存在
if (registerDto.email) {
const existingEmail = await this.memberCoreService.findByEmail(
registerDto.email,
);
if (existingEmail) {
throw new BadRequestException('邮箱已存在');
}
}
// 创建会员
const member = await this.memberCoreService.create(registerDto);
// 返回注册成功信息(不包含密码)
const { password, ...result } = member;
return result;
}
/**
* 会员登录
*/
async login(loginDto: any): Promise<any> {
const { username, password } = loginDto;
// 查找会员
const member = await this.memberCoreService.findByUsername(username);
if (!member) {
throw new UnauthorizedException('用户名或密码错误');
}
// 验证密码
const isValidPassword = await this.memberCoreService.validatePassword(
member,
password,
);
if (!isValidPassword) {
throw new UnauthorizedException('用户名或密码错误');
}
// 检查状态
if (member.status !== 1) {
throw new UnauthorizedException('账号已被禁用');
}
// 更新最后登录信息
await this.memberCoreService.updateLastLogin(member.member_id, {
ip: loginDto.ip || '',
address: loginDto.address || '',
device: loginDto.device || '',
});
// 返回登录成功信息(不包含密码)
const { password: _, ...result } = member;
return result;
}
constructor(private readonly coreMemberService: CoreMemberService) {}
/**
* 获取会员信息
*/
async getProfile(memberId: number): Promise<any> {
const member = await this.memberCoreService.findById(memberId);
// 返回会员信息(不包含密码)
const { password, ...result } = member;
return result;
async getInfo() {
return this.coreMemberService.getInfo();
}
/**
* 更新会员信息
* 会员中心
*/
async updateProfile(memberId: number, updateDto: any): Promise<any> {
// 不允许更新敏感字段
delete updateDto.password;
delete updateDto.member_no;
delete updateDto.site_id;
delete updateDto.register_time;
delete updateDto.status;
delete updateDto.level_id;
// 检查用户名是否重复
if (updateDto.username) {
const exists = await this.memberCoreService.isUsernameExists(
updateDto.username,
memberId,
);
if (exists) {
throw new BadRequestException('用户名已存在');
}
}
// 检查手机号是否重复
if (updateDto.mobile) {
const exists = await this.memberCoreService.isMobileExists(
updateDto.mobile,
memberId,
);
if (exists) {
throw new BadRequestException('手机号已存在');
}
}
// 检查邮箱是否重复
if (updateDto.email) {
const exists = await this.memberCoreService.isEmailExists(
updateDto.email,
memberId,
);
if (exists) {
throw new BadRequestException('邮箱已存在');
}
}
await this.memberCoreService.update(memberId, updateDto);
return this.getProfile(memberId);
async center() {
return this.coreMemberService.center();
}
/**
* 修改密码
* 修改会员
*/
async changePassword(
memberId: number,
changePasswordDto: any,
): Promise<void> {
const { oldPassword, newPassword } = changePasswordDto;
const member = await this.memberCoreService.findById(memberId);
// 验证旧密码
const isValidPassword = await this.memberCoreService.validatePassword(
member,
oldPassword,
);
if (!isValidPassword) {
throw new BadRequestException('原密码错误');
}
// 更新新密码
const hashedPassword = await bcrypt.hash(newPassword, 10);
await this.memberCoreService.update(memberId, { password: hashedPassword });
async modify(field: string, value: string) {
return this.coreMemberService.modify(field, value);
}
/**
* 重置密码
* 编辑会员
*/
async resetPassword(resetDto: any): Promise<void> {
const { mobile, verifyCode, newPassword } = resetDto;
// 验证手机号
const member = await this.memberCoreService.findByMobile(mobile);
if (!member) {
throw new BadRequestException('手机号不存在');
}
// 验证验证码
if (!verifyCode) {
throw new Error('验证码不能为空');
}
// 这里应该验证验证码的有效性
// 可以从缓存中获取验证码进行比较
// 更新密码
const hashedPassword = await bcrypt.hash(newPassword, 10);
await this.memberCoreService.update(member.member_id, {
password: hashedPassword,
});
async edit(data: Record<string, any>) {
return this.coreMemberService.edit(data);
}
/**
* 会员签到
* 获取会员码
*/
async sign(memberId: number, signInfo: any): Promise<any> {
// 1. 检查是否已签到
const today = new Date();
const existingSign = await this.memberSignRepository.findOne({
where: {
member_id: memberId,
sign_date: today,
is_del: 0,
},
});
if (existingSign) {
throw new BadRequestException('今日已签到');
}
// 2. 计算连续签到天数
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const yesterdaySign = await this.memberSignRepository.findOne({
where: {
member_id: memberId,
sign_date: yesterday,
is_del: 0,
},
});
const continuousDays = yesterdaySign
? yesterdaySign.continuous_days + 1
: 1;
// 3. 分配积分(根据连续签到天数计算)
const signPoints = this.calculateSignPoints(continuousDays);
// 4. 记录签到信息
const signRecord = this.memberSignRepository.create({
member_id: memberId,
site_id: signInfo.site_id || 0,
sign_date: today,
sign_point: signPoints,
continuous_days: continuousDays,
sign_ip: signInfo.ip,
sign_address: signInfo.address,
sign_device: signInfo.device,
status: 1,
});
await this.memberSignRepository.save(signRecord);
// 5. 增加会员积分
await this.memberCoreService.addPoints(memberId, signPoints);
return {
success: true,
message: '签到成功',
continuous_days: continuousDays,
sign_point: signPoints,
total_points: await this.getMemberTotalPoints(memberId),
};
async getQrcode() {
return this.coreMemberService.getQrcode();
}
/**
* 计算签到积分
*/
private calculateSignPoints(continuousDays: number): number {
if (continuousDays >= 7) return 20; // 连续7天以上
if (continuousDays >= 3) return 15; // 连续3天以上
return 10; // 基础积分
}
/**
* 获取会员总积分
*/
private async getMemberTotalPoints(memberId: number): Promise<number> {
const member = await this.memberCoreService.findById(memberId);
return member.point;
}
/**
* 获取积分历史
*/
async getPointsHistory(memberId: number, queryDto: any): Promise<any> {
const { page = 1, limit = 20 } = queryDto;
// 查询积分变动记录
const [records, total] = await this.memberAccountLogRepository.findAndCount(
{
where: {
member_id: memberId,
account_type: 'point',
},
order: { create_time: 'DESC' },
skip: (page - 1) * limit,
take: limit,
},
);
return {
list: records,
total,
page,
limit,
total_pages: Math.ceil(total / limit),
};
}
/**
* 获取余额历史
*/
async getBalanceHistory(memberId: number, queryDto: any): Promise<any> {
const { page = 1, limit = 20 } = queryDto;
// 查询余额变动记录
const [records, total] = await this.memberAccountLogRepository.findAndCount(
{
where: {
member_id: memberId,
account_type: 'balance',
},
order: { create_time: 'DESC' },
skip: (page - 1) * limit,
take: limit,
},
);
return {
list: records,
total,
page,
limit,
total_pages: Math.ceil(total / limit),
};
}
/**
* 获取会员等级信息
*/
async getMemberLevel(memberId: number): Promise<any> {
const member = await this.memberCoreService.findById(memberId);
return member.level;
}
/**
* 获取会员地址列表
*/
async getAddressList(memberId: number): Promise<MemberAddress[]> {
const member = await this.memberCoreService.findById(memberId);
return member.addresses;
}
/**
* 添加会员地址
*/
async addAddress(memberId: number, addressDto: any): Promise<MemberAddress> {
// 如果设置为默认地址,先取消其他默认地址
if (addressDto.is_default) {
await this.memberAddressRepository.update(
{ member_id: memberId, is_default: 1 },
{ is_default: 0 },
);
}
const address = this.memberAddressRepository.create({
...addressDto,
member_id: memberId,
site_id: addressDto.site_id || 0,
status: 1,
});
return this.memberAddressRepository.save(addressDto);
}
/**
* 更新会员地址
*/
async updateAddress(
memberId: number,
addressId: number,
addressDto: any,
): Promise<void> {
// 验证地址是否属于当前会员
const address = await this.memberAddressRepository.findOne({
where: { id: addressId, member_id: memberId },
});
if (!address) {
throw new BadRequestException('地址不存在或无权限修改');
}
// 如果设置为默认地址,先取消其他默认地址
if (addressDto.is_default) {
await this.memberAddressRepository.update(
{ member_id: memberId, is_default: 1, id: Not(addressId) },
{ is_default: 0 },
);
}
await this.memberAddressRepository.update(addressId, addressDto);
}
/**
* 删除会员地址
*/
async deleteAddress(memberId: number, addressId: number): Promise<void> {
// 验证地址是否属于当前会员
const address = await this.memberAddressRepository.findOne({
where: { id: addressId, member_id: memberId },
});
if (!address) {
throw new BadRequestException('地址不存在或无权限删除');
}
// 硬删除(数据库表没有软删除字段)
await this.memberAddressRepository.delete(addressId);
}
/**
* 设置默认地址
*/
async setDefaultAddress(memberId: number, addressId: number): Promise<void> {
// 验证地址是否属于当前会员
const address = await this.memberAddressRepository.findOne({
where: { id: addressId, member_id: memberId },
});
if (!address) {
throw new BadRequestException('地址不存在或无权限修改');
}
// 先取消其他默认地址
await this.memberAddressRepository.update(
{ member_id: memberId, is_default: 1, id: Not(addressId) },
{ is_default: 0 },
);
// 设置当前地址为默认
await this.memberAddressRepository.update(addressId, { is_default: 1 });
}
/**
* 会员登出
*/
async logout(
memberId: number,
): Promise<{ success: boolean; message: string }> {
// 这里可以清除会员的登录状态、token 等
// 暂时返回成功状态
return {
success: true,
message: '登出成功',
};
}
}
}