暂存提交
This commit is contained in:
@@ -14,14 +14,22 @@ public interface AuthAccountClient {
|
||||
@PathVariable Long id);
|
||||
@PutMapping("/{id}/status") ApiResponse<Void> status(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@PathVariable Long id, @RequestBody StatusRequest request);
|
||||
@PutMapping("/{id}/profile") ApiResponse<Void> profile(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@PathVariable Long id, @RequestBody ProfileRequest request);
|
||||
@PutMapping("/{id}/password") ApiResponse<Void> reset(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@PathVariable Long id, @RequestBody PasswordRequest request);
|
||||
@DeleteMapping("/{id}/compensation") ApiResponse<Void> compensate(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@PathVariable Long id);
|
||||
|
||||
record CreateAccountRequest(Long accountId, String username, String password, String displayName,
|
||||
Long tenantId, Long membershipId) { }
|
||||
Long tenantId, Long membershipId, String appCode) {
|
||||
public CreateAccountRequest(Long accountId, String username, String password, String displayName,
|
||||
Long tenantId, Long membershipId) {
|
||||
this(accountId, username, password, displayName, tenantId, membershipId, "MERCHANT");
|
||||
}
|
||||
}
|
||||
record AccountSummary(Long accountId, String username, String displayName, String status) { }
|
||||
record StatusRequest(String status) { }
|
||||
record ProfileRequest(String displayName) { }
|
||||
record PasswordRequest(String newPassword) { }
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class MerchantTenantController {
|
||||
}
|
||||
|
||||
@GetMapping("/tenant/profile")
|
||||
@PreAuthorize("hasAuthority('merchant:profile:view')")
|
||||
@Operation(summary = "Get current enterprise profile / 查询当前企业资料", description = "Tenant ID is taken only from the authenticated merchant JWT. / 租户 ID 仅从已认证商户 JWT 获取。")
|
||||
@ApiResponses({@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Profile returned / 查询成功"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "Tenant not found / 企业不存在")})
|
||||
public ApiResponse<TenantResponse> profile(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.baiyue.iam.controller;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.iam.dto.TenantDtos.RegisterRequest;
|
||||
import com.baiyue.iam.dto.TenantDtos.ReviewRequest;
|
||||
@@ -13,9 +14,12 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
@@ -25,11 +29,30 @@ import java.util.List;
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/iam")
|
||||
@Validated
|
||||
@Tag(name = "Platform tenant administration / 平台企业管理", description = "Enterprise registration review and tenant activation for platform operators. / 平台运营人员进行企业注册审核和租户启用。")
|
||||
public class PlatformTenantController {
|
||||
@Resource
|
||||
private TenantService service;
|
||||
|
||||
@GetMapping("/tenants")
|
||||
@PreAuthorize("hasAuthority('platform:customer:list')")
|
||||
@Operation(summary = "List enterprise tenants / 查询企业列表", description = "Returns paged merchant enterprise tenants for platform operators. / 平台端分页查询商户企业,支持企业名称、统一社会信用代码、联系人和状态筛选。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Tenant list returned / 企业列表查询成功"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Invalid paging parameters / 分页参数无效"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Platform scope required / 需要平台端权限")
|
||||
})
|
||||
public ApiResponse<PageResult<TenantResponse>> page(
|
||||
@RequestParam(defaultValue = "1") @Min(1) long page,
|
||||
@RequestParam(defaultValue = "20") @Min(1) @Max(200) long size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String status,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
return ApiResponse.success(service.pageMerchants(page, size, keyword, status));
|
||||
}
|
||||
|
||||
@PostMapping("/tenants")
|
||||
@PreAuthorize("hasAuthority('platform:customer:create')")
|
||||
@Operation(summary = "Register enterprise tenant / 提交企业注册", description = "Create a pending merchant tenant and its registration review ticket. Platform token required. / 创建待审核商户租户及注册审核单,需要平台端令牌。")
|
||||
|
||||
@@ -3,12 +3,11 @@ package com.baiyue.iam.controller;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.iam.dto.ProfileDtos.AddressRequest;
|
||||
import com.baiyue.iam.dto.ProfileDtos.CreateUserRequest;
|
||||
import com.baiyue.iam.dto.ProfileDtos.QualificationRequest;
|
||||
import com.baiyue.iam.dto.PlatformInternalUserDtos.*;
|
||||
import com.baiyue.iam.entity.IamTenantAddress;
|
||||
import com.baiyue.iam.entity.IamTenantQualification;
|
||||
import com.baiyue.iam.entity.IamUser;
|
||||
import com.baiyue.iam.service.ProfileResourceService;
|
||||
import com.baiyue.iam.service.PlatformInternalUserService;
|
||||
import com.baiyue.iam.service.TenantGuard;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
@@ -29,23 +28,57 @@ import java.util.List;
|
||||
public class ProfileResourceController {
|
||||
@Resource
|
||||
private ProfileResourceService service;
|
||||
@Resource
|
||||
private PlatformInternalUserService internalUserService;
|
||||
|
||||
@GetMapping("/api/platform/v1/iam/internal-users")
|
||||
@PreAuthorize("hasAuthority('platform:user:list')")
|
||||
@Operation(summary = "List platform internal users / 查询平台内部用户", description = "Platform scope is required. / 需要平台端权限域。")
|
||||
@ApiResponses({@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "User list returned / 查询成功"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "Platform scope required / 需要平台端权限域")})
|
||||
public ApiResponse<List<IamUser>> internalUsers(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
public ApiResponse<List<InternalUserResponse>> internalUsers(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
return ApiResponse.success(service.internalUsers());
|
||||
return ApiResponse.success(internalUserService.list());
|
||||
}
|
||||
|
||||
@PostMapping("/api/platform/v1/iam/internal-users")
|
||||
@PreAuthorize("hasAuthority('platform:user:create')")
|
||||
@Operation(summary = "Create platform internal user / 新增平台内部用户", description = "Create an IAM profile linked to an auth-service account. / 创建关联统一认证账号的 IAM 用户资料。")
|
||||
@ApiResponses({@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "User created / 创建成功"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Invalid user profile / 用户资料不合法")})
|
||||
public ApiResponse<IamUser> addUser(@Valid @RequestBody CreateUserRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
public ApiResponse<InternalUserResponse> addUser(@Valid @RequestBody CreateInternalUserRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
return ApiResponse.success(service.addUser(request));
|
||||
return ApiResponse.success(internalUserService.create(request));
|
||||
}
|
||||
|
||||
@PutMapping("/api/platform/v1/iam/internal-users/{membershipId:\\d+}")
|
||||
@PreAuthorize("hasAuthority('platform:user:edit')")
|
||||
@Operation(summary = "编辑平台内部用户")
|
||||
public ApiResponse<InternalUserResponse> updateInternalUser(
|
||||
@PathVariable Long membershipId, @Valid @RequestBody UpdateInternalUserRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
return ApiResponse.success(internalUserService.update(membershipId, request));
|
||||
}
|
||||
|
||||
@PutMapping("/api/platform/v1/iam/internal-users/{membershipId:\\d+}/status")
|
||||
@PreAuthorize("hasAuthority('platform:user:status')")
|
||||
@Operation(summary = "启用或停用平台内部用户")
|
||||
public ApiResponse<InternalUserResponse> changeInternalUserStatus(
|
||||
@PathVariable Long membershipId, @Valid @RequestBody ChangeInternalUserStatusRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
return ApiResponse.success(internalUserService.changeStatus(membershipId, request, user.membershipId()));
|
||||
}
|
||||
|
||||
@PutMapping("/api/platform/v1/iam/internal-users/{membershipId:\\d+}/password")
|
||||
@PreAuthorize("hasAuthority('platform:user:reset-password')")
|
||||
@Operation(summary = "重置平台内部用户密码")
|
||||
public ApiResponse<Void> resetInternalUserPassword(
|
||||
@PathVariable Long membershipId, @Valid @RequestBody ResetInternalUserPasswordRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
TenantGuard.tenant(user, "PLATFORM");
|
||||
internalUserService.resetPassword(membershipId, request);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/api/merchant/v1/iam/tenant/addresses")
|
||||
@@ -79,12 +112,4 @@ public class ProfileResourceController {
|
||||
return ApiResponse.success(service.qualifications(tenantId));
|
||||
}
|
||||
|
||||
@PostMapping("/api/merchant/v1/iam/tenant/qualifications")
|
||||
@PreAuthorize("hasAuthority('merchant:qualification:upload')")
|
||||
@Operation(summary = "Submit enterprise qualification / 提交企业资质", description = "Create a pending qualification record; document content is stored externally. / 创建待审核资质记录,文件内容由外部对象存储保存。")
|
||||
@ApiResponses({@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Qualification submitted / 提交成功"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Invalid qualification / 资质信息不合法")})
|
||||
public ApiResponse<IamTenantQualification> addQualification(@Valid @RequestBody QualificationRequest request,
|
||||
@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
return ApiResponse.success(service.addQualification(TenantGuard.tenant(user, "MERCHANT"), request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,12 @@ package com.baiyue.iam.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/** 用户档案、地址和资质的数据契约。 */
|
||||
public final class ProfileDtos {
|
||||
private ProfileDtos() { }
|
||||
|
||||
public record CreateUserRequest(@NotNull Long authUserId, @NotBlank String userType,
|
||||
@NotBlank String displayName, String phone, String email) { }
|
||||
|
||||
public record PersonalProfileResponse(Long userId, Long authUserId, String username,
|
||||
String displayName, String phone, String email) { }
|
||||
|
||||
@@ -25,6 +19,4 @@ public final class ProfileDtos {
|
||||
String city, String district, @NotBlank String detail,
|
||||
String postalCode, Boolean isDefault) { }
|
||||
|
||||
public record QualificationRequest(@NotBlank String qualificationType, String certificateNo,
|
||||
@NotBlank String documentUrl, LocalDate validFrom, LocalDate validTo) { }
|
||||
}
|
||||
|
||||
@@ -27,13 +27,11 @@ public class ProfileResourceRepository {
|
||||
this.qualificationMapper = qualificationMapper;
|
||||
}
|
||||
|
||||
public void insertUser(IamUser user) { userMapper.insert(user); }
|
||||
public IamUser findUserByAuthId(Long authUserId) { return userMapper.selectOne(new LambdaQueryWrapper<IamUser>().eq(IamUser::getAuthUserId, authUserId).last("LIMIT 1")); }
|
||||
public void saveUser(IamUser user) { userMapper.updateById(user); }
|
||||
public void insertAddress(IamTenantAddress address) { addressMapper.insert(address); }
|
||||
public void insertQualification(IamTenantQualification qualification) { qualificationMapper.insert(qualification); }
|
||||
public void saveQualification(IamTenantQualification qualification) { qualificationMapper.updateById(qualification); }
|
||||
public List<IamUser> findInternalUsers() { return userMapper.selectList(new LambdaQueryWrapper<IamUser>().eq(IamUser::getUserType, "INTERNAL")); }
|
||||
public List<IamTenantAddress> findAddresses(long tenantId) { return addressMapper.selectList(new LambdaQueryWrapper<IamTenantAddress>().eq(IamTenantAddress::getTenantId, tenantId)); }
|
||||
public List<IamTenantQualification> findQualifications(long tenantId) { return qualificationMapper.selectList(new LambdaQueryWrapper<IamTenantQualification>().eq(IamTenantQualification::getTenantId, tenantId)); }
|
||||
public long countApprovedQualifications(long tenantId) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.baiyue.iam.repository;
|
||||
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.iam.entity.IamTenant;
|
||||
import com.baiyue.iam.entity.IamTenantReview;
|
||||
import com.baiyue.iam.mapper.IamTenantMapper;
|
||||
import com.baiyue.iam.mapper.IamTenantReviewMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -45,4 +48,21 @@ public class TenantRepository {
|
||||
.eq(IamTenantReview::getStatus, "PENDING")
|
||||
.orderByAsc(IamTenantReview::getId));
|
||||
}
|
||||
|
||||
public PageResult<IamTenant> pageMerchants(long page, long size, String keyword, String status) {
|
||||
LambdaQueryWrapper<IamTenant> wrapper = new LambdaQueryWrapper<IamTenant>()
|
||||
.eq(IamTenant::getTenantType, "MERCHANT")
|
||||
.eq(StringUtils.hasText(status), IamTenant::getStatus, status)
|
||||
.orderByDesc(IamTenant::getCreatedAt);
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.and(condition -> condition
|
||||
.like(IamTenant::getTenantCode, keyword)
|
||||
.or().like(IamTenant::getTenantName, keyword)
|
||||
.or().like(IamTenant::getLegalName, keyword)
|
||||
.or().like(IamTenant::getUnifiedSocialCreditCode, keyword)
|
||||
.or().like(IamTenant::getContactName, keyword)
|
||||
.or().like(IamTenant::getContactPhone, keyword));
|
||||
}
|
||||
return PageResult.from(tenantMapper.selectPage(new Page<>(page, size), wrapper));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,10 @@ import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.iam.dto.OnboardingDtos.QualificationSubmissionRequest;
|
||||
import com.baiyue.iam.dto.OnboardingDtos.QualificationSubmissionResponse;
|
||||
import com.baiyue.iam.dto.ProfileDtos.AddressRequest;
|
||||
import com.baiyue.iam.dto.ProfileDtos.CreateUserRequest;
|
||||
import com.baiyue.iam.dto.ProfileDtos.QualificationRequest;
|
||||
import com.baiyue.iam.entity.IamTenant;
|
||||
import com.baiyue.iam.entity.IamTenantAddress;
|
||||
import com.baiyue.iam.entity.IamTenantQualification;
|
||||
import com.baiyue.iam.entity.IamTenantReview;
|
||||
import com.baiyue.iam.entity.IamUser;
|
||||
import com.baiyue.iam.repository.ProfileResourceRepository;
|
||||
import com.baiyue.iam.repository.TenantRepository;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@@ -29,16 +26,6 @@ public class ProfileResourceService {
|
||||
@Resource private ProfileResourceRepository repository;
|
||||
@Resource private TenantRepository tenantRepository;
|
||||
|
||||
@BusinessAudit(event = "INTERNAL_USER_CREATE", resourceType = "IAM_USER",
|
||||
resourceId = "#q.authUserId", description = "新增平台内部用户")
|
||||
public IamUser addUser(CreateUserRequest q) {
|
||||
IamUser u = new IamUser();
|
||||
u.setId(IdWorker.getId()); u.setAuthUserId(q.authUserId()); u.setUserType(q.userType());
|
||||
u.setDisplayName(q.displayName()); u.setPhone(q.phone()); u.setEmail(q.email()); u.setStatus("ACTIVE");
|
||||
repository.insertUser(u); return u;
|
||||
}
|
||||
|
||||
public List<IamUser> internalUsers() { return repository.findInternalUsers(); }
|
||||
public List<IamTenantAddress> addresses(long tenant) { return repository.findAddresses(tenant); }
|
||||
|
||||
@BusinessAudit(event = "TENANT_ADDRESS_CREATE", resourceType = "TENANT_ADDRESS",
|
||||
@@ -54,11 +41,6 @@ public class ProfileResourceService {
|
||||
|
||||
public List<IamTenantQualification> qualifications(long tenant) { return repository.findQualifications(tenant); }
|
||||
|
||||
public IamTenantQualification addQualification(long tenant, QualificationRequest q) {
|
||||
throw new BizException(ResultCode.CONFLICT,
|
||||
"Upload the business license through the qualification file API");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@BusinessAudit(event = "TENANT_QUALIFICATION_SUBMIT", resourceType = "TENANT_QUALIFICATION",
|
||||
resourceId = "#request.tenantId", description = "提交企业资质", captureAfter = false)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.baiyue.iam.service;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import com.baiyue.common.audit.BusinessAudit;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.iam.entity.IamTenant;
|
||||
@@ -72,6 +73,12 @@ public class TenantService {
|
||||
return repository.findPendingReviews();
|
||||
}
|
||||
|
||||
public PageResult<TenantResponse> pageMerchants(long page, long size, String keyword, String status) {
|
||||
PageResult<IamTenant> source = repository.pageMerchants(page, size, keyword, status);
|
||||
return new PageResult<>(source.records().stream().map(this::response).toList(),
|
||||
source.page(), source.size(), source.total(), source.pages());
|
||||
}
|
||||
|
||||
@BusinessAudit(
|
||||
event = "TENANT_REVIEW",
|
||||
resourceType = "TENANT_REVIEW",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
SET NAMES utf8mb4;
|
||||
-- IAM 服务独占数据库:负责租户、成员、双端角色、菜单/页面/按钮/API权限和数据范围。
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS baiyue_iam
|
||||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||||
USE baiyue_iam;
|
||||
|
||||
@@ -61,11 +61,5 @@ FROM iam_permission
|
||||
WHERE app_code='MERCHANT' AND tenant_id=0
|
||||
AND permission_type IN ('MENU','BUTTON') AND status='ACTIVE' AND deleted=0;
|
||||
|
||||
-- Keep the phase-one demo merchant eligible for existing order acceptance tests.
|
||||
INSERT INTO iam_tenant_qualification (
|
||||
id, tenant_id, qualification_type, certificate_no, document_url, status, deleted
|
||||
) VALUES (
|
||||
7000101, 10001, 'BUSINESS_LICENSE', '91310000PHASE10001',
|
||||
'/phase1-seed/demo-business-license.jpg', 'APPROVED', 0
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE status='APPROVED', deleted=0;
|
||||
-- 不再写入伪造的资质 URL。资质必须先由 File 服务保存真实图片,再由 IAM 记录文件地址和审核结果。
|
||||
-- 已部署环境中的 /phase1-seed/ 历史记录按 docs/企业资质文件迁移说明.md 处理,本脚本不得将其改成不存在的 UUID。
|
||||
|
||||
@@ -12,6 +12,9 @@ VALUES
|
||||
(5101025, 0, 'PLATFORM', 'BUTTON', 5101020, 'platform:role:delete', '删除角色', 50, 'ACTIVE', 0),
|
||||
(5101040, 0, 'PLATFORM', 'MENU', 5101000, 'platform:member:list', '平台成员', 40, 'ACTIVE', 0),
|
||||
(5101041, 0, 'PLATFORM', 'BUTTON', 5101040, 'platform:member:grant-role', '分配成员角色', 10, 'ACTIVE', 0),
|
||||
(5101012, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:edit', '编辑用户', 20, 'ACTIVE', 0),
|
||||
(5101013, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:status', '启停用户', 30, 'ACTIVE', 0),
|
||||
(5101014, 0, 'PLATFORM', 'BUTTON', 5101010, 'platform:user:reset-password', '重置密码', 40, 'ACTIVE', 0),
|
||||
(5200033, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:edit', '编辑角色', 30, 'ACTIVE', 0),
|
||||
(5200034, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:status', '启停角色', 40, 'ACTIVE', 0),
|
||||
(5200035, 10001, 'MERCHANT', 'BUTTON', 5200030, 'merchant:role:delete', '删除角色', 50, 'ACTIVE', 0),
|
||||
@@ -28,7 +31,8 @@ INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||||
SELECT 4000001, id, 0 FROM iam_permission
|
||||
WHERE tenant_id = 0 AND app_code = 'PLATFORM'
|
||||
AND permission_code IN ('platform:role:edit', 'platform:role:status', 'platform:role:delete',
|
||||
'platform:member:list', 'platform:member:grant-role', 'platform:customer:create')
|
||||
'platform:member:list', 'platform:member:grant-role', 'platform:customer:create',
|
||||
'platform:user:edit', 'platform:user:status', 'platform:user:reset-password')
|
||||
ON DUPLICATE KEY UPDATE tenant_id = VALUES(tenant_id);
|
||||
|
||||
INSERT INTO iam_role_permission (role_id, permission_id, tenant_id)
|
||||
|
||||
@@ -77,24 +77,73 @@ paths:
|
||||
/api/platform/v1/iam/internal-users:
|
||||
get:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: List platform internal users / 查询平台内部用户
|
||||
summary: 查询平台内部用户
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
post:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: Create platform internal user / 新增平台内部用户
|
||||
description: Create an IAM profile linked to an existing authentication account. / 创建关联已有认证账号的 IAM 用户资料。
|
||||
summary: 新增平台内部用户
|
||||
description: 创建完整的 Auth/IAM 平台账号,也兼容通过 authUserId 关联已有账号。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CreateIamUserRequest'}
|
||||
schema: {$ref: '#/components/schemas/CreateInternalUserRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'400': {$ref: '#/components/responses/BadRequest'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
'409': {$ref: '#/components/responses/Conflict'}
|
||||
/api/platform/v1/iam/internal-users/{membershipId}:
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/MembershipId'}
|
||||
put:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 编辑平台内部用户
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/UpdateInternalUserRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'400': {$ref: '#/components/responses/BadRequest'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
'404': {$ref: '#/components/responses/NotFound'}
|
||||
'409': {$ref: '#/components/responses/Conflict'}
|
||||
/api/platform/v1/iam/internal-users/{membershipId}/status:
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/MembershipId'}
|
||||
put:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 启用或停用平台内部用户
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ChangeInternalUserStatusRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'400': {$ref: '#/components/responses/BadRequest'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
'404': {$ref: '#/components/responses/NotFound'}
|
||||
/api/platform/v1/iam/internal-users/{membershipId}/password:
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/MembershipId'}
|
||||
put:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 重置平台内部用户密码
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ResetInternalUserPasswordRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'400': {$ref: '#/components/responses/BadRequest'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
'404': {$ref: '#/components/responses/NotFound'}
|
||||
/api/merchant/v1/iam/tenant/profile:
|
||||
get:
|
||||
tags: [Merchant Tenant / 商户企业]
|
||||
@@ -165,18 +214,6 @@ paths:
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
post:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: Add tenant qualification / 新增企业资质
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/QualificationRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/Success'}
|
||||
'400': {$ref: '#/components/responses/BadRequest'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
/api/{app}/v1/iam/roles:
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/App'}
|
||||
@@ -276,6 +313,12 @@ components:
|
||||
description: Application domain / 应用域
|
||||
schema: {type: string, enum: [platform, merchant]}
|
||||
example: platform
|
||||
MembershipId:
|
||||
name: membershipId
|
||||
in: path
|
||||
required: true
|
||||
description: Platform membership ID / 平台成员关系 ID
|
||||
schema: {type: integer, format: int64}
|
||||
responses:
|
||||
Success: {description: Operation succeeded / 操作成功, content: {application/json: {schema: {$ref: '#/components/schemas/ApiResponse'}}}}
|
||||
BadRequest: {description: Invalid request / 请求参数错误, content: {application/json: {schema: {$ref: '#/components/schemas/ApiResponse'}}}}
|
||||
@@ -317,15 +360,53 @@ components:
|
||||
type: object
|
||||
properties:
|
||||
comment: {type: string, description: Review comment or rejection reason / 审核意见或驳回原因}
|
||||
CreateIamUserRequest:
|
||||
CreateInternalUserRequest:
|
||||
type: object
|
||||
required: [authUserId, userType, displayName]
|
||||
required: [displayName]
|
||||
properties:
|
||||
authUserId: {type: integer, format: int64, description: 兼容旧接口;填写后关联已有认证账号}
|
||||
userType: {type: string, enum: [INTERNAL, PLATFORM], description: 兼容旧接口,可省略}
|
||||
username: {type: string, minLength: 4, maxLength: 64}
|
||||
password: {type: string, format: password, minLength: 8, maxLength: 64, writeOnly: true}
|
||||
displayName: {type: string, maxLength: 64}
|
||||
phone: {type: string, maxLength: 32}
|
||||
email: {type: string, format: email, maxLength: 128}
|
||||
memberNo: {type: string, maxLength: 64}
|
||||
roleIds: {type: array, items: {type: integer, format: int64}}
|
||||
UpdateInternalUserRequest:
|
||||
type: object
|
||||
required: [displayName, memberNo, roleIds]
|
||||
properties:
|
||||
displayName: {type: string, maxLength: 64}
|
||||
phone: {type: string, maxLength: 32}
|
||||
email: {type: string, format: email, maxLength: 128}
|
||||
memberNo: {type: string, maxLength: 64}
|
||||
roleIds: {type: array, items: {type: integer, format: int64}}
|
||||
ChangeInternalUserStatusRequest:
|
||||
type: object
|
||||
required: [status]
|
||||
properties:
|
||||
status: {type: string, enum: [ACTIVE, DISABLED]}
|
||||
ResetInternalUserPasswordRequest:
|
||||
type: object
|
||||
required: [newPassword]
|
||||
properties:
|
||||
newPassword: {type: string, format: password, minLength: 8, maxLength: 64, writeOnly: true}
|
||||
InternalUserResponse:
|
||||
type: object
|
||||
required: [membershipId, userId, authUserId, username, displayName, memberType, status, roleIds]
|
||||
properties:
|
||||
membershipId: {type: integer, format: int64}
|
||||
userId: {type: integer, format: int64}
|
||||
authUserId: {type: integer, format: int64}
|
||||
userType: {type: string, enum: [PLATFORM, ENTERPRISE]}
|
||||
username: {type: string}
|
||||
displayName: {type: string}
|
||||
phone: {type: string}
|
||||
email: {type: string, format: email}
|
||||
memberType: {type: string, enum: [EMPLOYEE]}
|
||||
memberNo: {type: string}
|
||||
status: {type: string, enum: [ACTIVE, DISABLED]}
|
||||
roleIds: {type: array, items: {type: integer, format: int64}}
|
||||
CreateMemberRequest:
|
||||
type: object
|
||||
required: [username, password, displayName, memberType]
|
||||
@@ -350,15 +431,6 @@ components:
|
||||
detail: {type: string}
|
||||
postalCode: {type: string}
|
||||
isDefault: {type: boolean}
|
||||
QualificationRequest:
|
||||
type: object
|
||||
required: [qualificationType, documentUrl]
|
||||
properties:
|
||||
qualificationType: {type: string, example: BUSINESS_LICENSE}
|
||||
certificateNo: {type: string}
|
||||
documentUrl: {type: string, format: uri}
|
||||
validFrom: {type: string, format: date}
|
||||
validTo: {type: string, format: date}
|
||||
CreateRoleRequest:
|
||||
type: object
|
||||
required: [roleCode, roleName]
|
||||
|
||||
Reference in New Issue
Block a user