fix: harden real backend admin workflows and ui
This commit is contained in:
@@ -37,6 +37,11 @@ export class AdminSendChainController {
|
||||
return this.sendChain.enqueueBatchTask(taskId);
|
||||
}
|
||||
|
||||
@Post('batch-tasks/:id/terminate')
|
||||
terminateTask(@Param('id') taskId: string) {
|
||||
return this.sendChain.terminateBatchTask(taskId);
|
||||
}
|
||||
|
||||
@Post('scheduled/dispatch-due')
|
||||
dispatchDueScheduledTasks() {
|
||||
return this.sendChain.dispatchDueScheduledTasks();
|
||||
|
||||
@@ -244,6 +244,23 @@ describe('SendChainService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('terminates non-final tasks by canceling unsubmitted messages', async () => {
|
||||
const { service, prisma } = createService();
|
||||
prisma.smsBatchTask.findUnique.mockResolvedValue({ id: 'task-1', status: 'sending' });
|
||||
service['refreshTaskProgress'] = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
await service.terminateBatchTask('task-1');
|
||||
|
||||
expect(prisma.smsMessageRecord.updateMany).toHaveBeenCalledWith({
|
||||
where: { batchTaskId: 'task-1', status: { in: ['ready', 'queued', 'scheduled', 'submit_queued'] } },
|
||||
data: { status: 'canceled', errorMessage: '运营终止任务,未提交号码停止发送' },
|
||||
});
|
||||
expect(prisma.smsBatchTask.update).toHaveBeenCalledWith({
|
||||
where: { id: 'task-1' },
|
||||
data: { status: 'canceled', canceledAt: expect.any(Date), rejectReason: '运营终止任务' },
|
||||
});
|
||||
});
|
||||
|
||||
it('blocks sending when enterprise certification is not approved', async () => {
|
||||
const { service, prisma } = createService();
|
||||
prisma.tenant.findUnique.mockResolvedValue({ id: 'tenant-1', status: 'active', certificationStatus: 'rejected' });
|
||||
|
||||
@@ -237,7 +237,13 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
listBatchTasks(tenantId?: string, status?: string) {
|
||||
return this.prisma.smsBatchTask.findMany({
|
||||
where: { tenantId, status },
|
||||
include: { apiRequests: true },
|
||||
include: {
|
||||
tenant: true,
|
||||
application: true,
|
||||
template: true,
|
||||
apiRequests: true,
|
||||
messages: { include: { channel: true }, orderBy: { queuedAt: 'asc' }, take: 100000 },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 100,
|
||||
});
|
||||
@@ -388,6 +394,25 @@ export class SendChainService implements OnModuleInit, OnModuleDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
async terminateBatchTask(taskId: string) {
|
||||
const task = await this.prisma.smsBatchTask.findUnique({ where: { id: taskId } });
|
||||
if (!task) {
|
||||
throw new NotFoundException('SMS batch task not found');
|
||||
}
|
||||
if (['finished', 'completed', 'failed', 'canceled', 'rejected'].includes(task.status)) {
|
||||
throw new BadRequestException('SMS batch task is already final');
|
||||
}
|
||||
await this.prisma.smsMessageRecord.updateMany({
|
||||
where: { batchTaskId: taskId, status: { in: ['ready', 'queued', 'scheduled', 'submit_queued'] } },
|
||||
data: { status: 'canceled', errorMessage: '运营终止任务,未提交号码停止发送' },
|
||||
});
|
||||
await this.refreshTaskProgress(taskId);
|
||||
return this.prisma.smsBatchTask.update({
|
||||
where: { id: taskId },
|
||||
data: { status: 'canceled', canceledAt: new Date(), rejectReason: '运营终止任务' },
|
||||
});
|
||||
}
|
||||
|
||||
async dispatchDueScheduledTasks(now = new Date()) {
|
||||
const tasks = await this.prisma.smsBatchTask.findMany({
|
||||
where: { status: 'scheduled', scheduledAt: { lte: now } },
|
||||
|
||||
Reference in New Issue
Block a user