feat: add cmpp inbound gateway listener
This commit is contained in:
@@ -8,6 +8,7 @@ function createPrismaMock() {
|
||||
tenantId: 'tenant-1',
|
||||
name: '应用A',
|
||||
status: 'active',
|
||||
cmppAccount: '100001',
|
||||
queuePriority: 'normal',
|
||||
tenant: { id: 'tenant-1', name: '租户A', code: 'TENANT-A' },
|
||||
messageRecords: [{ status: 'delivered' }, { status: 'undelivered' }],
|
||||
@@ -17,6 +18,7 @@ function createPrismaMock() {
|
||||
tenantId: 'tenant-1',
|
||||
name: '应用A',
|
||||
status: 'active',
|
||||
cmppAccount: '100001',
|
||||
queuePriority: 'normal',
|
||||
secretHash: 'secret-hash',
|
||||
tenant: { id: 'tenant-1', name: '租户A', code: 'TENANT-A' },
|
||||
@@ -164,6 +166,7 @@ describe('SmsConfigService', () => {
|
||||
await expect(service.getApplicationCmppParams('app-1')).resolves.toEqual(expect.objectContaining({
|
||||
applicationId: 'app-1',
|
||||
tenantName: '租户A',
|
||||
account: '100001',
|
||||
gatewayHost: '127.0.0.1',
|
||||
gatewayPort: 17890,
|
||||
maxConnections: 2,
|
||||
@@ -172,6 +175,7 @@ describe('SmsConfigService', () => {
|
||||
|
||||
it('creates enterprise applications with persisted queue priority', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsApplication.findUnique.mockResolvedValueOnce(null);
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await expect(service.createApplication({
|
||||
@@ -185,6 +189,7 @@ describe('SmsConfigService', () => {
|
||||
data: expect.objectContaining({
|
||||
tenantId: 'tenant-1',
|
||||
name: '优先应用',
|
||||
cmppAccount: expect.stringMatching(/^\d{6}$/),
|
||||
queuePriority: 'priority',
|
||||
ipAllowlist: { create: [{ ipCidr: '10.0.0.1/32' }] },
|
||||
}),
|
||||
@@ -195,11 +200,11 @@ describe('SmsConfigService', () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
expect(() => service.createApplication({
|
||||
await expect(service.createApplication({
|
||||
tenantId: 'tenant-1',
|
||||
name: '异常应用',
|
||||
queuePriority: 'urgent',
|
||||
})).toThrow('queuePriority must be normal or priority');
|
||||
})).rejects.toThrow('queuePriority must be normal or priority');
|
||||
|
||||
expect(prisma.smsApplication.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { randomBytes, createHash } from 'node:crypto';
|
||||
import { randomBytes, randomInt, createHash } from 'node:crypto';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
export interface CreateSmsApplicationDto {
|
||||
@@ -150,15 +150,17 @@ export class SmsConfigService {
|
||||
return application;
|
||||
}
|
||||
|
||||
createApplication(data: CreateSmsApplicationDto) {
|
||||
async createApplication(data: CreateSmsApplicationDto) {
|
||||
const secret = randomBytes(24).toString('hex');
|
||||
const queuePriority = normalizeApplicationQueuePriority(data.queuePriority);
|
||||
const cmppAccount = await this.generateCmppAccount();
|
||||
return this.prisma.smsApplication.create({
|
||||
data: {
|
||||
tenantId: data.tenantId,
|
||||
name: data.name,
|
||||
scene: data.scene,
|
||||
callbackUrl: data.callbackUrl,
|
||||
cmppAccount,
|
||||
secretHash: hashSecret(secret),
|
||||
dailyLimit: data.dailyLimit,
|
||||
customerUnitPrice: data.customerUnitPrice ?? 0,
|
||||
@@ -345,8 +347,8 @@ export class SmsConfigService {
|
||||
gatewayHost: channel?.gatewayHost ?? '',
|
||||
gatewayPort: channel?.gatewayPort ?? 0,
|
||||
enterpriseCode: channel?.enterpriseCode ?? application.tenant.code,
|
||||
account: channel?.account ?? application.tenant.code,
|
||||
passwordCipher: channel?.passwordCipher ?? application.secretHash,
|
||||
account: application.cmppAccount,
|
||||
passwordCipher: application.secretHash,
|
||||
srcId: channel?.srcId ?? '',
|
||||
maxConnections: channel?.config && typeof channel.config === 'object' && 'maxConnections' in channel.config ? Number(channel.config.maxConnections) : 1,
|
||||
heartbeatSeconds: 30,
|
||||
@@ -355,6 +357,17 @@ export class SmsConfigService {
|
||||
};
|
||||
}
|
||||
|
||||
private async generateCmppAccount() {
|
||||
for (let attempt = 0; attempt < 20; attempt += 1) {
|
||||
const cmppAccount = String(randomInt(100000, 1000000));
|
||||
const exists = await this.prisma.smsApplication.findUnique({ where: { cmppAccount } });
|
||||
if (!exists) {
|
||||
return cmppAccount;
|
||||
}
|
||||
}
|
||||
throw new BadRequestException('Unable to generate unique CMPP account');
|
||||
}
|
||||
|
||||
async disconnectApplicationConnection(applicationId: string, connectionId: string, data: StatusChangeDto = { status: 'disconnected' }) {
|
||||
const application = await this.prisma.smsApplication.findUnique({ where: { id: applicationId } });
|
||||
if (!application) {
|
||||
|
||||
Reference in New Issue
Block a user