104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
||
import { redactSensitive } from '../../common/security/redact-sensitive';
|
||
import { DeployRunSummary } from '../../deploy-runs/deploy-run.types';
|
||
import { WeComClient } from './wecom.client';
|
||
|
||
export type DeployNotificationEvent =
|
||
| 'created'
|
||
| 'queued'
|
||
| 'succeeded'
|
||
| 'failed'
|
||
| 'canceled'
|
||
| 'retried';
|
||
|
||
export type DeployNotificationResult = {
|
||
status: 'sent' | 'skipped' | 'failed';
|
||
channel: 'wecom';
|
||
reason?: string;
|
||
};
|
||
|
||
/**
|
||
* 统一封装发布单企微通知,保证 webhook 只存在于后端配置边界内。
|
||
*/
|
||
@Injectable()
|
||
export class DeployNotificationService {
|
||
constructor(private readonly weComClient: WeComClient) {}
|
||
|
||
async sendDeployRunEvent(
|
||
run: DeployRunSummary,
|
||
event: DeployNotificationEvent,
|
||
): Promise<DeployNotificationResult> {
|
||
const health = this.weComClient.healthSummary();
|
||
|
||
if (health.status === 'not_configured') {
|
||
return {
|
||
status: 'skipped',
|
||
channel: 'wecom',
|
||
reason: health.message ?? 'WeCom webhook is not configured',
|
||
};
|
||
}
|
||
|
||
try {
|
||
await this.weComClient.sendMarkdown({
|
||
title: this.titleFor(event),
|
||
markdown: this.markdownFor(run, event),
|
||
});
|
||
|
||
return { status: 'sent', channel: 'wecom' };
|
||
} catch (error) {
|
||
return {
|
||
status: 'failed',
|
||
channel: 'wecom',
|
||
reason:
|
||
error instanceof Error
|
||
? redactSensitive({ message: error.message }).message
|
||
: 'Unknown WeCom notification error',
|
||
};
|
||
}
|
||
}
|
||
|
||
private titleFor(event: DeployNotificationEvent): string {
|
||
if (event === 'canceled') {
|
||
return 'DevOps 发布已取消';
|
||
}
|
||
|
||
if (event === 'queued') {
|
||
return 'DevOps 发布已进入 Jenkins 队列';
|
||
}
|
||
|
||
if (event === 'succeeded') {
|
||
return 'DevOps 发布成功';
|
||
}
|
||
|
||
if (event === 'failed') {
|
||
return 'DevOps 发布失败';
|
||
}
|
||
|
||
if (event === 'retried') {
|
||
return 'DevOps 发布已重试';
|
||
}
|
||
|
||
return 'DevOps 发布已创建';
|
||
}
|
||
|
||
private markdownFor(
|
||
run: DeployRunSummary,
|
||
event: DeployNotificationEvent,
|
||
): string {
|
||
return [
|
||
`> 事件:${this.titleFor(event)}`,
|
||
`> 项目:${run.projectKey}`,
|
||
`> 环境:${run.environment}`,
|
||
`> Ref:${run.ref}`,
|
||
`> 状态:${run.status}`,
|
||
`> 操作者:${run.operator}`,
|
||
`> Run ID:${run.id}`,
|
||
run.jenkinsQueueId ? `> Jenkins Queue:${run.jenkinsQueueId}` : undefined,
|
||
run.jenkinsBuildNumber ? `> Jenkins Build:#${run.jenkinsBuildNumber}` : undefined,
|
||
run.failureSummary ? `> 失败摘要:${run.failureSummary}` : undefined,
|
||
]
|
||
.filter(Boolean)
|
||
.join('\n');
|
||
}
|
||
}
|