feat: 初始化DevOps平台后端

This commit is contained in:
湛兮
2026-06-11 20:49:59 +08:00
commit caec5a618d
88 changed files with 11893 additions and 0 deletions
@@ -0,0 +1,103 @@
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');
}
}