pipeline-template-module

流水线模板(Pipeline Template)是 BK-CI 的核心功能模块之一,允许用户将流水线配置抽象为可复用的模板,支持模板的创建、版本管理、实例化、权限控制以及与研发商店的集成。

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "pipeline-template-module" with this command: npx skills add tencentblueking/bk-ci/tencentblueking-bk-ci-pipeline-template-module

流水线模板模块架构指南

概述

流水线模板(Pipeline Template)是 BK-CI 的核心功能模块之一,允许用户将流水线配置抽象为可复用的模板,支持模板的创建、版本管理、实例化、权限控制以及与研发商店的集成。

核心价值

  • 配置复用:将通用的流水线配置抽象为模板,避免重复配置

  • 标准化管理:通过模板统一管理流水线标准,确保一致性

  • 版本控制:支持模板版本管理,可追溯历史变更

  • 批量操作:支持批量实例化和更新,提高效率

  • 商店集成:支持模板发布到研发商店,实现跨项目共享

系统架构

┌─────────────────────────────────────────────────────────────────────────┐ │ 流水线模板模块 │ ├─────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ API 接口层 │ │ 业务服务层 │ │ 数据访问层 │ │ │ │ (V1 + V2) │ │ (Service) │ │ (DAO) │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 数据库表 │ │ │ │ T_TEMPLATE | T_TEMPLATE_PIPELINE | T_PIPELINE_TEMPLATE_INFO │ │ │ │ T_PIPELINE_TEMPLATE_RESOURCE_VERSION | T_TEMPLATE_INSTANCE_* │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 权限控制 │ │ 研发商店集成 │ │ 事件驱动 │ │ │ │ (RBAC) │ │ (Store) │ │ (MQ) │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘

一、版本架构

系统存在两个版本的实现,V2 版本是新架构,功能更完善:

版本 特点 适用场景

V1 原始实现,功能基础 兼容老版本,简单场景

V2 新架构,支持草稿、PAC、完善的版本管理 新功能开发,复杂场景

模板类型

// 文件: process/api-process/.../pojo/template/TemplateType.kt enum class TemplateType(val value: String) { CUSTOMIZE("customize"), // 自定义模板 - 项目内创建 CONSTRAINT("constraint"), // 约束模板 - 来自研发商店 PUBLIC("public") // 公共模板 - 系统级公共模板 }

二、目录结构

2.1 API 接口层

src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/ ├── api/template/ # V1 版本 API │ ├── UserPTemplateResource.kt # 用户模板接口 │ ├── UserTemplateInstanceResource.kt # 模板实例化接口 │ ├── UserPipelineTemplateResource.kt # 流水线模板接口 │ ├── UserTemplateAtomResource.kt # 模板插件接口 │ ├── ServicePTemplateResource.kt # 服务间调用接口 │ ├── ServiceTemplateInstanceResource.kt # 服务间实例接口 │ └── v2/ # V2 版本 API │ ├── UserPipelineTemplateV2Resource.kt # V2 用户模板接口 │ ├── UserPipelineTemplateInstanceV2Resource.kt # V2 实例化接口 │ ├── ServicePipelineTemplateV2Resource.kt # V2 服务间接口 │ ├── OpPipelineTemplateResource.kt # V2 运营管理接口 │ └── UserTemplateAtomV2Resource.kt # V2 插件检查接口 ├── api/op/ │ └── OpPipelineTemplateResource.kt # 运营管理模板接口 ├── api/builds/ │ └── BuildTemplateAcrossResource.kt # 跨项目模板访问 └── api/service/ └── ServiceTemplateAcrossResource.kt # 服务间跨项目模板

2.2 POJO 数据模型

src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/template/ ├── TemplateType.kt # 模板类型枚举 ├── TemplateModel.kt # 模板模型 ├── TemplateModelDetail.kt # 模板模型详情 ├── TemplateVersion.kt # 模板版本信息 ├── TemplateId.kt # 模板ID ├── TemplateInstanceCreate.kt # 实例创建请求 ├── TemplateInstanceUpdate.kt # 实例更新请求 ├── TemplateInstanceParams.kt # 实例参数 ├── TemplatePipeline.kt # 模板流水线 ├── TemplateWithPermission.kt # 带权限的模板 ├── TemplateCompareModel.kt # 模板对比模型 ├── CopyTemplateReq.kt # 复制模板请求 ├── SaveAsTemplateReq.kt # 另存为模板请求 ├── MarketTemplateRequest.kt # 商店模板请求 └── v2/ # V2 版本 POJO ├── PipelineTemplateInfoV2.kt # V2 模板基础信息 ├── PipelineTemplateResource.kt # V2 模板资源(版本) ├── PipelineTemplateDetailsResponse.kt # V2 模板详情响应 ├── PipelineTemplateVersionInfo.kt # V2 版本信息 ├── PipelineTemplateCustomCreateReq.kt # V2 自定义创建请求 ├── PipelineTemplateCopyCreateReq.kt # V2 复制创建请求 ├── PipelineTemplateMarketCreateReq.kt # V2 商店导入请求 ├── PipelineTemplateDraftSaveReq.kt # V2 草稿保存请求 ├── PipelineTemplateDraftReleaseReq.kt # V2 草稿发布请求 ├── PipelineTemplateInstancesRequest.kt # V2 批量实例化请求 ├── PipelineTemplateCompareResponse.kt # V2 版本对比响应 └── TemplateInstanceType.kt # 实例类型枚举

2.3 业务服务层

src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/ ├── TemplateFacadeService.kt # V1 核心门面服务 (120KB) ├── TemplateCommonService.kt # 模板通用服务 ├── TemplateSettingService.kt # 模板设置服务 ├── TemplateAtomService.kt # 模板插件服务 ├── TemplatePACService.kt # 模板 PAC 服务 └── v2/ # V2 版本服务 ├── PipelineTemplateFacadeService.kt # V2 核心门面服务 (71KB) ├── PipelineTemplateInstanceService.kt # V2 实例化服务 (41KB) ├── PipelineTemplatePersistenceService.kt # V2 持久化服务 (38KB) ├── PipelineTemplateMigrateService.kt # V2 迁移服务 (38KB) ├── PipelineTemplateGenerator.kt # 模板生成器 ├── PipelineTemplateMarketFacadeService.kt # 商店模板服务 ├── PipelineTemplateInfoService.kt # 模板信息服务 ├── PipelineTemplateResourceService.kt # 模板资源服务 ├── PipelineTemplateRelatedService.kt # 模板关联服务 ├── PipelineTemplateSettingService.kt # V2 模板设置服务 ├── PipelineTemplateCommonService.kt # V2 通用服务 ├── PipelineTemplateAtomService.kt # V2 插件服务 ├── PipelineTemplateVersionValidator.kt # 版本校验器 ├── PipelineTemplateModelInitializer.kt # 模型初始化器 ├── PipelineTemplateInstanceListener.kt # 实例化事件监听器 └── version/ # 版本管理 ├── PipelineTemplateVersionManager.kt # 版本管理器 ├── PipelineTemplateVersionCreateContext.kt # 版本创建上下文 ├── PipelineTemplateVersionDeleteContext.kt # 版本删除上下文 ├── convert/ # 请求转换器 │ ├── PipelineTemplateCustomCreateReqConverter.kt │ ├── PipelineTemplateCopyCreateReqConverter.kt │ ├── PipelineTemplateMarketCreateReqConverter.kt │ ├── PipelineTemplateDraftSaveReqConverter.kt │ ├── PipelineTemplateDraftReleaseReqConverter.kt │ └── PipelineTemplateDraftRollbackReqConverter.kt ├── hander/ # 版本处理器 │ ├── PipelineTemplateVersionCreateHandler.kt │ ├── PipelineTemplateDraftSaveHandler.kt │ ├── PipelineTemplateDraftReleaseHandler.kt │ ├── PipelineTemplateReleaseCreateHandler.kt │ ├── PipelineTemplateBranchCreateHandler.kt │ └── PipelineTemplateVersionDeleteHandler.kt └── processor/ # 版本后处理器 ├── PTemplateVersionCreatePostProcessor.kt ├── PTemplateVersionDeletePostProcessor.kt ├── PTemplateCompatibilityVersionPostProcessor.kt ├── PTemplateMarketInstallVersionPostProcessor.kt └── PTemplateOperationLogVersionPostProcessor.kt

2.4 数据访问层

src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/ ├── engine/dao/template/ # V1 版本 DAO │ ├── TemplateDao.kt # 核心模板 DAO (25KB) │ └── TemplatePipelineDao.kt # 模板流水线 DAO (16KB) └── dao/template/ # V2 版本 DAO ├── PipelineTemplateInfoDao.kt # V2 模板信息 DAO (16KB) ├── PipelineTemplateResourceDao.kt # V2 模板资源 DAO (23KB) ├── PipelineTemplateSettingDao.kt # V2 模板设置 DAO (13KB) ├── PipelineTemplateRelatedDao.kt # V2 模板关联 DAO (15KB) └── PipelineTemplateMigrationDao.kt # V2 迁移 DAO

2.5 权限控制

src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/template/ ├── PipelineTemplatePermissionService.kt # 权限服务接口 ├── AbstractPipelineTemplatePermissionService.kt # 抽象实现 ├── RbacPipelineTemplatePermissionService.kt # RBAC 权限实现 ├── MockPipelineTemplatePermissionService.kt # Mock 实现 └── config/ └── PipelineTemplatePermConfiguration.kt # 权限配置

三、数据库表结构

3.1 V1 版本表

T_TEMPLATE - 模板信息表

CREATE TABLE IF NOT EXISTS T_TEMPLATE ( VERSION bigint(20) NOT NULL AUTO_INCREMENT COMMENT '版本号', ID varchar(32) NOT NULL COMMENT '模板ID', TEMPLATE_NAME varchar(64) NOT NULL COMMENT '模板名称', PROJECT_ID varchar(34) NOT NULL COMMENT '项目ID', VERSION_NAME varchar(64) NOT NULL COMMENT '版本名称', CREATOR varchar(64) NOT NULL COMMENT '创建者', CREATED_TIME datetime(3) DEFAULT NULL COMMENT '创建时间', TEMPLATE mediumtext COMMENT '模板内容(JSON)', TYPE varchar(32) NOT NULL DEFAULT 'CUSTOMIZE' COMMENT '模板类型', CATEGORY varchar(128) DEFAULT NULL COMMENT '分类', LOGO_URL varchar(512) DEFAULT NULL COMMENT 'Logo地址', SRC_TEMPLATE_ID varchar(32) DEFAULT NULL COMMENT '源模板ID', STORE_FLAG bit(1) DEFAULT b'0' COMMENT '是否来自商店', WEIGHT int(11) DEFAULT '0' COMMENT '权重', LATEST_FLAG bit(1) DEFAULT b'0' COMMENT '是否最新版本', DRAFT_FLAG bit(1) DEFAULT b'0' COMMENT '是否草稿', PRIMARY KEY (VERSION), KEY IDX_PROJECT_ID (PROJECT_ID), KEY IDX_ID (ID), KEY IDX_SRC_TEMPLATE_ID (SRC_TEMPLATE_ID) );

T_TEMPLATE_PIPELINE - 模板流水线关联表

CREATE TABLE IF NOT EXISTS T_TEMPLATE_PIPELINE ( PIPELINE_ID varchar(34) NOT NULL COMMENT '流水线ID', INSTANCE_TYPE varchar(32) NOT NULL DEFAULT 'CONSTRAINT' COMMENT '实例类型', ROOT_TEMPLATE_ID varchar(32) DEFAULT NULL COMMENT '根模板ID', VERSION bigint(20) NOT NULL COMMENT '模板版本', VERSION_NAME varchar(64) NOT NULL COMMENT '版本名称', TEMPLATE_ID varchar(32) NOT NULL COMMENT '模板ID', CREATOR varchar(64) NOT NULL COMMENT '创建者', UPDATOR varchar(64) NOT NULL COMMENT '更新者', CREATED_TIME datetime NOT NULL COMMENT '创建时间', UPDATED_TIME datetime NOT NULL COMMENT '更新时间', BUILD_NO text COMMENT '构建号信息', PARAM mediumtext COMMENT '参数', DELETED bit(1) DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (PIPELINE_ID), KEY IDX_TEMPLATE_ID (TEMPLATE_ID), KEY IDX_ROOT_TEMPLATE_ID (ROOT_TEMPLATE_ID) );

3.2 V2 版本表

T_PIPELINE_TEMPLATE_INFO - 模板基础信息表

CREATE TABLE IF NOT EXISTS T_PIPELINE_TEMPLATE_INFO ( TEMPLATE_ID varchar(34) NOT NULL COMMENT '模板ID', PROJECT_ID varchar(64) NOT NULL COMMENT '项目ID', TEMPLATE_NAME varchar(255) NOT NULL COMMENT '模板名称', TEMPLATE_TYPE varchar(32) NOT NULL DEFAULT 'CUSTOMIZE' COMMENT '模板类型', CREATOR varchar(64) NOT NULL COMMENT '创建者', CREATE_TIME datetime(3) NOT NULL COMMENT '创建时间', LATEST_VERSION bigint(20) DEFAULT NULL COMMENT '最新版本号', RELEASE_VERSION bigint(20) DEFAULT NULL COMMENT '发布版本号', DRAFT_VERSION bigint(20) DEFAULT NULL COMMENT '草稿版本号', DELETE bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (TEMPLATE_ID), KEY IDX_PROJECT_ID (PROJECT_ID) );

T_PIPELINE_TEMPLATE_RESOURCE_VERSION - 模板资源版本表

CREATE TABLE IF NOT EXISTS T_PIPELINE_TEMPLATE_RESOURCE_VERSION ( TEMPLATE_ID varchar(34) NOT NULL COMMENT '模板ID', VERSION bigint(20) NOT NULL COMMENT '版本号', VERSION_NAME varchar(64) NOT NULL COMMENT '版本名称', PROJECT_ID varchar(64) NOT NULL COMMENT '项目ID', CREATOR varchar(64) NOT NULL COMMENT '创建者', CREATE_TIME datetime(3) NOT NULL COMMENT '创建时间', MODEL mediumtext COMMENT '模型内容(JSON)', STATUS varchar(32) DEFAULT NULL COMMENT '版本状态', BRANCH_ACTION varchar(32) DEFAULT NULL COMMENT '分支操作', DESCRIPTION varchar(1024) DEFAULT NULL COMMENT '版本描述', YAML mediumtext COMMENT 'YAML内容', YAML_VERSION varchar(34) DEFAULT NULL COMMENT 'YAML版本', BASE_VERSION bigint(20) DEFAULT NULL COMMENT '基础版本', DEBUG_BUILD_ID varchar(64) DEFAULT NULL COMMENT '调试构建ID', REFER_FLAG bit(1) DEFAULT NULL COMMENT '引用标志', REFER_TEMPLATE_ID varchar(34) DEFAULT NULL COMMENT '引用模板ID', PRIMARY KEY (TEMPLATE_ID, VERSION), KEY IDX_PROJECT_ID (PROJECT_ID) );

T_TEMPLATE_INSTANCE_BASE - 实例化基础表

CREATE TABLE IF NOT EXISTS T_TEMPLATE_INSTANCE_BASE ( ID varchar(32) NOT NULL COMMENT '主键ID', TEMPLATE_ID varchar(32) DEFAULT NULL COMMENT '模板ID', TEMPLATE_VERSION bigint(20) DEFAULT NULL COMMENT '模板版本', USE_TEMPLATE_SETTINGS_FLAG bit(1) DEFAULT b'1' COMMENT '使用模板设置', PROJECT_ID varchar(64) NOT NULL COMMENT '项目ID', TOTAL_ITEM_NUM int(11) NOT NULL COMMENT '总实例数', SUCCESS_ITEM_NUM int(11) NOT NULL DEFAULT '0' COMMENT '成功实例数', FAIL_ITEM_NUM int(11) NOT NULL DEFAULT '0' COMMENT '失败实例数', STATUS varchar(32) NOT NULL COMMENT '状态', CREATOR varchar(50) NOT NULL COMMENT '创建者', MODIFIER varchar(50) NOT NULL COMMENT '修改者', UPDATE_TIME datetime(3) NOT NULL COMMENT '更新时间', CREATE_TIME datetime(3) NOT NULL COMMENT '创建时间', PRIMARY KEY (ID), KEY IDX_TEMPLATE_ID (TEMPLATE_ID), KEY IDX_PROJECT_ID (PROJECT_ID) );

T_TEMPLATE_INSTANCE_ITEM - 实例化项表

CREATE TABLE IF NOT EXISTS T_TEMPLATE_INSTANCE_ITEM ( ID varchar(32) NOT NULL COMMENT '主键ID', BASE_ID varchar(32) NOT NULL COMMENT '基础ID', PIPELINE_ID varchar(34) DEFAULT NULL COMMENT '流水线ID', PIPELINE_NAME varchar(255) NOT NULL COMMENT '流水线名称', BUILD_NO_INFO varchar(512) DEFAULT NULL COMMENT '构建号信息', STATUS varchar(32) NOT NULL COMMENT '状态', PARAM mediumtext COMMENT '参数', CREATE_TIME datetime(3) NOT NULL COMMENT '创建时间', UPDATE_TIME datetime(3) NOT NULL COMMENT '更新时间', PRIMARY KEY (ID), KEY IDX_BASE_ID (BASE_ID), KEY IDX_PIPELINE_ID (PIPELINE_ID) );

四、核心功能实现

4.1 模板创建

V1 版本创建流程

// 文件: process/biz-process/.../service/template/TemplateFacadeService.kt

@Service class TemplateFacadeService {

fun createTemplate(
    projectId: String,
    userId: String,
    template: Model,
    templateType: TemplateType = TemplateType.CUSTOMIZE
): String {
    // 1. 权限校验
    pipelineTemplatePermissionService.checkPipelineTemplatePermission(
        userId = userId,
        projectId = projectId,
        permission = AuthPermission.CREATE
    )
    
    // 2. 模板名称校验
    if (templateDao.countByName(dslContext, projectId, template.name) > 0) {
        throw ErrorCodeException(errorCode = ProcessMessageCode.ERROR_TEMPLATE_NAME_IS_EXISTS)
    }
    
    // 3. 生成模板ID
    val templateId = UUIDUtil.generate()
    
    // 4. 保存模板
    templateDao.create(
        dslContext = dslContext,
        templateId = templateId,
        projectId = projectId,
        templateName = template.name,
        versionName = INIT_TEMPLATE_VERSION_NAME,
        userId = userId,
        template = JsonUtil.toJson(template),
        type = templateType.name
    )
    
    // 5. 注册权限资源
    pipelineTemplatePermissionService.createResource(
        userId = userId,
        projectId = projectId,
        templateId = templateId,
        templateName = template.name
    )
    
    return templateId
}

}

V2 版本创建流程

// 文件: process/biz-process/.../service/template/v2/PipelineTemplateFacadeService.kt

@Service class PipelineTemplateFacadeService {

fun create(
    userId: String,
    projectId: String,
    request: PipelineTemplateCustomCreateReq
): PipelineTemplateCreateResp {
    // 1. 权限校验
    pipelineTemplatePermissionService.checkPipelineTemplatePermission(
        userId = userId,
        projectId = projectId,
        permission = AuthPermission.CREATE
    )
    
    // 2. 调用版本管理器
    val result = pipelineTemplateVersionManager.deployTemplate(
        userId = userId,
        projectId = projectId,
        request = request
    )
    
    // 3. 注册权限资源
    pipelineTemplatePermissionService.createResource(
        userId = userId,
        projectId = projectId,
        templateId = result.templateId,
        templateName = request.name
    )
    
    return PipelineTemplateCreateResp(
        templateId = result.templateId,
        version = result.version
    )
}

}

4.2 模板实例化

模板实例化是将模板转换为具体流水线的过程:

// 文件: process/biz-process/.../service/template/v2/PipelineTemplateInstanceService.kt

@Service class PipelineTemplateInstanceService {

/**
 * 批量实例化模板
 */
fun createTemplateInstances(
    userId: String,
    projectId: String,
    templateId: String,
    version: Long,
    request: PipelineTemplateInstancesRequest
): List<PipelineTemplateInstanceItem> {
    // 1. 校验创建流水线权限
    pipelinePermissionService.validPipelinePermission(
        userId = userId,
        projectId = projectId,
        permission = AuthPermission.CREATE
    )
    
    // 2. 获取模板详情
    val templateDetail = pipelineTemplateFacadeService.getTemplateDetail(
        userId = userId,
        projectId = projectId,
        templateId = templateId,
        version = version
    )
    
    // 3. 遍历创建流水线实例
    return request.instances.map { instance ->
        createInstance(
            userId = userId,
            projectId = projectId,
            templateDetail = templateDetail,
            instance = instance
        )
    }
}

private fun createInstance(
    userId: String,
    projectId: String,
    templateDetail: PipelineTemplateDetailsResponse,
    instance: PipelineTemplateInstanceItem
): PipelineTemplateInstanceItem {
    // 1. 合并模板参数和实例参数
    val model = mergeTemplateAndInstanceParams(
        templateModel = templateDetail.model,
        instanceParams = instance.params
    )
    
    // 2. 创建流水线
    val pipelineId = pipelineInfoFacadeService.create(
        userId = userId,
        projectId = projectId,
        model = model,
        channelCode = ChannelCode.BS
    )
    
    // 3. 建立模板-流水线关联
    templatePipelineDao.create(
        dslContext = dslContext,
        pipelineId = pipelineId,
        templateId = templateDetail.templateId,
        version = templateDetail.version,
        versionName = templateDetail.versionName,
        userId = userId
    )
    
    return instance.copy(pipelineId = pipelineId)
}

}

4.3 版本管理

V2 版本采用策略模式实现版本管理:

// 文件: process/biz-process/.../service/template/v2/version/PipelineTemplateVersionManager.kt

@Service class PipelineTemplateVersionManager( private val versionReqConverters: List<PipelineTemplateVersionReqConverter>, private val versionCreateHandlers: List<PipelineTemplateVersionCreateHandler>, private val pipelineTemplateVersionValidator: PipelineTemplateVersionValidator, private val versionDeleteHandler: PipelineTemplateVersionDeleteHandler ) {

/**
 * 部署模板(创建/更新版本)
 */
fun deployTemplate(
    userId: String,
    projectId: String,
    templateId: String? = null,
    version: Long? = null,
    request: PipelineTemplateVersionReq
): DeployTemplateResult {
    // 1. 转换请求为上下文
    val context = getConverter(request).convert(
        userId = userId,
        projectId = projectId,
        templateId = templateId,
        version = version,
        request = request
    )
    
    // 2. 校验版本
    pipelineTemplateVersionValidator.validate(context = context)
    
    // 3. 调用对应处理器
    return getHandler(context).handle(context = context)
}

/**
 * 删除版本
 */
fun deleteVersion(
    userId: String,
    projectId: String,
    templateId: String,
    version: Long?,
    versionName: String? = null
) {
    val context = PipelineTemplateVersionDeleteContext(
        userId = userId,
        projectId = projectId,
        templateId = templateId,
        version = version,
        versionAction = PipelineVersionAction.DELETE_VERSION
    )
    versionDeleteHandler.handle(context = context)
}

private fun getHandler(context: PipelineTemplateVersionCreateContext): PipelineTemplateVersionCreateHandler {
    return versionCreateHandlers.find { it.support(context) }
        ?: throw IllegalArgumentException("Unsupported version event")
}

private fun getConverter(request: PipelineTemplateVersionReq): PipelineTemplateVersionReqConverter {
    return versionReqConverters.find { it.support(request) }
        ?: throw IllegalArgumentException("Unsupported version request")
}

}

版本处理器类型

处理器 功能 触发场景

PipelineTemplateDraftSaveHandler

保存草稿 编辑模板后保存草稿

PipelineTemplateDraftReleaseHandler

发布草稿 将草稿发布为正式版本

PipelineTemplateReleaseCreateHandler

创建发布版本 直接创建正式版本

PipelineTemplateBranchCreateHandler

创建分支版本 PAC 场景下的分支版本

PipelineTemplateVersionDeleteHandler

删除版本 删除指定版本

4.4 权限控制

// 文件: process/biz-process/.../permission/template/PipelineTemplatePermissionService.kt

interface PipelineTemplatePermissionService {

/**
 * 校验模板权限
 */
fun checkPipelineTemplatePermission(
    userId: String,
    projectId: String,
    templateId: String? = null,
    permission: AuthPermission,
    message: String? = null
)

/**
 * 创建权限资源
 */
fun createResource(
    userId: String,
    projectId: String,
    templateId: String,
    templateName: String
)

/**
 * 删除权限资源
 */
fun deleteResource(
    projectId: String,
    templateId: String
)

/**
 * 获取有权限的模板列表
 */
fun filterTemplates(
    userId: String,
    projectId: String,
    permission: AuthPermission,
    templateIds: List&#x3C;String>
): List&#x3C;String>

/**
 * 是否启用模板权限管理
 */
fun enableTemplatePermissionManage(projectId: String): Boolean

}

RBAC 权限实现

// 文件: process/biz-process/.../permission/template/RbacPipelineTemplatePermissionService.kt

@Service class RbacPipelineTemplatePermissionService( private val client: Client, private val authPermissionApi: AuthPermissionApi, private val authResourceApi: AuthResourceApi ) : AbstractPipelineTemplatePermissionService() {

override fun checkPipelineTemplatePermission(
    userId: String,
    projectId: String,
    templateId: String?,
    permission: AuthPermission,
    message: String?
) {
    // 调用权限中心校验
    val hasPermission = authPermissionApi.validateUserResourcePermission(
        userId = userId,
        serviceCode = AuthServiceCode.PIPELINE,
        resourceType = AuthResourceType.PIPELINE_TEMPLATE,
        projectCode = projectId,
        resourceCode = templateId ?: "*",
        permission = permission
    )
    
    if (!hasPermission) {
        throw PermissionForbiddenException(message ?: "无模板操作权限")
    }
}

override fun createResource(
    userId: String,
    projectId: String,
    templateId: String,
    templateName: String
) {
    authResourceApi.createResource(
        userId = userId,
        serviceCode = AuthServiceCode.PIPELINE,
        resourceType = AuthResourceType.PIPELINE_TEMPLATE,
        projectCode = projectId,
        resourceCode = templateId,
        resourceName = templateName
    )
}

}

五、PAC (Pipeline as Code) 模板

5.1 PAC 概述

PAC(Pipeline as Code)是 BK-CI 的核心特性,允许用户通过 YAML 文件定义和管理流水线模板。模板 PAC 支持:

  • YAML 定义模板:使用 YAML 格式定义模板配置

  • Git 仓库托管:模板 YAML 文件存储在 Git 仓库中

  • Webhook 自动同步:代码推送自动触发模板更新

  • 分支版本管理:支持基于 Git 分支的版本管理

  • 双向转换:Model ↔ YAML 双向转换

5.2 模板类型(PAC 视角)

// 文件: common/common-pipeline/.../template/PipelineTemplateType.kt enum class PipelineTemplateType(val value: String) { PIPELINE("pipeline"), // 完整流水线模板 STAGE("stage"), // Stage 级别模板 JOB("job"), // Job 级别模板 STEP("step"), // Step 级别模板 VARIABLE("variable") // 变量模板 }

5.3 PAC 核心服务

5.3.1 TemplatePACService(V1 PAC 服务)

// 文件: process/biz-process/.../service/template/TemplatePACService.kt

@Service class TemplatePACService( private val templateDao: TemplateDao, private val transferYamlService: PipelineTransferYamlService, private val templateCommonService: TemplateCommonService ) { /** * 预览模板(返回 Model + YAML + 高亮标记) */ fun previewTemplate( userId: String, projectId: String, templateId: String, highlightType: HighlightType? ): TemplatePreviewDetail { // 1. 获取模板 val template = templateDao.getLatestTemplate(dslContext, projectId, templateId) val model: Model = objectMapper.readValue(template.template)

    // 2. 获取设置
    val setting = pipelineRepositoryService.getSetting(projectId, templateId)
    
    // 3. Model 转 YAML
    val yaml = transferYamlService.transfer(
        userId = userId,
        projectId = projectId,
        actionType = TransferActionType.FULL_MODEL2YAML,
        data = TransferBody(PipelineModelAndSetting(model, setting))
    ).yamlWithVersion?.yamlStr
    
    // 4. 生成高亮标记(用于 UI 展示)
    val highlightMarkList = buildHighlightMarks(yaml, highlightType)
    
    return TemplatePreviewDetail(
        template = model,
        templateYaml = yaml,
        setting = setting,
        hasPermission = hasPermission,
        highlightMarkList = highlightMarkList
    )
}

}

5.3.2 PTemplateYamlResourceService(YAML 资源服务)

// 文件: process/biz-process/.../yaml/resource/PTemplateYamlResourceService.kt

@Service class PTemplateYamlResourceService( private val pipelineTemplateFacadeService: PipelineTemplateFacadeService, private val pipelineTemplateInfoService: PipelineTemplateInfoService ) : IPipelineYamlResourceService {

/**
 * 通过 YAML 创建模板(Git Webhook 触发)
 */
override fun createYamlPipeline(
    userId: String,
    projectId: String,
    yaml: String,
    event: PipelineYamlFileEvent
): DeployPipelineResult {
    with(event) {
        val isDefaultBranch = ref == defaultBranch
        val yamlFileName = GitActionCommon.getCiTemplateName(filePath)
        
        // 调用 V2 服务创建 YAML 模板
        val result = pipelineTemplateFacadeService.createYamlTemplate(
            userId = userId,
            projectId = projectId,
            yaml = yaml,
            yamlFileName = yamlFileName,
            branchName = ref,
            isDefaultBranch = isDefaultBranch,
            description = commit!!.commitMsg,
            yamlFileInfo = PipelineYamlFileInfo(repoHashId, filePath)
        )
        
        return DeployPipelineResult(
            pipelineId = result.templateId,
            pipelineName = result.templateName,
            version = result.version.toInt(),
            versionName = result.versionName
        )
    }
}

/**
 * 通过 YAML 更新模板
 */
override fun updateYamlPipeline(
    userId: String,
    projectId: String,
    pipelineId: String,
    yaml: String,
    event: PipelineYamlFileEvent
): DeployPipelineResult {
    // 类似创建逻辑,调用 updateYamlTemplate
}

/**
 * 分支失活(分支删除时调用)
 */
override fun updateBranchAction(
    userId: String,
    projectId: String,
    pipelineId: String,
    branchName: String,
    branchVersionAction: BranchVersionAction
) {
    pipelineTemplateFacadeService.inactiveBranch(
        userId = userId,
        projectId = projectId,
        templateId = pipelineId,
        branch = branchName
    )
}

}

5.3.3 PipelineYamlResourceManager(统一管理器)

// 文件: process/biz-process/.../yaml/resource/PipelineYamlResourceManager.kt

@Service class PipelineYamlResourceManager( private val pipelineYamlResourceService: PipelineYamlResourceService, @Lazy private val pTemplateYamlResourceService: PTemplateYamlResourceService ) { /** * 根据 isTemplate 标志选择对应的服务 */ fun getService(isTemplate: Boolean): IPipelineYamlResourceService { return if (isTemplate) { pTemplateYamlResourceService // 模板 YAML 服务 } else { pipelineYamlResourceService // 流水线 YAML 服务 } }

fun createYamlPipeline(userId: String, projectId: String, yaml: String, event: PipelineYamlFileEvent) =
    getService(event.isTemplate).createYamlPipeline(userId, projectId, yaml, event)

fun updateYamlPipeline(userId: String, projectId: String, pipelineId: String, yaml: String, event: PipelineYamlFileEvent) =
    getService(event.isTemplate).updateYamlPipeline(userId, projectId, pipelineId, yaml, event)

}

5.4 YAML Webhook 请求处理

5.4.1 PipelineTemplateYamlWebhookReq(请求对象)

// 文件: process/api-process/.../pojo/template/v2/PipelineTemplateYamlWebhookReq.kt

@Schema(title = "模版yaml文件推送请求") data class PipelineTemplateYamlWebhookReq( @get:Schema(title = "模板YAML", required = true) val yaml: String,

@get:Schema(title = "yaml文件名", required = true)
val yamlFileName: String,

@get:Schema(title = "分支名", required = true)
val branchName: String,

@get:Schema(title = "是否默认分支", required = true)
val isDefaultBranch: Boolean,

@get:Schema(title = "描述", required = true)
val description: String? = null,

@get:Schema(title = "yaml文件信息", required = true)
val yamlFileInfo: PipelineYamlFileInfo? = null

) : PipelineTemplateVersionReq // 继承版本请求接口

5.4.2 PipelineTemplateYamlWebhookReqConverter(请求转换器)

// 文件: process/biz-process/.../version/convert/PipelineTemplateYamlWebhookReqConverter.kt

@Service class PipelineTemplateYamlWebhookReqConverter( private val pipelineTemplateGenerator: PipelineTemplateGenerator, private val pipelineTemplateInfoService: PipelineTemplateInfoService ) : PipelineTemplateVersionReqConverter {

override fun support(request: PipelineTemplateVersionReq) = 
    request is PipelineTemplateYamlWebhookReq

override fun convert(
    userId: String,
    projectId: String,
    templateId: String?,
    version: Long?,
    request: PipelineTemplateVersionReq
): PipelineTemplateVersionCreateContext {
    request as PipelineTemplateYamlWebhookReq
    
    // 1. YAML 转 Model
    val transferResult = pipelineTemplateGenerator.transfer(
        userId = userId,
        projectId = projectId,
        storageType = PipelineStorageType.YAML,
        yaml = request.yaml
    )
    
    // 2. 根据是否默认分支决定版本状态
    val (status, versionAction) = if (request.isDefaultBranch) {
        Pair(VersionStatus.RELEASED, PipelineVersionAction.CREATE_RELEASE)
    } else {
        Pair(VersionStatus.BRANCH, PipelineVersionAction.CREATE_BRANCH)
    }
    
    // 3. 模板名称优先级:setting > model > fileName
    val templateName = transferResult.templateSetting.pipelineName
        .takeIf { it.isNotBlank() }
        ?: (transferResult.templateModel as? Model)?.name?.ifBlank { request.yamlFileName }
        ?: request.yamlFileName
    
    // 4. 构建上下文
    return PipelineTemplateVersionCreateContext(
        userId = userId,
        projectId = projectId,
        templateId = templateId ?: pipelineTemplateGenerator.generateTemplateId(),
        versionAction = versionAction,
        newTemplate = templateId == null,
        enablePac = true,  // 标记为 PAC 模板
        yamlFileInfo = request.yamlFileInfo,
        branchName = request.branchName
    )
}

}

5.5 Model ↔ YAML 转换

5.5.1 PipelineTemplateGenerator(模板生成器)

// 文件: process/biz-process/.../service/template/v2/PipelineTemplateGenerator.kt

@Service class PipelineTemplateGenerator( private val transferService: PipelineTransferYamlService ) { /** * 双向转换:Model ↔ YAML */ fun transfer( userId: String, projectId: String, storageType: PipelineStorageType, templateType: PipelineTemplateType?, templateModel: ITemplateModel?, templateSetting: PipelineSetting?, params: List<BuildFormProperty>?, yaml: String?, fallbackOnError: Boolean = false ): PTemplateModelTransferResult { return if (storageType == PipelineStorageType.YAML) { // YAML → Model transferYamlToModel(userId, projectId, templateType, params, yaml) } else { // Model → YAML transferModelToYamlWithFallback( userId, projectId, templateType, templateModel, templateSetting, params, fallbackOnError ) } }

/**
 * YAML → Model 转换
 */
private fun transferYamlToModel(...): PTemplateModelTransferResult {
    val transferResult = transferService.transfer(
        userId = userId,
        projectId = projectId,
        actionType = TransferActionType.TEMPLATE_YAML2MODEL_PIPELINE,
        data = TransferBody(oldYaml = yaml)
    )
    return PTemplateModelTransferResult(
        templateType = templateType ?: PipelineTemplateType.PIPELINE,
        templateModel = transferResult.templateModelAndSetting?.templateModel,
        templateSetting = transferResult.templateModelAndSetting?.setting,
        yamlWithVersion = transferResult.yamlWithVersion
    )
}

/**
 * Model → YAML 转换(带异常兜底)
 */
private fun transferModelToYamlWithFallback(...): PTemplateModelTransferResult {
    val actionType = getTransferActionType(templateType)
    // TransferActionType 根据模板类型选择:
    // PIPELINE → TEMPLATE_MODEL2YAML_PIPELINE
    // STAGE    → TEMPLATE_MODEL2YAML_STAGE
    // JOB      → TEMPLATE_MODEL2YAML_JOB
    // STEP     → TEMPLATE_MODEL2YAML_STEP
    
    return try {
        val result = transferService.transfer(
            actionType = actionType,
            data = TransferBody(
                templateModelAndSetting = TemplateModelAndSetting(templateModel, templateSetting)
            )
        )
        PTemplateModelTransferResult(yamlWithVersion = result.yamlWithVersion, ...)
    } catch (ex: Exception) {
        if (fallbackOnError) {
            // 兜底:返回原始 Model,YAML 为空
            PTemplateModelTransferResult(yamlWithVersion = null, ...)
        } else {
            throw ex
        }
    }
}

}

5.5.2 TransferActionType(转换动作类型)

// 转换动作类型枚举 enum class TransferActionType { // 模板 YAML → Model TEMPLATE_YAML2MODEL_PIPELINE,

// 模板 Model → YAML(按模板类型)
TEMPLATE_MODEL2YAML_PIPELINE,
TEMPLATE_MODEL2YAML_STAGE,
TEMPLATE_MODEL2YAML_JOB,
TEMPLATE_MODEL2YAML_STEP,

// 完整转换(包含设置)
FULL_MODEL2YAML,
FULL_YAML2MODEL

}

5.6 PAC 版本状态

// 版本状态枚举 enum class VersionStatus { COMMITTING, // 草稿状态 RELEASED, // 已发布(默认分支) BRANCH // 分支版本(非默认分支) }

状态 触发场景 说明

RELEASED

推送到默认分支 正式发布版本

BRANCH

推送到非默认分支 分支版本,可用于测试

COMMITTING

UI 保存草稿 草稿状态,未发布

5.7 PAC 版本生成

// 文件: PipelineTemplateGenerator.kt

/**

  • 生成分支版本 */ fun generateBranchVersion( projectId: String, templateId: String, branchName: String ): PTemplateResourceOnlyVersion { val latestResource = pipelineTemplateResourceService.getLatestVersionResource(projectId, templateId)

    // 如果已存在同名分支版本,基于分支版本创建 val branchResource = pipelineTemplateResourceService.getLatestBranchResource( projectId, templateId, branchName )

    return PTemplateResourceOnlyVersion( version = generateTemplateVersion(), number = latestResource.number + 1, versionName = branchName, // 分支版本名 = 分支名 baseVersion = branchResource?.version ?: latestResource.version ) }

/**

  • 草稿发布时的 PAC 版本生成 */ fun generateDraftReleaseVersionWithPac( projectId: String, templateId: String, draftResource: PipelineTemplateResource, repoHashId: String, targetAction: CodeTargetAction?, targetBranch: String? ): Pair<VersionStatus, PTemplateResourceOnlyVersion> { return when (targetAction) { // 直接提交到主分支 → 发布版本 CodeTargetAction.COMMIT_TO_MASTER -> { Pair(VersionStatus.RELEASED, generateReleaseVersion(...)) }

     // 提交到源分支 → 分支版本
     CodeTargetAction.COMMIT_TO_SOURCE_BRANCH -> {
         Pair(VersionStatus.BRANCH, ...)
     }
     
     // 新建分支并提交 → 分支版本
     CodeTargetAction.CHECKOUT_BRANCH_AND_REQUEST_MERGE -> {
         val versionName = "bk-ci-template-$templateId-${draftResource.number}"
         Pair(VersionStatus.BRANCH, ...)
     }
     
     // 提交到指定分支
     CodeTargetAction.COMMIT_TO_BRANCH -> {
         // 如果是默认分支 → 发布版本
         // 否则 → 分支版本
     }
    

    } }

5.8 PAC 目录结构

src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/ ├── yaml/ │ ├── resource/ │ │ ├── IPipelineYamlResourceService.kt # YAML 资源服务接口 │ │ ├── PipelineYamlResourceService.kt # 流水线 YAML 服务 │ │ ├── PTemplateYamlResourceService.kt # 模板 YAML 服务 │ │ └── PipelineYamlResourceManager.kt # 统一管理器 │ ├── mq/ │ │ └── PipelineYamlFileEvent.kt # YAML 文件事件 │ ├── PipelineYamlFileManager.kt # YAML 文件管理 │ ├── PipelineYamlFacadeService.kt # YAML 门面服务 │ └── PipelineYamlSyncService.kt # YAML 同步服务 ├── service/template/ │ ├── TemplatePACService.kt # V1 PAC 服务 │ └── v2/ │ ├── PipelineTemplateGenerator.kt # 模板生成器(含转换) │ └── version/convert/ │ └── PipelineTemplateYamlWebhookReqConverter.kt # Webhook 转换器 └── common/common-pipeline-yaml/ # YAML 解析公共库 └── src/main/kotlin/.../yaml/ ├── v2/parsers/template/ │ ├── TemplateYamlMapper.kt # YAML 映射 │ └── TemplateYamlUtil.kt # YAML 工具 └── v3/parsers/template/ ├── TemplateYamlMapper.kt └── TemplateYamlUtil.kt

5.9 PAC 完整流程

┌─────────────────────────────────────────────────────────────────────────────┐ │ PAC 模板创建/更新流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────────┐ │ │ │ Git Push │───▶│ Webhook Event │───▶│ PipelineYamlFileEvent │ │ │ │ (YAML文件) │ │ (代码仓库触发) │ │ (isTemplate=true) │ │ │ └─────────────┘ └──────────────────┘ └───────────┬─────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ PipelineYamlResourceManager │ │ │ │ (根据 isTemplate 路由) │ │ │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ PTemplateYamlResourceService │ │ │ │ (模板 YAML 资源服务) │ │ │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ PipelineTemplateFacadeService.createYamlTemplate() │ │ │ │ 构建 PipelineTemplateYamlWebhookReq │ │ │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ PipelineTemplateVersionManager │ │ │ │ deployTemplate() │ │ │ └───────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ┌────────────────────────┼────────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌────────────────────┐ ┌───────────────────┐ │ │ │ Converter │───▶│ Validator │───▶│ Handler │ │ │ │ (YAML→Model)│ │ (校验模板) │ │ (创建/更新版本) │ │ │ └─────────────┘ └────────────────────┘ └─────────┬─────────┘ │ │ │ │ │ ┌───────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 默认分支? │ │ │ │ ├── YES → VersionStatus.RELEASED → 正式版本 │ │ │ │ └── NO → VersionStatus.BRANCH → 分支版本 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘

5.10 PAC 最佳实践

5.10.1 模板 YAML 文件命名

  • 文件路径:.ci/templates/xxx.yml 或 .ci/templates/xxx.yaml

  • 模板名称默认从 YAML 中的 name 字段获取,若无则使用文件名

5.10.2 分支策略

分支类型 版本状态 使用场景

默认分支(main/master) RELEASED

正式发布,可被实例化

特性分支 BRANCH

开发测试,不影响正式版本

PR 分支 BRANCH

代码审查,合并后自动发布

5.10.3 enablePac 标志

// 模板信息中的 PAC 标志 data class PipelineTemplateInfoV2( val id: String, val projectId: String, val name: String, val enablePac: Boolean = false, // 是否启用 PAC // ... )

  • enablePac = true :模板由 YAML 文件管理,UI 编辑会同步到代码仓库

  • enablePac = false :传统模板,仅在 BK-CI 内管理

六、研发商店集成

6.1 Store 模块模板服务

src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/template/ ├── service/ │ ├── MarketTemplateService.kt # 商店模板服务接口 │ ├── MarketTemplateServiceImpl.kt # 商店模板服务实现 │ ├── TemplateReleaseService.kt # 模板发布服务接口 │ ├── TemplateReleaseServiceImpl.kt # 模板发布服务实现 │ ├── OpTemplateService.kt # 运营模板服务接口 │ └── OpTemplateServiceImpl.kt # 运营模板服务实现 └── dao/ ├── MarketTemplateDao.kt # 商店模板 DAO ├── TemplateCategoryRelDao.kt # 模板分类关联 DAO └── TemplateLabelRelDao.kt # 模板标签关联 DAO

6.2 商店模板数据库表

-- Store 模块的模板表 CREATE TABLE IF NOT EXISTS T_TEMPLATE ( ID varchar(32) NOT NULL COMMENT '主键ID', TEMPLATE_NAME varchar(200) NOT NULL COMMENT '模板名称', TEMPLATE_CODE varchar(64) NOT NULL COMMENT '模板代码', CLASSIFY_ID varchar(32) NOT NULL COMMENT '分类ID', VERSION varchar(20) NOT NULL COMMENT '版本号', TEMPLATE_TYPE tinyint(4) NOT NULL DEFAULT '1' COMMENT '模板类型', TEMPLATE_STATUS tinyint(4) NOT NULL COMMENT '模板状态', TEMPLATE_STATUS_MSG varchar(1024) DEFAULT NULL COMMENT '状态信息', LOGO_URL varchar(256) DEFAULT NULL COMMENT 'Logo地址', SUMMARY varchar(256) DEFAULT NULL COMMENT '简介', DESCRIPTION text COMMENT '描述', PUBLISHER varchar(50) NOT NULL COMMENT '发布者', PUB_DESCRIPTION text COMMENT '发布描述', PUBLIC_FLAG bit(1) NOT NULL DEFAULT b'0' COMMENT '是否公开', LATEST_FLAG bit(1) NOT NULL COMMENT '是否最新', CREATOR varchar(50) NOT NULL COMMENT '创建者', MODIFIER varchar(50) NOT NULL COMMENT '修改者', CREATE_TIME datetime NOT NULL COMMENT '创建时间', UPDATE_TIME datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (ID), UNIQUE KEY UNI_TEMPLATE_CODE_VERSION (TEMPLATE_CODE, VERSION) );

6.3 模板安装流程

// 从商店安装模板到项目 fun installTemplateFromStore( userId: String, projectId: String, templateCode: String, version: String ): String { // 1. 获取商店模板信息 val storeTemplate = marketTemplateService.getTemplateByCode(templateCode, version)

// 2. 获取模板模型
val templateModel = storeTemplate.templateModel

// 3. 创建项目模板(类型为 CONSTRAINT)
val templateId = templateFacadeService.createTemplate(
    projectId = projectId,
    userId = userId,
    template = templateModel,
    templateType = TemplateType.CONSTRAINT
)

// 4. 记录安装历史
templateVersionInstallHistoryDao.create(
    projectId = projectId,
    templateId = templateId,
    templateCode = templateCode,
    version = version,
    userId = userId
)

return templateId

}

七、事件驱动机制

7.1 模板相关事件

// 文件: process/biz-base/.../engine/pojo/event/

// 模板实例化事件 data class PipelineTemplateInstanceEvent( val source: String, val projectId: String, val templateId: String, val version: Long, val instanceBaseId: String, val userId: String ) : IEvent

// 模板迁移事件 data class PipelineTemplateMigrateEvent( val source: String, val projectId: String, val templateId: String, val userId: String ) : IEvent

// 模板触发器升级事件 data class PipelineTemplateTriggerUpgradesEvent( val source: String, val projectId: String, val templateId: String, val version: Long, val userId: String ) : IEvent

7.2 事件监听器

// 文件: process/biz-process/.../service/template/v2/PipelineTemplateInstanceListener.kt

@Service class PipelineTemplateInstanceListener( private val pipelineTemplateInstanceService: PipelineTemplateInstanceService ) {

@StreamListener(PipelineTemplateInstanceEvent.TOPIC)
fun onTemplateInstance(event: PipelineTemplateInstanceEvent) {
    // 处理异步批量实例化
    pipelineTemplateInstanceService.processAsyncInstance(
        projectId = event.projectId,
        templateId = event.templateId,
        version = event.version,
        instanceBaseId = event.instanceBaseId,
        userId = event.userId
    )
}

}

八、分布式锁

8.1 模板相关锁

// 文件: process/biz-base/.../engine/control/lock/

// 实例计数锁 - 防止并发实例化时计数错误 class PipelineTemplateInstanceCountLock( private val redisOperation: RedisOperation, private val templateId: String ) : BaseLock(redisOperation, "template:instance:count:$templateId")

// 触发器升级锁 - 防止并发升级触发器 class PipelineTemplateTriggerUpgradesLock( private val redisOperation: RedisOperation, private val templateId: String ) : BaseLock(redisOperation, "template:trigger:upgrade:$templateId")

// 文件: process/biz-process/.../service/template/v2/

// V2 实例化锁 class PipelineTemplateInstanceLock( private val redisOperation: RedisOperation, private val templateId: String, private val pipelineId: String ) : BaseLock(redisOperation, "template:v2:instance:$templateId:$pipelineId")

// V2 模型锁 class PipelineTemplateModelLock( private val redisOperation: RedisOperation, private val templateId: String ) : BaseLock(redisOperation, "template:v2:model:$templateId")

九、OpenAPI 接口

9.1 API 网关接口

src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/ ├── v3/ │ ├── ApigwTemplateResourceV3.kt # V3 模板接口 │ ├── ApigwTemplateInstanceResourceV3.kt # V3 实例化接口 │ └── ApigwMarketTemplateResourceV3.kt # V3 商店模板接口 └── v4/ ├── ApigwTemplateResourceV4.kt # V4 模板接口 ├── ApigwTemplateInstanceResourceV4.kt # V4 实例化接口 └── ApigwMarketTemplateResourceV4.kt # V4 商店模板接口

9.2 主要 API 端点

方法 路径 说明

POST /v4/projects/{projectId}/templates

创建模板

GET /v4/projects/{projectId}/templates

获取模板列表

GET /v4/projects/{projectId}/templates/{templateId}

获取模板详情

PUT /v4/projects/{projectId}/templates/{templateId}

更新模板

DELETE /v4/projects/{projectId}/templates/{templateId}

删除模板

POST /v4/projects/{projectId}/templates/{templateId}/instances

实例化模板

GET /v4/projects/{projectId}/templates/{templateId}/instances

获取实例列表

GET /v4/projects/{projectId}/templates/{templateId}/versions

获取版本列表

十、最佳实践

10.1 新增模板功能

  • API 层:在 api/template/v2/ 下定义接口

  • POJO 层:在 pojo/template/v2/ 下定义请求/响应对象

  • Service 层:在 service/template/v2/ 下实现业务逻辑

  • DAO 层:在 dao/template/ 下实现数据访问

  • 权限控制:调用 PipelineTemplatePermissionService 进行权限校验

10.2 版本管理扩展

如需新增版本操作类型:

  • 在 PipelineVersionAction 枚举中添加新操作

  • 实现 PipelineTemplateVersionReqConverter 转换器

  • 实现 PipelineTemplateVersionCreateHandler 处理器

  • 可选:实现 PTemplateVersionPostProcessor 后处理器

10.3 商店集成扩展

  • 在 Store 模块的 template/service/ 下实现服务

  • 调用 Process 模块的模板服务进行模板操作

  • 维护商店特有的元数据(分类、标签、评论等)

十一、常见问题

Q1: V1 和 V2 版本如何选择?

  • 新功能开发优先使用 V2 版本

  • V1 版本用于兼容老数据和简单场景

  • V2 版本支持草稿、PAC、完善的版本管理

Q2: 模板实例化失败如何排查?

  • 检查模板是否存在且版本正确

  • 检查用户是否有创建流水线权限

  • 检查模板参数是否完整

  • 查看 T_TEMPLATE_INSTANCE_ITEM 表中的错误信息

Q3: 如何实现模板的批量更新?

使用异步实例化接口:

  • 调用 createAsyncTemplateInstances 创建异步任务

  • 系统通过 MQ 事件异步处理每个实例

  • 通过 getTemplateInstanceStatus 查询进度

Q4: 模板权限如何配置?

  • 项目级别开关:enableTemplatePermissionManage

  • 权限类型:CREATE、EDIT、DELETE、LIST

  • 与蓝鲸权限中心集成,支持 RBAC 模型

Q5: PAC 模板和普通模板有什么区别?

特性 PAC 模板 普通模板

存储方式 Git 仓库 YAML 文件 BK-CI 数据库

编辑方式 代码编辑器 / IDE BK-CI UI

版本管理 Git 分支 BK-CI 版本号

同步机制 Webhook 自动同步 手动保存

协作方式 Git 工作流(PR/MR) BK-CI 权限控制

回滚方式 Git revert BK-CI 版本回滚

Q6: PAC 模板的 YAML 转换失败怎么办?

  • 检查 YAML 语法是否正确

  • 确认使用的是支持的 YAML 版本(v2/v3)

  • 查看 PipelineTemplateGenerator.transfer() 的异常日志

  • 使用 fallbackOnError=true 可以在转换失败时返回原始 Model

Q7: 如何启用模板的 PAC 功能?

  • 在代码仓库中创建 .ci/templates/xxx.yml 文件

  • 配置 Webhook 触发器

  • 推送代码后自动创建/更新模板

  • 模板的 enablePac 标志会自动设为 true

Q8: 分支版本和正式版本的关系?

  • 推送到默认分支(main/master)→ 创建 RELEASED 正式版本

  • 推送到其他分支 → 创建 BRANCH 分支版本

  • 分支版本可用于测试,不影响正式版本

  • 分支合并到默认分支后,自动发布为正式版本

十二、相关 Skill

Skill 说明

yaml-pipeline-transfer

YAML 流水线转换,理解 Model ↔ YAML 转换机制

pipeline-model-architecture

流水线模型架构,理解模板的模型结构

process-module-architecture

Process 模块架构,理解模板所在模块

auth-module-architecture

权限模块架构,理解模板权限控制

store-module-architecture

研发商店架构,理解模板商店集成

microservice-infrastructure

微服务基础设施(事件驱动),理解异步实例化机制

common-technical-practices

通用技术实践(分布式锁),理解模板并发控制

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

git-commit-specification

No summary provided by upstream source.

Repository SourceNeeds Review
General

design-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

database-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

unit-testing

No summary provided by upstream source.

Repository SourceNeeds Review