feat: add HTTP API and complete client workflows

This commit is contained in:
hectorzhao
2026-07-16 11:34:06 +08:00
parent 4f07b331e5
commit dcb6162dcf
40 changed files with 2548 additions and 365 deletions
+178
View File
@@ -45,6 +45,8 @@ model Tenant {
cmppConnectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
openApiRequests OpenApiRequest[]
httpWebhookEvents HttpWebhookEvent[]
}
model EnterpriseCertification {
@@ -391,6 +393,12 @@ model SmsApplication {
connectionStates CmppConnectionState[]
gatewayDownstreamRecoveryStatuses GatewayDownstreamRecoveryStatus[]
gatewaySubmitDeadLetters GatewaySubmitDeadLetter[]
httpConfig SmsApplicationHttpConfig?
httpIpAllowlist SmsApplicationHttpIpAllowlist[]
httpApiCredentials HttpApiCredential[]
openApiRequests OpenApiRequest[]
httpWebhookEndpoints HttpWebhookEndpoint[]
httpWebhookEvents HttpWebhookEvent[]
@@index([tenantId, status])
}
@@ -407,6 +415,174 @@ model SmsApplicationIpAllowlist {
@@unique([applicationId, ipCidr])
}
model SmsApplicationHttpConfig {
id String @id @default(cuid())
applicationId String @unique
enabled Boolean @default(false)
sendEnabled Boolean @default(false)
messageQueryEnabled Boolean @default(false)
receiptWebhookEnabled Boolean @default(false)
uplinkWebhookEnabled Boolean @default(false)
uplinkQueryEnabled Boolean @default(false)
credentialSelfServiceEnabled Boolean @default(false)
qpsLimit Int @default(10)
timestampToleranceSeconds Int @default(300)
maxCredentialCount Int @default(2)
uplinkRetentionDays Int @default(90)
maxQueryRangeDays Int @default(31)
maxPageSize Int @default(100)
receiptDeliveryMode String @default("cmpp")
uplinkDeliveryMode String @default("cmpp")
webhookRetryEnabled Boolean @default(true)
webhookMaxAttempts Int @default(7)
webhookTimeoutSeconds Int @default(10)
requireHttps Boolean @default(true)
allowClientManualRetry Boolean @default(true)
allowClientTest Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
}
model SmsApplicationHttpIpAllowlist {
id String @id @default(cuid())
applicationId String
ipCidr String
remark String?
createdAt DateTime @default(now())
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
@@unique([applicationId, ipCidr])
}
model HttpApiCredential {
id String @id @default(cuid())
applicationId String
name String
accessKey String @unique
secretEncrypted String
secretLast4 String
status String @default("active")
expiresAt DateTime?
lastUsedAt DateTime?
lastUsedIp String?
createdById String?
revokedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
requests OpenApiRequest[]
@@index([applicationId, status])
}
model OpenApiRequest {
id String @id @default(cuid())
tenantId String
applicationId String
credentialId String
requestId String @unique
idempotencyKey String
bodyHash String
clientMessageId String?
messageRecordId String?
httpStatus Int?
businessCode String?
responseBody Json?
sourceIp String?
userAgent String?
durationMs Int?
status String @default("processing")
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
credential HttpApiCredential @relation(fields: [credentialId], references: [id])
@@unique([applicationId, idempotencyKey])
@@index([applicationId, createdAt])
}
model HttpWebhookEndpoint {
id String @id @default(cuid())
applicationId String
eventType String
url String
secretEncrypted String
secretLast4 String
status String @default("active")
lastTestAt DateTime?
lastTestStatus String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
deliveries HttpWebhookDelivery[]
@@unique([applicationId, eventType])
}
model HttpWebhookEvent {
id String @id @default(cuid())
eventId String @unique
tenantId String
applicationId String
eventType String
messageRecordId String?
messageId String?
uplinkMessageId String?
payload Json
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
application SmsApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
deliveries HttpWebhookDelivery[]
@@index([applicationId, createdAt])
}
model HttpWebhookDelivery {
id String @id @default(cuid())
eventId String
endpointId String
status String @default("pending")
attemptCount Int @default(0)
nextRetryAt DateTime?
lastHttpStatus Int?
lastError String?
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event HttpWebhookEvent @relation(fields: [eventId], references: [id], onDelete: Cascade)
endpoint HttpWebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
attempts HttpWebhookAttempt[]
@@unique([eventId, endpointId])
@@index([status, nextRetryAt])
}
model HttpWebhookAttempt {
id String @id @default(cuid())
deliveryId String
attemptNo Int
requestHeaders Json?
responseStatus Int?
responseSummary String?
errorMessage String?
durationMs Int?
createdAt DateTime @default(now())
delivery HttpWebhookDelivery @relation(fields: [deliveryId], references: [id], onDelete: Cascade)
@@unique([deliveryId, attemptNo])
}
model SmsSignature {
id String @id @default(cuid())
tenantId String
@@ -1135,6 +1311,7 @@ model SmsMessageRecord {
drainageInfoId String?
reviewTaskId String?
messageId String @unique
clientMessageId String?
phoneNumber String
carrier String?
province String?
@@ -1181,6 +1358,7 @@ model SmsMessageRecord {
@@index([phoneNumber])
@@index([gatewayMessageId])
@@index([drainageInfoId, queuedAt])
@@unique([applicationId, clientMessageId])
}
model CmppSubmitSession {