function id(value) { return value == null ? '' : String(value) } export async function run(context) { const module = 'system' const suffix = context.marker.replace(/\D/g, '').slice(-12) const permissionsResponse = await context.request('GET', '/api/platform/v1/iam/permissions') const permissions = context.apiSuccess(module, '查询菜单权限资源', permissionsResponse) context.assert(module, '权限资源返回数组', permissionsResponse, Array.isArray(permissions), `count=${Array.isArray(permissions) ? permissions.length : 'n/a'}`) const permissionPayload = { type: 'BUTTON', code: `platform:acceptance:${suffix}`, name: `Admin验收权限-${suffix}`, parentId: null, routePath: null, httpMethod: null, sortNo: 9999, } const createPermissionResponse = await context.request('POST', '/api/platform/v1/iam/permissions', permissionPayload) const permission = context.apiSuccess(module, '创建自定义权限资源', createPermissionResponse) const permissionId = id(permission?.id) context.assert(module, '权限创建字段正确', createPermissionResponse, permissionId && permission?.permissionCode === permissionPayload.code && permission?.builtIn === false, `permissionId=${permissionId || 'missing'}`) if (!permissionId) throw new Error('权限资源创建失败,系统模块无法继续') context.createdResources.systemPermission = { id: permissionId, code: permissionPayload.code } const additionalPermissionIds = [] for (const definition of [ { type: 'MENU', routePath: `/acceptance-${suffix}`, httpMethod: null }, { type: 'PAGE', routePath: `/acceptance-${suffix}/detail`, httpMethod: null }, { type: 'API', routePath: `/api/platform/v1/acceptance/${suffix}`, httpMethod: 'GET' }, ]) { const payload = { type: definition.type, code: `platform:acceptance:${definition.type.toLowerCase()}:${suffix}`, name: `Admin验收${definition.type}-${suffix}`, parentId: null, routePath: definition.routePath, httpMethod: definition.httpMethod, sortNo: 9999, } const response = await context.request('POST', '/api/platform/v1/iam/permissions', payload) const created = context.apiSuccess(module, `创建 ${definition.type} 权限资源`, response) const createdId = id(created?.id) context.assert(module, `${definition.type} 类型与端点字段正确回显`, response, createdId && created?.permissionType === definition.type && created?.routePath === definition.routePath && (created?.httpMethod ?? null) === definition.httpMethod, `permissionId=${createdId || 'missing'}, type=${created?.permissionType || 'missing'}, method=${created?.httpMethod ?? 'null'}`) if (createdId) additionalPermissionIds.push(createdId) } const updatePermissionResponse = await context.request('PUT', `/api/platform/v1/iam/permissions/${permissionId}`, { name: `${permissionPayload.name}-已编辑`, parentId: null, routePath: null, httpMethod: null, sortNo: 9998, }) const updatedPermission = context.apiSuccess(module, '编辑自定义权限资源', updatePermissionResponse) context.assert(module, '权限编辑正确回显', updatePermissionResponse, updatedPermission?.permissionName === `${permissionPayload.name}-已编辑` && Number(updatedPermission?.sortNo) === 9998, `permissionId=${permissionId}`) for (const status of ['DISABLED', 'ACTIVE']) { const response = await context.request('PUT', `/api/platform/v1/iam/permissions/${permissionId}/status`, { status }) const changed = context.apiSuccess(module, `${status === 'ACTIVE' ? '启用' : '停用'}自定义权限`, response) context.assert(module, `权限状态回显 ${status}`, response, changed?.status === status, `status=${changed?.status ?? 'missing'}`) } const rolePayload = { roleCode: `ADMIN_ACCEPT_${suffix}`, roleName: `Admin验收角色-${suffix}`, description: context.marker } const createRoleResponse = await context.request('POST', '/api/platform/v1/iam/roles', rolePayload) const role = context.apiSuccess(module, '创建平台自定义角色', createRoleResponse) const roleId = id(role?.id) context.assert(module, '角色创建字段正确', createRoleResponse, roleId && role?.roleCode === rolePayload.roleCode && role?.roleType === 'CUSTOM', `roleId=${roleId || 'missing'}`) if (!roleId) throw new Error('角色创建失败,系统模块无法继续') context.createdResources.systemRole = { id: roleId, code: rolePayload.roleCode, finalStatus: 'DISABLED', retainedBecauseAssigned: true } const editRoleResponse = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}`, { roleName: `${rolePayload.roleName}-已编辑`, description: `${context.marker}-EDITED`, }) const editedRole = context.apiSuccess(module, '编辑平台自定义角色', editRoleResponse) context.assert(module, '角色编辑正确回显', editRoleResponse, editedRole?.roleName === `${rolePayload.roleName}-已编辑`, `roleId=${roleId}`) const grantResponse = await context.request('PUT', '/api/platform/v1/iam/roles/permissions', { roleId, permissionIds: [permissionId] }) context.apiSuccess(module, '角色全量保存权限', grantResponse) const rolesResponse = await context.request('GET', '/api/platform/v1/iam/roles') const roles = context.apiSuccess(module, '回查平台角色列表', rolesResponse) const listedRole = roles?.find?.((item) => id(item.id) === roleId) context.assert(module, '角色权限 ID 正确回显', rolesResponse, listedRole && listedRole.permissionIds?.map(id).includes(permissionId), `permissionIds=${listedRole?.permissionIds?.map(id).join(',') || 'missing'}`) for (const status of ['DISABLED', 'ACTIVE']) { const response = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}/status`, { status }) const changed = context.apiSuccess(module, `${status === 'ACTIVE' ? '启用' : '停用'}自定义角色`, response) context.assert(module, `角色状态回显 ${status}`, response, changed?.status === status, `status=${changed?.status ?? 'missing'}`) } const optionsResponse = await context.request('GET', '/api/platform/v1/iam/users/options') const options = context.apiSuccess(module, '查询内部用户可选平台角色', optionsResponse) context.assert(module, '用户选项包含平台与测试角色', optionsResponse, options?.tenants?.some((item) => id(item.id) === '0') && options?.roles?.some((item) => id(item.id) === roleId), `tenants=${options?.tenants?.length ?? 'n/a'}, roles=${options?.roles?.length ?? 'n/a'}`) const phone = await context.findAvailablePhone(module) const username = `admin_accept_${suffix}` const initialPassword = `Accept${suffix}A1` const resetPassword = `Reset${suffix}B2` const userPayload = { username, displayName: `Admin验收用户-${suffix}`, phone, email: `${username}@example.com`, tenantId: '0', roleIds: [roleId], password: initialPassword, status: 'ACTIVE', } const createUserResponse = await context.request('POST', '/api/platform/v1/iam/users', userPayload) const user = context.apiSuccess(module, '创建平台内部用户', createUserResponse) const membershipId = id(user?.membershipId) context.assert(module, '内部用户账号、平台范围和角色正确', createUserResponse, membershipId && user?.username === username && id(user?.tenantId) === '0' && user?.roleIds?.map(id).includes(roleId), `membershipId=${membershipId || 'missing'}`) if (!membershipId) throw new Error('平台内部用户创建失败,系统模块无法继续') context.createdResources.systemUser = { membershipId, username, finalStatus: 'DISABLED' } const pageResponse = await context.request('GET', `/api/platform/v1/iam/users?page=1&size=20&keyword=${username}`) const page = context.apiSuccess(module, '按账号查询平台内部用户', pageResponse) const listedUser = page?.records?.find((item) => id(item.membershipId) === membershipId) const missingUserFields = listedUser ? requiredUserFields().filter((field) => !Object.hasOwn(listedUser, field)) : requiredUserFields() context.assert(module, '用户列表包含新账号且字段完整', pageResponse, listedUser && missingUserFields.length === 0 && (!Object.hasOwn(listedUser, 'lastLoginAt') || typeof listedUser.lastLoginAt === 'string'), `records=${page?.records?.length ?? 'n/a'}, missing=${missingUserFields.join(',') || 'none'}, lastLoginAt=${listedUser?.lastLoginAt ?? 'omitted'}`) const updateUserPayload = { displayName: `${userPayload.displayName}-已编辑`, phone, email: `edited-${userPayload.email}`, tenantId: '0', roleIds: [roleId], } const updateUserResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}`, updateUserPayload) const updatedUser = context.apiSuccess(module, '编辑平台内部用户', updateUserResponse) context.assert(module, '用户编辑正确回显', updateUserResponse, updatedUser?.displayName === updateUserPayload.displayName && updatedUser?.email === updateUserPayload.email, `membershipId=${membershipId}`) const loginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: initialPassword }, '') const loginPassed = loginResponse.http === 200 && String(loginResponse.body?.code) === '200' && Boolean(loginResponse.body?.data?.accessToken) context.addStep(module, '新建内部用户可使用初始密码登录', loginResponse, loginPassed, `username=${username}`) const resetResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}/password`, { newPassword: resetPassword }) context.apiSuccess(module, '重置平台内部用户密码', resetResponse) const resetLoginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: resetPassword }, '') const resetLoginPassed = resetLoginResponse.http === 200 && String(resetLoginResponse.body?.code) === '200' && Boolean(resetLoginResponse.body?.data?.accessToken) context.addStep(module, '内部用户使用重置密码登录', resetLoginResponse, resetLoginPassed, `username=${username}`) if (context.options.modules.includes('phase4-cross-region-events')) { const restrictedOverseasToken = resetLoginResponse.body?.data?.accessToken || '' context.assert(module, '受限角色登录同时签发统一Token', resetLoginResponse, Boolean(restrictedOverseasToken), `tokenPresent=${Boolean(restrictedOverseasToken)}`, { scenario: 'canonical-overseas-permission-denial', deploymentBlocker: true }) if (restrictedOverseasToken) { const denied = await context.request('GET', '/api/platform/v1/last-leg/orders?page=1&size=1', undefined, restrictedOverseasToken, {}) context.assert(module, '缺少canonical海外订单权限必须403', denied, (denied.http === 403 || ['403', 'A0403'].includes(String(denied.body?.code))) && Boolean(denied.requestId) && Boolean(denied.traceId), `HTTP=${denied.http}, code=${denied.body?.code ?? 'missing'}, requestId=${denied.requestId || 'missing'}, traceId=${denied.traceId || 'missing'}`, { scenario: 'canonical-overseas-permission-denial', deploymentBlocker: true }) } } const disableUserResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}/status`, { status: 'DISABLED' }) const disabledUser = context.apiSuccess(module, '停用平台内部用户', disableUserResponse) context.assert(module, '内部用户停用状态正确', disableUserResponse, disabledUser?.status === 'DISABLED', `status=${disabledUser?.status ?? 'missing'}`) const disabledLoginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: resetPassword }, '') context.addStep(module, '停用用户禁止登录', disabledLoginResponse, disabledLoginResponse.http === 200 && String(disabledLoginResponse.body?.code) !== '200', `HTTP=${disabledLoginResponse.http}, code=${disabledLoginResponse.body?.code ?? 'missing'}`) const clearPermissionResponse = await context.request('PUT', '/api/platform/v1/iam/roles/permissions', { roleId, permissionIds: [] }) context.apiSuccess(module, '清空测试角色权限', clearPermissionResponse) const deleteRoleResponse = await context.request('DELETE', `/api/platform/v1/iam/roles/${roleId}`) context.expectConflict(module, '已分配成员的角色禁止删除', deleteRoleResponse, 'system-role-protection') const finalDisableRoleResponse = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}/status`, { status: 'DISABLED' }) const finalRole = context.apiSuccess(module, '清理时停用测试角色', finalDisableRoleResponse) context.assert(module, '保留测试角色处于停用状态', finalDisableRoleResponse, finalRole?.status === 'DISABLED', `status=${finalRole?.status ?? 'missing'}`) const deletePermissionResponse = await context.request('DELETE', `/api/platform/v1/iam/permissions/${permissionId}`) context.apiSuccess(module, '删除临时权限资源', deletePermissionResponse) for (const additionalPermissionId of additionalPermissionIds) { const response = await context.request('DELETE', `/api/platform/v1/iam/permissions/${additionalPermissionId}`) context.apiSuccess(module, '删除四类型验收权限资源', response, `permissionId=${additionalPermissionId}`) } } function requiredUserFields() { return ['membershipId', 'userId', 'authUserId', 'username', 'displayName', 'phone', 'email', 'tenantId', 'tenantName', 'memberType', 'status', 'roleIds', 'roleNames', 'protectedAccount'] }