Skip to content

Commit

Permalink
feat:支持用户主动退出项目 #11319
Browse files Browse the repository at this point in the history
  • Loading branch information
fcfang123 committed Jan 7, 2025
1 parent 7dc9905 commit 2829337
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ enum class HandoverAction(
1,
"已通过",
"你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。",
"你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。",
"你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。"
),

// 审批驳回
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ data class RemoveMemberFromProjectReq(
)
}
}

fun isNeedToHandover(): Boolean {
return handoverTo != null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.tencent.devops.auth.pojo.vo

import io.swagger.v3.oas.annotations.media.Schema

@Schema(title = "成员退出项目检查返回体")
data class MemberExitsProjectCheckVo(
@get:Schema(title = "组织加入的数量")
val departmentJoinedCount: Int? = 0,
@get:Schema(title = "组织")
val departments: String? = "",
@get:Schema(title = "唯一管理员组的数量")
val uniqueManagerCount: Int? = 0,
@get:Schema(title = "流水线授权数量")
val pipelineAuthorizationCount: Int? = 0,
@get:Schema(title = "代码库授权数量")
val repositoryAuthorizationCount: Int? = 0,
@get:Schema(title = "环境节点授权数量")
val envNodeAuthorizationCount: Int? = 0
) {
fun canExitsProjectDirectly(): Boolean {
return departmentJoinedCount == 0 && uniqueManagerCount == 0 && pipelineAuthorizationCount == 0 &&
repositoryAuthorizationCount == 0 && envNodeAuthorizationCount == 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo
import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo
import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo
import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo
import com.tencent.devops.auth.pojo.vo.MemberExitsProjectCheckVo
import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo
import com.tencent.devops.auth.service.DeptService
import com.tencent.devops.auth.service.PermissionAuthorizationService
Expand All @@ -59,6 +60,7 @@ import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService
import com.tencent.devops.auth.service.iam.PermissionResourceMemberService
import com.tencent.devops.auth.service.lock.HandleHandoverApplicationLock
import com.tencent.devops.common.api.exception.ErrorCodeException
import com.tencent.devops.common.api.exception.OperationException
import com.tencent.devops.common.api.model.SQLPage
import com.tencent.devops.common.api.util.DateTimeUtil
import com.tencent.devops.common.api.util.PageUtil
Expand Down Expand Up @@ -1189,9 +1191,9 @@ class RbacPermissionManageFacadeServiceImpl(
val handoverDetails = buildHandoverDetails(
projectCode = projectCode,
groupIds = groupIds.map { it.toString() },
invalidPipelines = invalidPipelines,
invalidRepertoryIds = invalidRepertoryIds,
invalidEnvNodeIds = invalidEnvNodeIds
pipelineAuthorizations = invalidPipelines,
repertoryAuthorizations = invalidRepertoryIds,
envNodeAuthorizations = invalidEnvNodeIds
)
val projectName = authResourceService.get(
projectCode = projectCode,
Expand All @@ -1217,9 +1219,9 @@ class RbacPermissionManageFacadeServiceImpl(
private fun buildHandoverDetails(
projectCode: String,
groupIds: List<String>,
invalidPipelines: List<String>,
invalidRepertoryIds: List<String>,
invalidEnvNodeIds: List<String>
pipelineAuthorizations: List<String>,
repertoryAuthorizations: List<String>,
envNodeAuthorizations: List<String>
): List<HandoverDetailDTO> {
val handoverDetails = mutableListOf<HandoverDetailDTO>()
if (groupIds.isNotEmpty()) {
Expand All @@ -1240,7 +1242,7 @@ class RbacPermissionManageFacadeServiceImpl(
}
}

invalidPipelines.forEach { pipelineId ->
pipelineAuthorizations.forEach { pipelineId ->
handoverDetails.add(
HandoverDetailDTO(
projectCode = projectCode,
Expand All @@ -1250,7 +1252,7 @@ class RbacPermissionManageFacadeServiceImpl(
)
)
}
invalidRepertoryIds.forEach { repertoryId ->
repertoryAuthorizations.forEach { repertoryId ->
handoverDetails.add(
HandoverDetailDTO(
projectCode = projectCode,
Expand All @@ -1260,7 +1262,7 @@ class RbacPermissionManageFacadeServiceImpl(
)
)
}
invalidEnvNodeIds.forEach { envNodeId ->
envNodeAuthorizations.forEach { envNodeId ->
handoverDetails.add(
HandoverDetailDTO(
projectCode = projectCode,
Expand Down Expand Up @@ -1438,9 +1440,9 @@ class RbacPermissionManageFacadeServiceImpl(
val handoverDetails = buildHandoverDetails(
projectCode = projectCode,
groupIds = toHandoverGroups.map { it.toString() },
invalidPipelines = invalidPipelines,
invalidRepertoryIds = invalidRepertoryIds,
invalidEnvNodeIds = invalidEnvNodeIds
pipelineAuthorizations = invalidPipelines,
repertoryAuthorizations = invalidRepertoryIds,
envNodeAuthorizations = invalidEnvNodeIds
)

val projectName = authResourceService.get(
Expand Down Expand Up @@ -1767,8 +1769,7 @@ class RbacPermissionManageFacadeServiceImpl(
logger.info("remove member from project $userId|$projectCode|$removeMemberFromProjectReq")
return with(removeMemberFromProjectReq) {
val memberType = targetMember.type
val isNeedToHandover = handoverTo != null
if (memberType == MemberType.USER.type && isNeedToHandover) {
if (memberType == MemberType.USER.type && isNeedToHandover()) {
removeMemberFromProjectReq.checkHandoverTo()
val handoverMemberDTO = GroupMemberHandoverConditionReq(
allSelection = true,
Expand Down Expand Up @@ -1892,7 +1893,7 @@ class RbacPermissionManageFacadeServiceImpl(
"content" to String.format(
request.handoverAction.emailContent,
request.flowNo,
overview.approver.plus("($handoverToCnName)"),
overview.approver.plus("($handoverToCnName)")
),
"weworkContent" to String.format(
request.handoverAction.weworkContent,
Expand Down Expand Up @@ -2132,6 +2133,143 @@ class RbacPermissionManageFacadeServiceImpl(
)
}

override fun checkMemberExitsProject(
projectCode: String,
userId: String
): MemberExitsProjectCheckVo {
logger.info("check member exits project:$projectCode|$userId")
val userDeptInfos = getMemberDeptInfos(
memberId = userId
)
val userDepartmentsInProject = authResourceGroupMemberDao.isMembersInProject(
dslContext = dslContext,
projectCode = projectCode,
memberNames = userDeptInfos,
memberType = MemberType.DEPARTMENT.type
).map { it.name }
if (userDepartmentsInProject.isNotEmpty()) {
return MemberExitsProjectCheckVo(
departmentJoinedCount = userDepartmentsInProject.size,
departments = userDepartmentsInProject.joinToString(",")
)
}
val resourceType2Authorizations = authAuthorizationDao.list(
dslContext = dslContext,
condition = ResourceAuthorizationConditionRequest(
projectCode = projectCode,
handoverFrom = userId
)
).groupBy { it.resourceType }
val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition(
projectCode = projectCode,
commonCondition = GroupMemberCommonConditionReq(
allSelection = true,
targetMember = ResourceMemberInfo(
id = userId,
type = MemberType.USER.type
)
)
)[MemberType.USER] ?: emptyList()
val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups(
dslContext = dslContext,
projectCode = projectCode,
iamGroupIds = groupIdsDirectlyJoined
)
return MemberExitsProjectCheckVo(
uniqueManagerCount = uniqueManagerGroups.size,
pipelineAuthorizationCount = resourceType2Authorizations[ResourceTypeId.PIPELINE]?.size,
repositoryAuthorizationCount = resourceType2Authorizations[ResourceTypeId.REPERTORY]?.size,
envNodeAuthorizationCount = resourceType2Authorizations[ResourceTypeId.ENV_NODE]?.size
)
}

override fun memberExitsProject(
projectCode: String,
request: RemoveMemberFromProjectReq
): String {
logger.info("member exits project :$projectCode|$request")
if (request.isNeedToHandover()) {
request.checkHandoverTo()
val handoverTo = request.handoverTo!!
// 需要交接的用户组
val groupIds = getGroupIdsByGroupMemberCondition(
projectCode = projectCode,
commonCondition = GroupMemberCommonConditionReq(
targetMember = request.targetMember,
allSelection = true
)
)[MemberType.get(MemberType.USER.type)]?.toMutableList() ?: emptyList()
// 需要交接的授权管理
val resourceAuthorizations = authAuthorizationDao.list(
dslContext = dslContext,
condition = ResourceAuthorizationConditionRequest(
projectCode = projectCode,
handoverFrom = request.targetMember.id
)
)
val resourceType2Authorizations = resourceAuthorizations.groupBy({ it.resourceType }, { it.resourceCode })
val repertoryAuthorizations = resourceType2Authorizations[ResourceTypeId.REPERTORY] ?: emptyList()
val pipelineAuthorizations = resourceType2Authorizations[ResourceTypeId.PIPELINE] ?: emptyList()
val envNodeRepertoryIds = resourceType2Authorizations[ResourceTypeId.ENV_NODE] ?: emptyList()
if (repertoryAuthorizations.isNotEmpty()) {
permissionAuthorizationService.checkRepertoryAuthorizationsHanover(
operator = request.targetMember.id,
projectCode = projectCode,
repertoryIds = repertoryAuthorizations,
handoverFrom = request.targetMember.id,
handoverTo = handoverTo.id
)
}
val handoverDetails = buildHandoverDetails(
projectCode = projectCode,
groupIds = groupIds.map { it.toString() },
pipelineAuthorizations = pipelineAuthorizations,
repertoryAuthorizations = repertoryAuthorizations,
envNodeAuthorizations = envNodeRepertoryIds
)
val projectName = authResourceService.get(
projectCode = projectCode,
resourceType = ResourceTypeId.PROJECT,
resourceCode = projectCode
).resourceName
// 创建交接单
val flowNo = permissionHandoverApplicationService.createHandoverApplication(
overview = HandoverOverviewCreateDTO(
projectCode = projectCode,
projectName = projectName,
applicant = request.targetMember.id,
approver = handoverTo.id,
handoverStatus = HandoverStatus.PENDING,
groupCount = groupIds.size,
authorizationCount = resourceAuthorizations.size
),
details = handoverDetails
)
return flowNo
} else {
val result = checkMemberExitsProject(
projectCode = projectCode,
userId = request.targetMember.id
)
if (!result.canExitsProjectDirectly()) {
throw OperationException(
message = "Direct exit from the project is not allowed!"
)
}
val removeMemberDTO = GroupMemberRemoveConditionReq(
allSelection = true,
targetMember = request.targetMember
)
batchOperateGroupMembers(
projectCode = projectCode,
type = BatchOperateType.REMOVE,
conditionReq = removeMemberDTO,
operateGroupMemberTask = ::deleteTask
)
}
return ""
}

private fun listGroupsOfHandoverPreview(queryReq: HandoverDetailsQueryReq): SQLPage<HandoverGroupDetailVo> {
val projectCode = queryReq.projectCode
val previewConditionReq = queryReq.previewConditionReq!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo
import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo
import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo
import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo
import com.tencent.devops.auth.pojo.vo.MemberExitsProjectCheckVo
import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo
import com.tencent.devops.auth.service.iam.PermissionManageFacadeService
import com.tencent.devops.common.api.model.SQLPage
Expand Down Expand Up @@ -174,4 +175,18 @@ class SamplePermissionManageFacadeService : PermissionManageFacadeService {
override fun isProjectMember(projectCode: String, userId: String): Boolean {
return true
}

override fun checkMemberExitsProject(
projectCode: String,
userId: String
): MemberExitsProjectCheckVo {
return MemberExitsProjectCheckVo()
}

override fun memberExitsProject(
projectCode: String,
request: RemoveMemberFromProjectReq
): String {
return ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo
import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo
import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo
import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo
import com.tencent.devops.auth.pojo.vo.MemberExitsProjectCheckVo
import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo
import com.tencent.devops.common.api.model.SQLPage

Expand Down Expand Up @@ -252,4 +253,20 @@ interface PermissionManageFacadeService {
projectCode: String,
userId: String
): Boolean

/**
* 用户主动退出项目检查
* */
fun checkMemberExitsProject(
projectCode: String,
userId: String
): MemberExitsProjectCheckVo

/**
* 用户主动退出项目
* */
fun memberExitsProject(
projectCode: String,
request: RemoveMemberFromProjectReq
): String
}

0 comments on commit 2829337

Please sign in to comment.