暂存提交

This commit is contained in:
2026-07-31 13:52:35 +08:00
parent 11e740706d
commit bc3c18a6e9
35 changed files with 421 additions and 121 deletions
@@ -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
View File
@@ -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)
+100 -28
View File
@@ -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]