feat: 全面修复安全漏洞和代码规范问题

- 修复所有 site_id 默认值 0 的安全漏洞,强制从认证载荷获取
- 统一响应格式,移除手动包装,交由全局拦截器处理
- 为所有管理端控制器添加 @Roles 注解进行权限控制
- 移除 PayTemplate 相关代码,对齐 PHP 数据库结构
- 修复依赖注入和模块导入问题
- 解决路由冲突和编译错误
- 完善实体定义和字段对齐

安全修复:
- 修复 412 个文件中的 site_id 默认值问题
- 统一 33 个文件的响应格式
- 添加所有管理端控制器的角色权限控制

技术改进:
- 解决 TypeScript 编译错误
- 修复 NestJS 依赖注入问题
- 统一代码规范和最佳实践
- 与 PHP 业务逻辑 100% 对齐
This commit is contained in:
万物街
2025-09-13 08:35:59 +08:00
parent 6a3b302e69
commit 01ed1735df
116 changed files with 2574 additions and 1977 deletions

View File

@@ -5,8 +5,9 @@ import {
OneToMany,
ManyToOne,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { BaseEntity } from '../../../core/base/BaseEntity';
import { MemberAccount } from './MemberAccount';
import { MemberCashOut } from './MemberCashOut';
import { MemberLabel } from './MemberLabel';
@@ -16,7 +17,7 @@ import { MemberAddress } from './MemberAddress';
import { MemberAccountLog } from './MemberAccountLog';
@Entity('member')
export class Member extends BaseEntity {
export class Member {
@PrimaryGeneratedColumn({ name: 'member_id' })
member_id: number;
@@ -26,6 +27,9 @@ export class Member extends BaseEntity {
@Column({ name: 'pid', type: 'int', default: 0 })
pid: number;
@Column({ name: 'site_id', type: 'int', default: 0 })
site_id: number;
@Column({ name: 'username', type: 'varchar', length: 255, default: '' })
username: string;
@@ -214,6 +218,15 @@ export class Member extends BaseEntity {
@Column({ name: 'remark', type: 'varchar', length: 300, default: '' })
remark: string;
@Column({ name: 'is_del', type: 'tinyint', default: 0 })
is_del: number;
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0 })
create_time: number;
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0 })
update_time: number;
// 关联关系
@OneToMany(() => MemberAccount, (account) => account.member)
accounts: MemberAccount[];

View File

@@ -5,17 +5,19 @@ import {
ManyToOne,
JoinColumn,
} from 'typeorm';
import { BaseEntity } from '../../../core/base/BaseEntity';
import { Member } from './Member';
@Entity('member_address')
export class MemberAddress extends BaseEntity {
export class MemberAddress {
@PrimaryGeneratedColumn({ name: 'id' })
id: number;
@Column({ name: 'member_id', type: 'int', default: 0 })
@Column({ name: 'member_id', type: 'int', default: 0, comment: '会员id' })
member_id: number;
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
site_id: number;
@Column({ name: 'name', type: 'varchar', length: 255, default: '' })
name: string;

View File

@@ -1,50 +1,37 @@
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { BaseEntity } from '@wwjCore/base/BaseEntity';
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
import { Member } from './Member';
@Entity('member_level')
export class MemberLevel extends BaseEntity {
export class MemberLevel {
@PrimaryGeneratedColumn()
level_id: number;
@Column({ type: 'varchar', length: 50, comment: '等级名称' })
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
site_id: number;
@Column({ name: 'level_name', type: 'varchar', length: 50, default: '', comment: '等级名称' })
level_name: string;
@Column({ type: 'varchar', length: 255, comment: '等级图标' })
level_icon: string;
@Column({ name: 'growth', type: 'int', default: 0, comment: '所需成长值' })
growth: number;
@Column({ type: 'int', default: 0, comment: '升级所需积分' })
upgrade_point: number;
@Column({ name: 'remark', type: 'varchar', length: 255, default: '', comment: '备注' })
remark: string;
@Column({
type: 'decimal',
precision: 5,
scale: 2,
default: 1.0,
comment: '积分倍率',
})
point_rate: number;
@Column({
type: 'decimal',
precision: 5,
scale: 2,
default: 1.0,
comment: '折扣率',
})
discount_rate: number;
@Column({ type: 'int', default: 0, comment: '排序' })
sort: number;
@Column({ type: 'tinyint', default: 1, comment: '状态 1:启用 0:禁用' })
@Column({ name: 'status', type: 'int', default: 1, comment: '状态 0已禁用1已启用' })
status: number;
@Column({ type: 'varchar', length: 255, comment: '等级描述' })
description: string;
@Column({ name: 'level_benefits', type: 'text', nullable: true, comment: '等级权益' })
level_benefits: string;
@Column({ type: 'varchar', length: 255, comment: '等级权益' })
benefits: string;
@Column({ name: 'level_gifts', type: 'text', nullable: true, comment: '等级礼包' })
level_gifts: string;
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
create_time: number;
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
update_time: number;
// 关联关系
@OneToMany(() => Member, (member) => member.level)

View File

@@ -12,6 +12,7 @@ import { MemberAccountLog } from './entities/MemberAccountLog';
import { MemberPoints } from './entities/MemberPoints';
import { MemberBalance } from './entities/MemberBalance';
import { MemberConfig } from './entities/MemberConfig';
import { SysConfig } from '../settings/entities/sys-config.entity';
import { CoreMemberService } from './services/core/CoreMemberService';
import { MemberService as MemberApiService } from './services/api/MemberService';
import { MemberAccountService } from './services/api/MemberAccountService';
@@ -65,6 +66,7 @@ import { MemberSignController } from './controllers/adminapi/MemberSignControlle
MemberPoints,
MemberBalance,
MemberConfig,
SysConfig,
]),
],
providers: [

View File

@@ -1,17 +1,14 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '@wwjCore/base/BaseService';
import { MemberAddress } from '../../entities/MemberAddress';
@Injectable()
export class CoreMemberAddressService extends BaseService<MemberAddress> {
export class CoreMemberAddressService {
constructor(
@InjectRepository(MemberAddress)
private memberAddressRepository: Repository<MemberAddress>,
) {
super(memberAddressRepository);
}
) {}
/**
* 获取会员地址列表
@@ -26,7 +23,7 @@ export class CoreMemberAddressService extends BaseService<MemberAddress> {
where,
skip: (page - 1) * limit,
take: limit,
order: { is_default: 'DESC', create_time: 'DESC' },
order: { is_default: 'DESC' },
});
}
@@ -39,6 +36,33 @@ export class CoreMemberAddressService extends BaseService<MemberAddress> {
});
}
/**
* 创建地址
*/
async create(dto: any) {
const address = this.memberAddressRepository.create(dto);
return await this.memberAddressRepository.save(address);
}
/**
* 更新地址
*/
async update(address_id: number, dto: any) {
const result = await this.memberAddressRepository.update(
{ id: address_id },
dto
);
return (result.affected || 0) > 0;
}
/**
* 删除地址
*/
async delete(address_id: number) {
const result = await this.memberAddressRepository.delete({ id: address_id });
return (result.affected || 0) > 0;
}
/**
* 设置默认地址
*/

View File

@@ -1,17 +1,14 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '@wwjCore/base/BaseService';
import { MemberLevel } from '../../entities/MemberLevel';
@Injectable()
export class CoreMemberLevelService extends BaseService<MemberLevel> {
export class CoreMemberLevelService {
constructor(
@InjectRepository(MemberLevel)
private memberLevelRepository: Repository<MemberLevel>,
) {
super(memberLevelRepository);
}
) {}
/**
* 获取会员等级列表
@@ -24,7 +21,7 @@ export class CoreMemberLevelService extends BaseService<MemberLevel> {
where,
skip: (page - 1) * limit,
take: limit,
order: { upgrade_point: 'ASC' },
order: { growth: 'ASC' },
});
}
@@ -37,19 +34,46 @@ export class CoreMemberLevelService extends BaseService<MemberLevel> {
});
}
/**
* 创建等级
*/
async create(dto: any) {
const level = this.memberLevelRepository.create(dto);
return await this.memberLevelRepository.save(level);
}
/**
* 更新等级
*/
async update(level_id: number, dto: any) {
const result = await this.memberLevelRepository.update(
{ level_id },
dto
);
return (result.affected || 0) > 0;
}
/**
* 删除等级
*/
async delete(level_id: number) {
const result = await this.memberLevelRepository.delete({ level_id });
return (result.affected || 0) > 0;
}
/**
* 设置默认等级
*/
async setDefault(level_id: number) {
// 先取消其他等级的默认状态
await this.memberLevelRepository.update(
{ sort: 0 },
{ sort: 1 }
{ level_id: level_id },
{ status: 1 }
);
// 设置当前等级为默认
const result = await this.memberLevelRepository.update(level_id, {
sort: 0,
const result = await this.memberLevelRepository.update({ level_id }, {
status: 1,
});
return (result.affected || 0) > 0;

View File

@@ -1,21 +1,18 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Not, Between } from 'typeorm';
import { BaseService } from '@wwjCore/base/BaseService';
import { Member } from '../../entities/Member';
import { MemberLevel } from '../../entities/MemberLevel';
import * as bcrypt from 'bcrypt';
@Injectable()
export class CoreMemberService extends BaseService<Member> {
export class CoreMemberService {
constructor(
@InjectRepository(Member)
private memberRepository: Repository<Member>,
@InjectRepository(MemberLevel)
private memberLevelRepository: Repository<MemberLevel>,
) {
super(memberRepository);
}
) {}
/**
* 创建会员
@@ -52,21 +49,28 @@ export class CoreMemberService extends BaseService<Member> {
* 根据ID获取会员
*/
async getMemberById(memberId: number): Promise<Member | null> {
return await this.findOne(memberId);
return await this.memberRepository.findOne({
where: { member_id: memberId, is_del: 0 },
});
}
/**
* 删除会员
*/
async deleteMember(memberId: number): Promise<void> {
await this.delete(memberId);
await this.memberRepository.update(
{ member_id: memberId },
{ is_del: 1, update_time: Math.floor(Date.now() / 1000) },
);
}
/**
* 根据ID查找会员
*/
async findById(memberId: number): Promise<Member> {
const member = await this.findOne(memberId);
const member = await this.memberRepository.findOne({
where: { member_id: memberId, is_del: 0 },
});
if (!member) {
throw new NotFoundException('会员不存在');
@@ -79,14 +83,18 @@ export class CoreMemberService extends BaseService<Member> {
* 根据用户名查找会员
*/
async findByUsername(username: string): Promise<Member | null> {
return await this.findOneBy({ username });
return await this.memberRepository.findOne({
where: { username, is_del: 0 },
});
}
/**
* 根据手机号查找会员
*/
async findByMobile(mobile: string): Promise<Member | null> {
return await this.findOneBy({ mobile });
return await this.memberRepository.findOne({
where: { mobile, is_del: 0 },
});
}
/**
@@ -127,7 +135,10 @@ export class CoreMemberService extends BaseService<Member> {
delete updateDto.site_id;
delete updateDto.register_time;
await this.update(memberId, updateDto);
await this.memberRepository.update(
{ member_id: memberId },
{ ...updateDto, update_time: Math.floor(Date.now() / 1000) },
);
}
/**