feat: allow deleting notification projects with history
This commit is contained in:
parent
c7fd17df35
commit
d74959da1f
@ -743,7 +743,7 @@ test('admin API 提供稳定 contract,rotate-secret/test/deliveries/audits 可
|
||||
headers: withAdminAuth(),
|
||||
})
|
||||
assert.equal(deleteResponse.status, 200)
|
||||
assert.deepEqual(await deleteResponse.json(), { projectCode: 'temp_unused', deleted: true })
|
||||
assert.deepEqual(await deleteResponse.json(), { projectCode: 'temp_unused', deleted: true, deliveryCount: 0 })
|
||||
|
||||
const oversizedResponse = await fetch(`http://127.0.0.1:${port}/admin/projects`, {
|
||||
method: 'POST',
|
||||
@ -769,3 +769,95 @@ test('admin API 提供稳定 contract,rotate-secret/test/deliveries/audits 可
|
||||
await prisma.$disconnect()
|
||||
}
|
||||
})
|
||||
|
||||
test('已有投递记录的项目也可删除,项目配置与 recipients 删除但历史记录保留', async () => {
|
||||
const oldEncryptionKey = process.env.YYD_NOTIFICATION_CONFIG_ENCRYPTION_KEY
|
||||
const oldAdminApiSecret = process.env.YYD_NOTIFICATION_ADMIN_API_SECRET
|
||||
const oldAdminToken = process.env.YYD_NOTIFICATION_ADMIN_TOKEN
|
||||
const oldProjectsJson = process.env.YYD_NOTIFICATION_PROJECTS_JSON
|
||||
const oldPublicBaseUrl = process.env.YYD_NOTIFICATION_PUBLIC_BASE_URL
|
||||
process.env.YYD_NOTIFICATION_CONFIG_ENCRYPTION_KEY = encryptionKey
|
||||
process.env.YYD_NOTIFICATION_ADMIN_API_SECRET = adminApiSecret
|
||||
process.env.YYD_NOTIFICATION_ADMIN_TOKEN = ''
|
||||
process.env.YYD_NOTIFICATION_PROJECTS_JSON = ''
|
||||
process.env.YYD_NOTIFICATION_PUBLIC_BASE_URL = 'https://sc.example.com/notification-center'
|
||||
|
||||
await resetDatabase()
|
||||
const registry = new AdminProjectRegistry()
|
||||
await registry.refresh()
|
||||
const server = createServer(registry, { resolveRecipientAlias }).listen(0)
|
||||
await new Promise<void>((resolve) => server.once('listening', resolve))
|
||||
const port = (server.address() as AddressInfo).port
|
||||
|
||||
try {
|
||||
const createResponse = await fetch(`http://127.0.0.1:${port}/admin/projects`, {
|
||||
method: 'POST',
|
||||
headers: withAdminAuth(),
|
||||
body: JSON.stringify({
|
||||
projectCode: 'delete_with_history',
|
||||
name: '删除保留历史',
|
||||
recipients: [{ alias: '海棠 新', userId: 'ding-ops-user-2' }],
|
||||
}),
|
||||
})
|
||||
assert.equal(createResponse.status, 201)
|
||||
|
||||
await prisma.notificationDelivery.create({
|
||||
data: {
|
||||
id: 'delivery-keep-after-project-delete',
|
||||
projectCode: 'delete_with_history',
|
||||
idempotencyKey: 'delete_with_history:1',
|
||||
requestHash: 'request-hash',
|
||||
recipientUserId: 'ding-ops-user-2',
|
||||
businessTitle: '历史任务',
|
||||
renderedTitle: '【删除保留历史】历史任务',
|
||||
content: '历史正文',
|
||||
actionTitle: '查看',
|
||||
actionUrl: '',
|
||||
status: 'failed',
|
||||
lastErrorCode: 'INVALID_USER',
|
||||
lastErrorMessage: '历史失败保留',
|
||||
},
|
||||
})
|
||||
|
||||
const deleteResponse = await fetch(`http://127.0.0.1:${port}/admin/projects/delete_with_history`, {
|
||||
method: 'DELETE',
|
||||
headers: withAdminAuth(),
|
||||
})
|
||||
assert.equal(deleteResponse.status, 200)
|
||||
assert.deepEqual(await deleteResponse.json(), {
|
||||
projectCode: 'delete_with_history',
|
||||
deleted: true,
|
||||
deliveryCount: 1,
|
||||
})
|
||||
|
||||
assert.equal(await prisma.notificationProject.findUnique({ where: { code: 'delete_with_history' } }), null)
|
||||
assert.equal(await prisma.notificationProjectRecipient.count({ where: { projectId: { not: '' } } }), 0)
|
||||
|
||||
const delivery = await prisma.notificationDelivery.findUniqueOrThrow({
|
||||
where: { id: 'delivery-keep-after-project-delete' },
|
||||
})
|
||||
assert.equal(delivery.projectCode, 'delete_with_history')
|
||||
assert.equal(delivery.businessTitle, '历史任务')
|
||||
|
||||
const audit = await prisma.notificationAudit.findFirstOrThrow({
|
||||
where: {
|
||||
action: 'project.delete',
|
||||
targetProjectCode: 'delete_with_history',
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
assert.equal(audit.summary, '删除项目 delete_with_history')
|
||||
assert.deepEqual(JSON.parse(audit.detailsJson || '{}'), {
|
||||
recipientCount: 1,
|
||||
deliveryCount: 1,
|
||||
})
|
||||
} finally {
|
||||
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()))
|
||||
process.env.YYD_NOTIFICATION_CONFIG_ENCRYPTION_KEY = oldEncryptionKey
|
||||
process.env.YYD_NOTIFICATION_ADMIN_API_SECRET = oldAdminApiSecret
|
||||
process.env.YYD_NOTIFICATION_ADMIN_TOKEN = oldAdminToken
|
||||
process.env.YYD_NOTIFICATION_PROJECTS_JSON = oldProjectsJson
|
||||
process.env.YYD_NOTIFICATION_PUBLIC_BASE_URL = oldPublicBaseUrl
|
||||
await prisma.$disconnect()
|
||||
}
|
||||
})
|
||||
|
||||
@ -810,11 +810,6 @@ export class AdminProjectRegistry {
|
||||
async deleteProject(projectCode: string, actor: AdminAuditActor) {
|
||||
const current = await this.loadExistingProjectForMutation(projectCode)
|
||||
const deliveryCount = await prisma.notificationDelivery.count({ where: { projectCode } })
|
||||
if (deliveryCount > 0) {
|
||||
throw new AdminApiError(409, 'PROJECT_HAS_DELIVERIES', `项目 ${projectCode} 已存在投递记录,请先停用而不是删除`, {
|
||||
deliveryCount,
|
||||
})
|
||||
}
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.notificationProject.delete({ where: { code: projectCode } })
|
||||
await tx.notificationAudit.create({
|
||||
@ -827,12 +822,15 @@ export class AdminProjectRegistry {
|
||||
requestId: actor.requestId || null,
|
||||
ipAddress: actor.ipAddress || null,
|
||||
summary: `删除项目 ${projectCode}`,
|
||||
detailsJson: createAuditDetailsJson({ recipientCount: current.recipients.length }),
|
||||
detailsJson: createAuditDetailsJson({
|
||||
recipientCount: current.recipients.length,
|
||||
deliveryCount,
|
||||
}),
|
||||
},
|
||||
})
|
||||
})
|
||||
await this.refresh()
|
||||
return { projectCode, deleted: true }
|
||||
return { projectCode, deleted: true, deliveryCount }
|
||||
}
|
||||
|
||||
async importFromEnvironment(actor: AdminAuditActor) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user