feat: harden platform workflows and UI governance

This commit is contained in:
hectorzhao
2026-07-22 14:14:55 +08:00
parent ef957f7daa
commit 0f223f7f91
80 changed files with 4958 additions and 764 deletions
+40 -4
View File
@@ -729,7 +729,13 @@ export class SmsConfigService {
const timeoutMs = getPositiveIntegerEnv('CMPP_DOWNSTREAM_HEARTBEAT_TIMEOUT_MS', DEFAULT_DOWNSTREAM_HEARTBEAT_TIMEOUT_MS);
const cutoff = new Date(now.getTime() - timeoutMs);
return this.prisma.cmppDownstreamConnection.deleteMany({
where: { status: 'connected', lastHeartbeatAt: { lt: cutoff } },
where: {
status: 'connected',
OR: [
{ lastHeartbeatAt: { lt: cutoff } },
{ lastHeartbeatAt: null, connectedAt: { lt: cutoff } },
],
},
});
}
@@ -1005,14 +1011,19 @@ export class SmsConfigService {
const drainageInfo = data.drainageInfo
? await this.withReportRequirementSnapshot(applicationId, data.drainageInfo)
: undefined;
const materialChanged = (data.applicationId !== undefined && data.applicationId !== signature.applicationId)
|| (data.name !== undefined && normalizeSmsSignature(data.name) !== normalizeSmsSignature(signature.name))
|| (data.purpose !== undefined && data.purpose !== signature.purpose)
|| (data.drainageInfo !== undefined && JSON.stringify(data.drainageInfo) !== JSON.stringify(signature.drainageInfo ?? null));
const auditStatus = materialChanged && signature.auditStatus === 'approved' ? 'pending' : data.auditStatus;
const updated = await this.prisma.smsSignature.update({
where: { id: signatureId },
data: {
applicationId: data.applicationId,
name: data.name,
purpose: data.purpose,
auditStatus: data.auditStatus,
rejectReason: data.auditStatus === 'pending' ? null : undefined,
auditStatus,
rejectReason: auditStatus === 'pending' ? null : undefined,
drainageInfo: drainageInfo as Prisma.InputJsonValue | undefined,
materialVersion: { increment: 1 },
pendingReport: true,
@@ -1376,6 +1387,12 @@ export class SmsConfigService {
const variables = data.content !== undefined || data.variables !== undefined
? validateAndNormalizeTemplateVariables(data.content ?? template.content, data.variables)
: undefined;
const materialChanged = (data.applicationId !== undefined && data.applicationId !== template.applicationId)
|| (data.signatureId !== undefined && data.signatureId !== template.signatureId)
|| (data.content !== undefined && data.content !== template.content)
|| (data.category !== undefined && data.category !== template.category)
|| data.variables !== undefined;
const auditStatus = materialChanged && template.auditStatus === 'approved' ? 'pending' : data.auditStatus;
return this.prisma.$transaction(async (tx) => {
if (variables) {
await tx.templateVariable.deleteMany({ where: { templateId } });
@@ -1388,7 +1405,8 @@ export class SmsConfigService {
name: data.name,
content: data.content,
category: data.category,
auditStatus: data.auditStatus,
auditStatus,
rejectReason: auditStatus === 'pending' ? null : undefined,
billingUnits: data.content ? estimateBillingUnits(data.content) : undefined,
variables: variables ? {
create: variables.map((variable) => ({
@@ -1403,6 +1421,24 @@ export class SmsConfigService {
});
}
async updateClientTemplate(templateId: string, data: UpdateSmsTemplateDto, tenantId?: string) {
const current = await this.prisma.smsTemplate.findUnique({ where: { id: templateId } });
if (!current || (tenantId && current.tenantId !== tenantId)) throw new NotFoundException('Template not found');
if (!['draft', 'rejected', 'approved'].includes(current.auditStatus)) {
throw new BadRequestException('当前审核状态不允许修改模板');
}
const updated = await this.updateTemplate(templateId, { ...data, auditStatus: 'pending' }, tenantId);
await this.createAuditRecord({
tenantId: current.tenantId,
targetType: 'sms_template',
targetId: templateId,
action: 'client_update_submit',
statusBefore: current.auditStatus,
statusAfter: 'pending',
});
return updated;
}
async submitTemplate(templateId: string, tenantId?: string) {
const template = await this.prisma.smsTemplate.findUnique({ where: { id: templateId } });
if (!template || (tenantId && template.tenantId !== tenantId)) {