diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/sync/OpAuthResourceGroupSyncResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/sync/OpAuthResourceGroupSyncResource.kt index 6563ef2ea96..a907ba8fb87 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/sync/OpAuthResourceGroupSyncResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/sync/OpAuthResourceGroupSyncResource.kt @@ -100,4 +100,9 @@ interface OpAuthResourceGroupSyncResource { @PathParam(value = "projectId") projectId: String ): Result + + @POST + @Path("/syncIamGroupMembersOfApply") + @Operation(summary = "同步iam组成员--用户申请加入") + fun syncIamGroupMembersOfApply(): Result } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt index b9ade443a7d..aa0f259134d 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt @@ -106,6 +106,9 @@ interface UserAuthResourceResource { @Parameter(description = "资源ID") @PathParam("resourceCode") resourceCode: String, + @Parameter(description = "获取所有成员标识") + @QueryParam("allProjectMembersGroupFlag") + allProjectMembersGroupFlag: Boolean?, @Parameter(description = "第几页") @QueryParam("page") page: Int, diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/ApplyToGroupStatus.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/ApplyToGroupStatus.kt new file mode 100644 index 00000000000..e1aa282decd --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/ApplyToGroupStatus.kt @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.tencent.devops.auth.pojo.enum + +enum class ApplyToGroupStatus(val value: Int) { + // 审批中 + PENDING(0), + + // 审批成功 + SUCCEED(1), + + // 审批超时 + TIME_OUT(2); +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt index a58c07b74a0..c7734f9ee65 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt @@ -24,7 +24,7 @@ class AuthCronSyncGroupAndMember( private val logger = LoggerFactory.getLogger(AuthCronSyncGroupAndMember::class.java) } - @Scheduled(cron = "0 0 22 * * ?") + @Scheduled(cron = "0 0 0 6 * ?") fun syncGroupAndMemberRegularly() { if (!enable) { return @@ -44,4 +44,23 @@ class AuthCronSyncGroupAndMember( logger.warn("sync group and member regularly |error", e) } } + + @Scheduled(cron = "0 0 8,16 * * ?") + fun syncIamGroupMembersOfApplyRegularly() { + if (!enable) { + return + } + try { + logger.info("sync members of apply regularly | start") + val lockSuccess = redisLock.tryLock() + if (lockSuccess) { + permissionResourceGroupSyncService.syncIamGroupMembersOfApply() + logger.info("sync members of apply regularly | finish") + } else { + logger.info("sync members of apply regularly | running") + } + } catch (e: Exception) { + logger.warn("sync members of apply regularly | error", e) + } + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt new file mode 100644 index 00000000000..82fd8c51918 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt @@ -0,0 +1,64 @@ +package com.tencent.devops.auth.dao + +import com.tencent.devops.auth.pojo.ApplyJoinGroupInfo +import com.tencent.devops.auth.pojo.enum.ApplyToGroupStatus +import com.tencent.devops.model.auth.tables.TAuthResourceGroupApply +import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupApplyRecord +import org.jooq.DSLContext +import org.jooq.Result +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +class AuthResourceGroupApplyDao { + fun list( + dslContext: DSLContext, + limit: Int, + offset: Int + ): Result { + return with(TAuthResourceGroupApply.T_AUTH_RESOURCE_GROUP_APPLY) { + dslContext.selectFrom(this) + .where(STATUS.eq(ApplyToGroupStatus.PENDING.value)) + .orderBy(CREATE_TIME.asc()) + .offset(offset) + .limit(limit) + .fetch() + } + } + + fun batchUpdate( + dslContext: DSLContext, + ids: List, + applyToGroupStatus: ApplyToGroupStatus + ) { + with(TAuthResourceGroupApply.T_AUTH_RESOURCE_GROUP_APPLY) { + dslContext.batch( + ids.map { id -> + dslContext.update(this) + .set(STATUS, applyToGroupStatus.value) + .set(NUMBER_OF_CHECKS, NUMBER_OF_CHECKS + 1) + .set(UPDATE_TIME, LocalDateTime.now()) + .where(ID.eq(id)) + } + ).execute() + } + } + + fun batchCreate( + dslContext: DSLContext, + applyJoinGroupInfo: ApplyJoinGroupInfo + ) { + with(TAuthResourceGroupApply.T_AUTH_RESOURCE_GROUP_APPLY) { + dslContext.batch( + applyJoinGroupInfo.groupIds.map { groupId -> + dslContext.insertInto(this) + .set(PROJECT_CODE, applyJoinGroupInfo.projectCode) + .set(MEMBER_ID, applyJoinGroupInfo.applicant) + .set(IAM_GROUP_ID, groupId) + .set(STATUS, ApplyToGroupStatus.PENDING.value) + .set(NUMBER_OF_CHECKS, 0) + } + ).execute() + } + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt index aa5e79a6e84..f6dc9dd7779 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt @@ -44,6 +44,7 @@ import com.tencent.bk.sdk.iam.service.v2.impl.V2ManagerServiceImpl import com.tencent.bk.sdk.iam.service.v2.impl.V2PolicyServiceImpl import com.tencent.devops.auth.dao.AuthMigrationDao import com.tencent.devops.auth.dao.AuthMonitorSpaceDao +import com.tencent.devops.auth.dao.AuthResourceGroupApplyDao import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.dao.AuthResourceGroupMemberDao @@ -305,7 +306,8 @@ class RbacAuthConfiguration { authResourceCodeConverter: AuthResourceCodeConverter, permissionService: PermissionService, itsmService: ItsmService, - deptService: DeptService + deptService: DeptService, + authResourceGroupApplyDao: AuthResourceGroupApplyDao ) = RbacPermissionApplyService( dslContext = dslContext, v2ManagerService = v2ManagerService, @@ -318,7 +320,8 @@ class RbacAuthConfiguration { authResourceCodeConverter = authResourceCodeConverter, permissionService = permissionService, itsmService = itsmService, - deptService = deptService + deptService = deptService, + authResourceGroupApplyDao = authResourceGroupApplyDao ) @Bean @@ -578,7 +581,8 @@ class RbacAuthConfiguration { authResourceGroupMemberDao: AuthResourceGroupMemberDao, rbacCacheService: RbacCacheService, redisOperation: RedisOperation, - authResourceSyncDao: AuthResourceSyncDao + authResourceSyncDao: AuthResourceSyncDao, + authResourceGroupApplyDao: AuthResourceGroupApplyDao ) = RbacPermissionResourceGroupSyncService( client = client, dslContext = dslContext, @@ -588,6 +592,7 @@ class RbacAuthConfiguration { authResourceGroupMemberDao = authResourceGroupMemberDao, rbacCacheService = rbacCacheService, redisOperation = redisOperation, - authResourceSyncDao = authResourceSyncDao + authResourceSyncDao = authResourceSyncDao, + authResourceGroupApplyDao = authResourceGroupApplyDao ) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt index 7897c7b490c..7e670bf6409 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt @@ -11,6 +11,7 @@ import com.tencent.devops.auth.constant.AuthI18nConstants import com.tencent.devops.auth.constant.AuthI18nConstants.ACTION_NAME_SUFFIX import com.tencent.devops.auth.constant.AuthI18nConstants.AUTH_RESOURCE_GROUP_CONFIG_GROUP_NAME_SUFFIX import com.tencent.devops.auth.constant.AuthMessageCode +import com.tencent.devops.auth.dao.AuthResourceGroupApplyDao import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.pojo.ApplyJoinGroupFormDataInfo @@ -64,7 +65,8 @@ class RbacPermissionApplyService @Autowired constructor( val authResourceCodeConverter: AuthResourceCodeConverter, val permissionService: PermissionService, val itsmService: ItsmService, - val deptService: DeptService + val deptService: DeptService, + val authResourceGroupApplyDao: AuthResourceGroupApplyDao ) : PermissionApplyService { @Value("\${auth.iamSystem:}") private val systemId = "" @@ -348,6 +350,11 @@ class RbacPermissionApplyService @Autowired constructor( .reason(applyJoinGroupInfo.reason).build() logger.info("apply to join group: iamApplicationDTO=$iamApplicationDTO") v2ManagerService.createRoleGroupApplicationV2(iamApplicationDTO) + // 记录单据,用于同步用户组 + authResourceGroupApplyDao.batchCreate( + dslContext = dslContext, + applyJoinGroupInfo = applyJoinGroupInfo + ) } catch (e: Exception) { throw ErrorCodeException( errorCode = AuthMessageCode.APPLY_TO_JOIN_GROUP_FAIL, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt index 9d88a0d9972..7c82422b684 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt @@ -32,11 +32,13 @@ import com.tencent.bk.sdk.iam.dto.V2PageInfoDTO import com.tencent.bk.sdk.iam.dto.manager.dto.SearchGroupDTO import com.tencent.bk.sdk.iam.exception.IamException import com.tencent.bk.sdk.iam.service.v2.V2ManagerService +import com.tencent.devops.auth.dao.AuthResourceGroupApplyDao import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.dao.AuthResourceGroupMemberDao import com.tencent.devops.auth.dao.AuthResourceSyncDao import com.tencent.devops.auth.pojo.AuthResourceGroup import com.tencent.devops.auth.pojo.AuthResourceGroupMember +import com.tencent.devops.auth.pojo.enum.ApplyToGroupStatus import com.tencent.devops.auth.pojo.enum.AuthMigrateStatus import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService import com.tencent.devops.auth.service.lock.SyncGroupAndMemberLock @@ -69,13 +71,15 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( private val authResourceGroupMemberDao: AuthResourceGroupMemberDao, private val rbacCacheService: RbacCacheService, private val redisOperation: RedisOperation, - private val authResourceSyncDao: AuthResourceSyncDao + private val authResourceSyncDao: AuthResourceSyncDao, + private val authResourceGroupApplyDao: AuthResourceGroupApplyDao ) : PermissionResourceGroupSyncService { companion object { private val logger = LoggerFactory.getLogger(RbacPermissionResourceGroupSyncService::class.java) private val syncExecutorService = Executors.newFixedThreadPool(5) private val syncProjectsExecutorService = Executors.newFixedThreadPool(10) private val syncResourceMemberExecutorService = Executors.newFixedThreadPool(50) + private const val MAX_NUMBER_OF_CHECKS = 120 } override fun syncByCondition(projectConditionDTO: ProjectConditionDTO) { @@ -164,6 +168,68 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( ) } + override fun syncIamGroupMembersOfApply() { + val traceId = MDC.get(TraceTag.BIZID) + syncExecutorService.submit { + MDC.put(TraceTag.BIZID, traceId) + val limit = 100 + var offset = 0 + val startEpoch = System.currentTimeMillis() + do { + logger.info("sync members of apply | start") + val records = authResourceGroupApplyDao.list( + dslContext = dslContext, + limit = limit, + offset = offset + ) + val recordIdsOfTimeOut = records.filter { it.numberOfChecks >= MAX_NUMBER_OF_CHECKS }.map { it.id } + val (recordsOfSuccess, recordsOfPending) = records.filterNot { + recordIdsOfTimeOut.contains(it.id) + }.partition { + try { + val isMemberJoinedToGroup = iamV2ManagerService.verifyGroupValidMember( + it.memberId, + it.iamGroupId.toString() + )[it.iamGroupId]?.belong == true + isMemberJoinedToGroup + } catch (ignore: Exception) { + logger.warn("verify group valid member failed,${it.memberId}|${it.iamGroupId}", ignore) + false + } + } + if (recordIdsOfTimeOut.isNotEmpty()) { + authResourceGroupApplyDao.batchUpdate( + dslContext = dslContext, + ids = recordIdsOfTimeOut, + applyToGroupStatus = ApplyToGroupStatus.TIME_OUT + ) + } + if (recordsOfPending.isNotEmpty()) { + authResourceGroupApplyDao.batchUpdate( + dslContext = dslContext, + ids = recordsOfPending.map { it.id }, + applyToGroupStatus = ApplyToGroupStatus.PENDING + ) + } + if (recordsOfSuccess.isNotEmpty()) { + recordsOfSuccess.forEach { + syncIamGroupMember( + projectCode = it.projectCode, + iamGroupId = it.iamGroupId + ) + } + authResourceGroupApplyDao.batchUpdate( + dslContext = dslContext, + ids = recordsOfSuccess.map { it.id }, + applyToGroupStatus = ApplyToGroupStatus.SUCCEED + ) + } + offset += limit + } while (records.size == limit) + logger.info("It take(${System.currentTimeMillis() - startEpoch})ms to sync members of apply") + } + } + override fun syncGroupAndMember(projectCode: String) { val traceId = MDC.get(TraceTag.BIZID) syncProjectsExecutorService.submit { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt index 1dafe76f592..eceb19c8a3c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt @@ -49,5 +49,7 @@ class SamplePermissionResourceGroupSyncService : PermissionResourceGroupSyncServ override fun syncIamGroupMember(projectCode: String, iamGroupId: Int) = Unit + override fun syncIamGroupMembersOfApply() = Unit + override fun fixResourceGroupMember(projectCode: String) = Unit } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt index 55f652d09ba..40cccbbb9ba 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt @@ -72,4 +72,9 @@ class OpAuthResourceGroupSyncResourceImpl @Autowired constructor( permissionResourceGroupSyncService.fixResourceGroupMember(projectId) return Result(true) } + + override fun syncIamGroupMembersOfApply(): Result { + permissionResourceGroupSyncService.syncIamGroupMembersOfApply() + return Result(true) + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt index c99eada828e..5ee46656fe5 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt @@ -84,6 +84,7 @@ class UserAuthResourceResourceImpl @Autowired constructor( projectId: String, resourceType: String, resourceCode: String, + allProjectMembersGroupFlag: Boolean?, page: Int, pageSize: Int ): Result> { @@ -94,7 +95,7 @@ class UserAuthResourceResourceImpl @Autowired constructor( projectId = projectId, resourceType = resourceType, resourceCode = resourceCode, - getAllProjectMembersGroup = true, + getAllProjectMembersGroup = allProjectMembersGroupFlag ?: true, page = page, pageSize = pageSize ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt index 34013381960..c9e5f5c8157 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt @@ -74,6 +74,11 @@ interface PermissionResourceGroupSyncService { */ fun syncIamGroupMember(projectCode: String, iamGroupId: Int) + /** + * 同步iam组成员--用户申请加入 + */ + fun syncIamGroupMembersOfApply() + /** * 防止出现用户组表的数据已经删了,但是用户组成员表的数据未删除,导致出现不同步,调用iam接口报错问题。 */ diff --git a/support-files/sql/1001_ci_auth_ddl_mysql.sql b/support-files/sql/1001_ci_auth_ddl_mysql.sql index 9d591fbbfa1..83f1a550c0b 100644 --- a/support-files/sql/1001_ci_auth_ddl_mysql.sql +++ b/support-files/sql/1001_ci_auth_ddl_mysql.sql @@ -405,4 +405,18 @@ CREATE TABLE IF NOT EXISTS `T_AUTH_RESOURCE_SYNC` PRIMARY KEY (`PROJECT_CODE`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='同步IAM资源'; +CREATE TABLE IF NOT EXISTS `T_AUTH_RESOURCE_GROUP_APPLY` +( + `ID` bigint auto_increment comment '主键ID', + `PROJECT_CODE` varchar(64) not null comment '项目ID', + `MEMBER_ID` varchar(64) not null comment '成员ID', + `IAM_GROUP_ID` int(20) not null comment 'IAM组ID', + `STATUS` int(2) default 0 null comment '状态, 0-审批中,1-审批成功,2-审批超时', + `NUMBER_OF_CHECKS` int(10) default 0 null comment '检查次数,用于同步组数据', + `CREATE_TIME` datetime default CURRENT_TIMESTAMP not null comment '创建时间', + `UPDATE_TIME` datetime default CURRENT_TIMESTAMP not null comment '更新时间', + PRIMARY KEY (`ID`), + INDEX `IDX_STATUS` (`STATUS`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='用户组申请记录表'; + SET FOREIGN_KEY_CHECKS = 1;