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,83 @@
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../../auth/guards/roles.guard';
import { NoticeAdminService } from '../../services/admin/NoticeAdminService';
import { NoticeLogAdminService } from '../../services/admin/NoticeLogAdminService';
import { NoticeSmsLogAdminService } from '../../services/admin/NoticeSmsLogAdminService';
import { NoticeNameDto, AddNoticeDto, EditNoticeDto, NoticeLogNameDto, NoticeSmsLogNameDto } from '../../dto/NoticeDto';
@Controller('adminapi/notice')
@UseGuards(JwtAuthGuard, RolesGuard)
export class NoticeController {
constructor(
private readonly noticeAdminService: NoticeAdminService,
private readonly noticeLogAdminService: NoticeLogAdminService,
private readonly noticeSmsLogAdminService: NoticeSmsLogAdminService,
) {}
// 通知配置管理
@Get('lists')
async getNoticeLists(@Query() query: NoticeNameDto) {
return await this.noticeAdminService.getNoticeLists(query);
}
@Get('info/:id')
async getNoticeInfo(@Param('id') id: number) {
return await this.noticeAdminService.getNoticeInfo(id);
}
@Post('add')
async addNotice(@Body() data: AddNoticeDto) {
return await this.noticeAdminService.addNotice(data);
}
@Put('edit/:id')
async editNotice(@Param('id') id: number, @Body() data: EditNoticeDto) {
return await this.noticeAdminService.editNotice(id, data);
}
@Delete('delete/:id')
async deleteNotice(@Param('id') id: number) {
return await this.noticeAdminService.deleteNotice(id);
}
// 通知日志管理
@Get('log/lists')
async getNoticeLogLists(@Query() query: NoticeLogNameDto) {
return await this.noticeLogAdminService.getNoticeLogLists(query);
}
@Get('log/info/:id')
async getNoticeLogInfo(@Param('id') id: number) {
return await this.noticeLogAdminService.getNoticeLogInfo(id);
}
@Delete('log/delete/:id')
async deleteNoticeLog(@Param('id') id: number) {
return await this.noticeLogAdminService.deleteNoticeLog(id);
}
// 短信日志管理
@Get('sms/log/lists')
async getNoticeSmsLogLists(@Query() query: NoticeSmsLogNameDto) {
return await this.noticeSmsLogAdminService.getNoticeSmsLogLists(query);
}
@Get('sms/log/info/:id')
async getNoticeSmsLogInfo(@Param('id') id: number) {
return await this.noticeSmsLogAdminService.getNoticeSmsLogInfo(id);
}
@Delete('sms/log/delete/:id')
async deleteNoticeSmsLog(@Param('id') id: number) {
return await this.noticeSmsLogAdminService.deleteNoticeSmsLog(id);
}
@Put('sms/log/status/:id')
async updateSmsLogStatus(
@Param('id') id: number,
@Body() data: { status: string; result?: string }
) {
return await this.noticeSmsLogAdminService.updateSendStatus(id, data.status, data.result);
}
}

View File

@@ -0,0 +1,145 @@
import { IsOptional, IsString, IsNumber, IsEnum } from 'class-validator';
export class NoticeNameDto {
@IsOptional()
@IsString()
key?: string;
@IsOptional()
@IsNumber()
site_id?: number;
@IsOptional()
@IsNumber()
page?: number;
@IsOptional()
@IsNumber()
pageSize?: number;
}
export class AddNoticeDto {
@IsNumber()
site_id: number;
@IsString()
key: string;
@IsOptional()
@IsString()
sms_content?: string;
@IsNumber()
is_wechat: number;
@IsNumber()
is_weapp: number;
@IsNumber()
is_sms: number;
@IsString()
wechat_template_id: string;
@IsString()
weapp_template_id: string;
@IsString()
sms_id: string;
@IsString()
wechat_first: string;
@IsString()
wechat_remark: string;
}
export class EditNoticeDto {
@IsOptional()
@IsString()
sms_content?: string;
@IsOptional()
@IsNumber()
is_wechat?: number;
@IsOptional()
@IsNumber()
is_weapp?: number;
@IsOptional()
@IsNumber()
is_sms?: number;
@IsOptional()
@IsString()
wechat_template_id?: string;
@IsOptional()
@IsString()
weapp_template_id?: string;
@IsOptional()
@IsString()
sms_id?: string;
@IsOptional()
@IsString()
wechat_first?: string;
@IsOptional()
@IsString()
wechat_remark?: string;
}
export class NoticeLogNameDto {
@IsOptional()
@IsString()
key?: string;
@IsOptional()
@IsString()
notice_type?: string;
@IsOptional()
@IsNumber()
site_id?: number;
@IsOptional()
@IsNumber()
page?: number;
@IsOptional()
@IsNumber()
pageSize?: number;
}
export class NoticeSmsLogNameDto {
@IsOptional()
@IsString()
mobile?: string;
@IsOptional()
@IsString()
sms_type?: string;
@IsOptional()
@IsString()
key?: string;
@IsOptional()
@IsString()
status?: string;
@IsOptional()
@IsNumber()
site_id?: number;
@IsOptional()
@IsNumber()
page?: number;
@IsOptional()
@IsNumber()
pageSize?: number;
}

View File

View File

@@ -0,0 +1,49 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('sys_notice_log')
export class SysNoticeLog {
@PrimaryGeneratedColumn({ name: 'id', comment: '通知记录ID' })
id: number;
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
site_id: number;
@Column({ name: 'key', type: 'varchar', length: 255, nullable: true, default: '', comment: '消息key' })
key: string;
@Column({ name: 'notice_type', type: 'varchar', length: 50, nullable: true, default: 'sms', comment: '消息类型sms,wechat.weapp' })
notice_type: string;
@Column({ name: 'uid', type: 'int', unsigned: true, default: 0, comment: '通知的用户id' })
uid: number;
@Column({ name: 'member_id', type: 'int', default: 0, comment: '消息的会员id' })
member_id: number;
@Column({ name: 'nickname', type: 'varchar', length: 255, default: '', comment: '接收人用户昵称或姓名' })
nickname: string;
@Column({ name: 'receiver', type: 'varchar', length: 255, default: '', comment: '接收人对应手机号openid' })
receiver: string;
@Column({ name: 'content', type: 'text', nullable: true, comment: '消息数据' })
content: string;
@Column({ name: 'is_click', type: 'tinyint', unsigned: true, default: 0, comment: '点击次数' })
is_click: number;
@Column({ name: 'is_visit', type: 'tinyint', unsigned: true, default: 0, comment: '访问次数' })
is_visit: number;
@Column({ name: 'visit_time', type: 'int', default: 0, comment: '访问时间' })
visit_time: number;
@Column({ name: 'create_time', type: 'int', unsigned: true, default: 0, comment: '消息时间' })
create_time: number;
@Column({ name: 'result', type: 'varchar', length: 1000, default: '', comment: '结果' })
result: string;
@Column({ name: 'params', type: 'text', nullable: true })
params: string;
}

View File

@@ -0,0 +1,46 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('sys_notice_sms_log')
export class SysNoticeSmsLog {
@PrimaryGeneratedColumn({ comment: 'id' })
id: number;
@Column({ name: 'site_id', type: 'int', default: 0 })
site_id: number;
@Column({ name: 'mobile', type: 'varchar', length: 11, default: '', comment: '手机号码' })
mobile: string;
@Column({ name: 'sms_type', type: 'varchar', length: 32, default: '', comment: '发送关键字(注册、找回密码)' })
sms_type: string;
@Column({ name: 'key', type: 'varchar', length: 32, default: '', comment: '发送关键字(注册、找回密码)' })
key: string;
@Column({ name: 'template_id', type: 'varchar', length: 50, default: '' })
template_id: string;
@Column({ name: 'content', type: 'text', comment: '发送内容' })
content: string;
@Column({ name: 'params', type: 'text', comment: '数据参数' })
params: string;
@Column({ name: 'status', type: 'varchar', length: 32, default: 'sending', comment: '发送状态sending-发送中success-发送成功fail-发送失败' })
status: string;
@Column({ name: 'result', type: 'text', nullable: true, comment: '短信结果' })
result: string;
@Column({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
create_time: number;
@Column({ name: 'send_time', type: 'int', default: 0, comment: '发送时间' })
send_time: number;
@Column({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
update_time: number;
@Column({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
delete_time: number;
}

View File

@@ -0,0 +1,43 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('sys_notice')
export class SysNotice {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点ID' })
site_id: number;
@Column({ name: 'key', type: 'varchar', length: 50, default: '', comment: '标识' })
key: string;
@Column({ name: 'sms_content', type: 'text', nullable: true, comment: '短信配置参数' })
sms_content: string;
@Column({ name: 'is_wechat', type: 'tinyint', default: 0, comment: '公众号模板消息0关闭1开启' })
is_wechat: number;
@Column({ name: 'is_weapp', type: 'tinyint', default: 0, comment: '小程序订阅消息0关闭1开启' })
is_weapp: number;
@Column({ name: 'is_sms', type: 'tinyint', default: 0, comment: '发送短信0关闭1开启' })
is_sms: number;
@Column({ name: 'wechat_template_id', type: 'varchar', length: 255, default: '', comment: '微信模版消息id' })
wechat_template_id: string;
@Column({ name: 'weapp_template_id', type: 'varchar', length: 255, default: '', comment: '微信小程序订阅消息id' })
weapp_template_id: string;
@Column({ name: 'sms_id', type: 'varchar', length: 255, default: '', comment: '短信id对应短信配置' })
sms_id: string;
@Column({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
create_time: number;
@Column({ name: 'wechat_first', type: 'varchar', length: 255, default: '', comment: '微信头部' })
wechat_first: string;
@Column({ name: 'wechat_remark', type: 'varchar', length: 255, default: '', comment: '微信说明' })
wechat_remark: string;
}

View File

@@ -0,0 +1,104 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { SysNotice } from '../../entities/sys-notice.entity';
import { NoticeNameDto, AddNoticeDto, EditNoticeDto } from '../../dto/NoticeDto';
@Injectable()
export class NoticeAdminService {
constructor(
@InjectRepository(SysNotice)
private readonly noticeRepository: Repository<SysNotice>,
) {}
/**
* 获取通知列表
*/
async getNoticeLists(query: NoticeNameDto) {
const { key, site_id, page = 1, pageSize = 20 } = query;
const skip = (page - 1) * pageSize;
const where = this.buildWhere({ key, site_id });
const [list, total] = await this.noticeRepository.findAndCount({
where,
skip,
take: pageSize,
order: { create_time: 'DESC' },
});
return {
list,
total,
page,
pageSize,
};
}
/**
* 获取通知信息
*/
async getNoticeInfo(id: number) {
return await this.noticeRepository.findOne({ where: { id } });
}
/**
* 添加通知
*/
async addNotice(data: AddNoticeDto) {
const notice = this.noticeRepository.create({
...data,
create_time: this.now(),
});
return await this.noticeRepository.save(notice);
}
/**
* 编辑通知
*/
async editNotice(id: number, data: EditNoticeDto) {
await this.noticeRepository.update(id, data);
return await this.getNoticeInfo(id);
}
/**
* 删除通知
*/
async deleteNotice(id: number) {
return await this.noticeRepository.delete(id);
}
/**
* 根据key获取通知配置
*/
async getNoticeByKey(key: string, site_id: number) {
return await this.noticeRepository.findOne({
where: { key, site_id },
});
}
/**
* 构建查询条件
*/
private buildWhere(params: { key?: string; site_id?: number }) {
const where: any = {};
if (params.key) {
where.key = Like(`%${params.key}%`);
}
if (params.site_id !== undefined) {
where.site_id = params.site_id;
}
return where;
}
/**
* 获取当前时间戳
*/
private now(): number {
return Math.floor(Date.now() / 1000);
}
}

View File

@@ -0,0 +1,99 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { SysNoticeLog } from '../../entities/sys-notice-log.entity';
import { NoticeLogNameDto } from '../../dto/NoticeDto';
@Injectable()
export class NoticeLogAdminService {
constructor(
@InjectRepository(SysNoticeLog)
private readonly noticeLogRepository: Repository<SysNoticeLog>,
) {}
/**
* 获取通知日志列表
*/
async getNoticeLogLists(query: NoticeLogNameDto) {
const { key, notice_type, site_id, page = 1, pageSize = 20 } = query;
const skip = (page - 1) * pageSize;
const where = this.buildWhere({ key, notice_type, site_id });
const [list, total] = await this.noticeLogRepository.findAndCount({
where,
skip,
take: pageSize,
order: { create_time: 'DESC' },
});
return {
list,
total,
page,
pageSize,
};
}
/**
* 获取通知日志信息
*/
async getNoticeLogInfo(id: number) {
return await this.noticeLogRepository.findOne({ where: { id } });
}
/**
* 添加通知日志
*/
async addNoticeLog(data: Partial<SysNoticeLog>) {
const noticeLog = this.noticeLogRepository.create({
...data,
create_time: this.now(),
});
return await this.noticeLogRepository.save(noticeLog);
}
/**
* 更新通知日志
*/
async updateNoticeLog(id: number, data: Partial<SysNoticeLog>) {
await this.noticeLogRepository.update(id, data);
return await this.getNoticeLogInfo(id);
}
/**
* 删除通知日志
*/
async deleteNoticeLog(id: number) {
return await this.noticeLogRepository.delete(id);
}
/**
* 构建查询条件
*/
private buildWhere(params: { key?: string; notice_type?: string; site_id?: number }) {
const where: any = {};
if (params.key) {
where.key = Like(`%${params.key}%`);
}
if (params.notice_type) {
where.notice_type = params.notice_type;
}
if (params.site_id !== undefined) {
where.site_id = params.site_id;
}
return where;
}
/**
* 获取当前时间戳
*/
private now(): number {
return Math.floor(Date.now() / 1000);
}
}

View File

@@ -0,0 +1,130 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { SysNoticeSmsLog } from '../../entities/sys-notice-sms-log.entity';
import { NoticeSmsLogNameDto } from '../../dto/NoticeDto';
@Injectable()
export class NoticeSmsLogAdminService {
constructor(
@InjectRepository(SysNoticeSmsLog)
private readonly noticeSmsLogRepository: Repository<SysNoticeSmsLog>,
) {}
/**
* 获取短信日志列表
*/
async getNoticeSmsLogLists(query: NoticeSmsLogNameDto) {
const { mobile, sms_type, key, status, site_id, page = 1, pageSize = 20 } = query;
const skip = (page - 1) * pageSize;
const where = this.buildWhere({ mobile, sms_type, key, status, site_id });
const [list, total] = await this.noticeSmsLogRepository.findAndCount({
where,
skip,
take: pageSize,
order: { create_time: 'DESC' },
});
return {
list,
total,
page,
pageSize,
};
}
/**
* 获取短信日志信息
*/
async getNoticeSmsLogInfo(id: number) {
return await this.noticeSmsLogRepository.findOne({ where: { id } });
}
/**
* 添加短信日志
*/
async addNoticeSmsLog(data: Partial<SysNoticeSmsLog>) {
const noticeSmsLog = this.noticeSmsLogRepository.create({
...data,
create_time: this.now(),
update_time: this.now(),
});
return await this.noticeSmsLogRepository.save(noticeSmsLog);
}
/**
* 更新短信日志
*/
async updateNoticeSmsLog(id: number, data: Partial<SysNoticeSmsLog>) {
await this.noticeSmsLogRepository.update(id, {
...data,
update_time: this.now(),
});
return await this.getNoticeSmsLogInfo(id);
}
/**
* 删除短信日志
*/
async deleteNoticeSmsLog(id: number) {
return await this.noticeSmsLogRepository.update(id, {
delete_time: this.now(),
});
}
/**
* 更新发送状态
*/
async updateSendStatus(id: number, status: string, result?: string) {
return await this.updateNoticeSmsLog(id, {
status,
result,
send_time: this.now(),
});
}
/**
* 构建查询条件
*/
private buildWhere(params: {
mobile?: string;
sms_type?: string;
key?: string;
status?: string;
site_id?: number
}) {
const where: any = {};
if (params.mobile) {
where.mobile = Like(`%${params.mobile}%`);
}
if (params.sms_type) {
where.sms_type = params.sms_type;
}
if (params.key) {
where.key = params.key;
}
if (params.status) {
where.status = params.status;
}
if (params.site_id !== undefined) {
where.site_id = params.site_id;
}
return where;
}
/**
* 获取当前时间戳
*/
private now(): number {
return Math.floor(Date.now() / 1000);
}
}