This commit is contained in:
2026-08-03 14:16:10 +08:00
parent 595a51ab79
commit fe5e3de9b1
20 changed files with 331 additions and 44 deletions
@@ -8,6 +8,7 @@ import com.baiyue.iam.dto.OnboardingDtos.MerchantRegistrationResponse;
import com.baiyue.iam.dto.OnboardingDtos.OrderEligibilityResponse;
import com.baiyue.iam.dto.OnboardingDtos.QualificationSubmissionRequest;
import com.baiyue.iam.dto.OnboardingDtos.QualificationSubmissionResponse;
import com.baiyue.iam.dto.OnboardingDtos.PlatformQualificationRegistrationRequest;
import com.baiyue.iam.service.MerchantOnboardingService;
import com.baiyue.iam.service.ProfileResourceService;
import com.baiyue.iam.service.TenantService;
@@ -48,6 +49,14 @@ public class InternalTenantController {
return ApiResponse.success(profileResourceService.submitQualification(request));
}
@PostMapping("/platform-qualifications")
public ApiResponse<QualificationSubmissionResponse> registerPlatformQualification(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@Valid @RequestBody PlatformQualificationRegistrationRequest request) {
verify(token);
return ApiResponse.success(profileResourceService.registerPlatformQualification(request));
}
@GetMapping("/tenants/{tenantId}/order-eligibility")
public ApiResponse<OrderEligibilityResponse> orderEligibility(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@@ -70,7 +70,7 @@ public class PlatformTenantController {
@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. / 创建待审核商户租户及注册审核单,需要平台端令牌。")
@Operation(summary = "Register enterprise tenant / 创建企业客户草稿", description = "Create a PROFILE_REQUIRED merchant draft only. Saving the customer does not create a review ticket. / 仅创建待完善企业客户草稿,保存客户不会创建审核单。")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Registration created / 注册已创建"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Invalid enterprise data / 企业资料不合法"),
@@ -103,6 +103,15 @@ public class PlatformTenantController {
return ApiResponse.success(service.changeMerchantStatus(tenantId, request));
}
@PostMapping("/tenants/{tenantId:\\d+}/submit-review")
@PreAuthorize("hasAnyAuthority('platform:customer:create','platform:customer:edit')")
@Operation(summary = "提交企业客户审核", description = "校验客户基础资料与必填资质后创建审核单;保存客户或上传文件不会自动进入审核。")
public ApiResponse<TenantResponse> submitTenantReview(@PathVariable long tenantId,
@AuthenticationPrincipal AuthenticatedUser user) {
TenantGuard.tenant(user, "PLATFORM");
return ApiResponse.success(service.submitMerchantReview(tenantId));
}
@GetMapping("/reviews")
@PreAuthorize("hasAuthority('platform:customer:review')")
@Operation(summary = "List pending enterprise reviews / 查询待审核企业", description = "Return pending enterprise registration review tickets ordered by creation. / 按创建顺序返回待审核企业注册单。")
@@ -2,6 +2,9 @@ package com.baiyue.iam.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDate;
public final class OnboardingDtos {
private OnboardingDtos() { }
@@ -28,5 +31,13 @@ public final class OnboardingDtos {
public record QualificationSubmissionResponse(Long qualificationId, Long reviewId, String tenantStatus) { }
public record PlatformQualificationRegistrationRequest(
@NotNull Long tenantId,
@NotBlank @Pattern(regexp = "BUSINESS_LICENSE|LEGAL_PERSON_ID|FREIGHT_FORWARDER_CERTIFICATE|BANK_ACCOUNT_LICENSE") String qualificationType,
String certificateNo,
@NotBlank String documentUrl,
LocalDate validTo
) { }
public record OrderEligibilityResponse(boolean eligible, String tenantStatus, String reason) { }
}
@@ -29,6 +29,7 @@ public final class TenantDtos {
@Schema(description = "Primary contact / 联系人", example = "张三") @NotBlank String contactName,
@Schema(description = "Contact phone / 联系电话", example = "13800138000") @NotBlank String contactPhone,
@Schema(description = "Contact email / 联系邮箱", example = "ops@example.com") @Email String contactEmail,
@NotBlank @Size(max = 64) String servicePackage,
@Size(max = 64) String contactWechat,
@Size(max = 64) String salesOwnerName,
@Min(0) @Max(365) Integer paymentTermDays,
@@ -57,6 +58,7 @@ public final class TenantDtos {
@NotBlank String contactName,
@NotBlank String contactPhone,
@Email String contactEmail,
@NotBlank @Size(max = 64) String servicePackage,
@Size(max = 64) String contactWechat,
@Size(max = 64) String salesOwnerName,
@Min(0) @Max(365) Integer paymentTermDays,
@@ -82,7 +84,7 @@ public final class TenantDtos {
public record TenantResponse(Long id, String tenantCode, String tenantName, String status,
String legalName, String unifiedSocialCreditCode, String contactName,
String contactPhone, String contactEmail, String contactWechat,
String salesOwnerName, Integer paymentTermDays, String enterpriseType,
String servicePackage, String salesOwnerName, Integer paymentTermDays, String enterpriseType,
LocalDate establishedDate, BigDecimal registeredCapital,
String registeredCapitalCurrency, String registeredAddress,
String internalRemark, LocalDateTime createdAt, LocalDateTime approvedAt) { }
@@ -27,6 +27,7 @@ public class IamTenant extends BaseEntity {
private String contactName;
private String contactPhone;
private String contactEmail;
private String servicePackage;
private String contactWechat;
private String salesOwnerName;
private Integer paymentTermDays;
@@ -5,6 +5,7 @@ import com.baiyue.common.audit.BusinessAudit;
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.OnboardingDtos.PlatformQualificationRegistrationRequest;
import com.baiyue.iam.dto.ProfileDtos.AddressRequest;
import com.baiyue.iam.entity.IamTenant;
import com.baiyue.iam.entity.IamTenantAddress;
@@ -88,6 +89,28 @@ public class ProfileResourceService {
return new QualificationSubmissionResponse(qualification.getId(), review.getId(), tenant.getStatus());
}
@Transactional
@BusinessAudit(event = "PLATFORM_TENANT_QUALIFICATION_UPLOAD", resourceType = "TENANT_QUALIFICATION",
resourceId = "#request.tenantId", description = "平台代客户上传资质", captureAfter = false)
public QualificationSubmissionResponse registerPlatformQualification(PlatformQualificationRegistrationRequest request) {
IamTenant tenant = tenantRepository.findTenant(request.tenantId());
if (tenant == null || !"MERCHANT".equals(tenant.getTenantType())) {
throw new BizException(ResultCode.NOT_FOUND, "Enterprise not found / 企业客户不存在");
}
if (!"PROFILE_REQUIRED".equals(tenant.getStatus()) && !"REJECTED".equals(tenant.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Qualifications can only be uploaded before review / 仅待完善或已驳回客户可上传资质");
}
if (repository.findQualifications(tenant.getId()).stream().anyMatch(item ->
request.qualificationType().equals(item.getQualificationType()) && !"REJECTED".equals(item.getStatus()))) {
throw new BizException(ResultCode.CONFLICT, "Qualification type already uploaded / 该类资质已上传");
}
IamTenantQualification qualification = createQualification(tenant.getId(), request.qualificationType(),
request.certificateNo(), request.documentUrl());
qualification.setValidTo(request.validTo());
repository.saveQualification(qualification);
return new QualificationSubmissionResponse(qualification.getId(), null, tenant.getStatus());
}
private IamTenantQualification createQualification(long tenant, String type, String certificateNo, String url) {
IamTenantQualification x = new IamTenantQualification();
x.setId(IdWorker.getId()); x.setTenantId(tenant); x.setQualificationType(type);
@@ -53,12 +53,13 @@ public class TenantService {
tenant.setTenantCode("TEN" + tenant.getId());
tenant.setTenantName(request.tenantName());
tenant.setTenantType("MERCHANT");
tenant.setStatus("PENDING");
tenant.setStatus("PROFILE_REQUIRED");
tenant.setLegalName(request.legalName());
tenant.setUnifiedSocialCreditCode(request.unifiedSocialCreditCode());
tenant.setContactName(request.contactName());
tenant.setContactPhone(request.contactPhone());
tenant.setContactEmail(request.contactEmail());
tenant.setServicePackage(request.servicePackage());
tenant.setContactWechat(request.contactWechat());
tenant.setSalesOwnerName(request.salesOwnerName());
tenant.setPaymentTermDays(request.paymentTermDays());
@@ -70,13 +71,6 @@ public class TenantService {
tenant.setInternalRemark(request.internalRemark());
repository.insertTenant(tenant);
IamTenantReview review = new IamTenantReview();
review.setId(IdWorker.getId());
review.setTenantId(tenant.getId());
review.setReviewType("REGISTER");
review.setStatus("PENDING");
review.setCreatedAt(LocalDateTime.now());
repository.insertReview(review);
return response(tenant);
}
@@ -108,6 +102,15 @@ public class TenantService {
throw new BizException(ResultCode.CONFLICT, "Review already completed / 审核已完成");
}
if (approve) {
var qualifications = profileRepository.findQualifications(review.getTenantId());
boolean hasLicense = qualifications.stream().anyMatch(item -> "BUSINESS_LICENSE".equals(item.getQualificationType()) && "PENDING".equals(item.getStatus()));
boolean hasLegalId = qualifications.stream().anyMatch(item -> "LEGAL_PERSON_ID".equals(item.getQualificationType()) && "PENDING".equals(item.getStatus()));
if (!hasLicense || !hasLegalId) {
throw new BizException(ResultCode.CONFLICT, "Required qualifications are incomplete / 必填资质不完整,不能审核通过");
}
}
review.setStatus(approve ? "APPROVED" : "REJECTED");
review.setReviewerId(reviewerId);
review.setReviewComment(comment);
@@ -171,6 +174,7 @@ public class TenantService {
tenant.setContactName(request.contactName());
tenant.setContactPhone(request.contactPhone());
tenant.setContactEmail(request.contactEmail());
tenant.setServicePackage(request.servicePackage());
tenant.setContactWechat(request.contactWechat());
tenant.setSalesOwnerName(request.salesOwnerName());
tenant.setPaymentTermDays(request.paymentTermDays());
@@ -198,6 +202,33 @@ public class TenantService {
return response(tenant);
}
@Transactional
@BusinessAudit(event = "PLATFORM_TENANT_SUBMIT_REVIEW", resourceType = "TENANT", resourceId = "#tenantId",
description = "平台提交企业客户审核")
public TenantResponse submitMerchantReview(long tenantId) {
IamTenant tenant = requireMerchant(tenantId);
if (!"PROFILE_REQUIRED".equals(tenant.getStatus()) && !"REJECTED".equals(tenant.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Only draft or rejected enterprise can be submitted / 仅待完善或已驳回客户可提交审核");
}
if (repository.findPendingReviewByTenant(tenantId) != null) {
throw new BizException(ResultCode.CONFLICT, "Enterprise information is already under review / 客户资料已在审核中");
}
var qualifications = profileRepository.findQualifications(tenantId);
boolean hasLicense = qualifications.stream().anyMatch(item -> "BUSINESS_LICENSE".equals(item.getQualificationType()) && !"REJECTED".equals(item.getStatus()));
boolean hasLegalId = qualifications.stream().anyMatch(item -> "LEGAL_PERSON_ID".equals(item.getQualificationType()) && !"REJECTED".equals(item.getStatus()));
if (!hasLicense || !hasLegalId) {
throw new BizException(ResultCode.BAD_REQUEST, "Business license and legal-person ID are required / 营业执照和法人身份证为必填资质");
}
IamTenantReview review = new IamTenantReview();
review.setId(IdWorker.getId()); review.setTenantId(tenantId);
review.setReviewType("REGISTER"); review.setStatus("PENDING");
review.setCreatedAt(LocalDateTime.now());
repository.insertReview(review);
tenant.setStatus("PENDING"); tenant.setRejectReason(null);
repository.saveTenant(tenant);
return response(tenant);
}
@Transactional
@BusinessAudit(event = "TENANT_PROFILE_UPDATE", resourceType = "TENANT",
resourceId = "#tenantId", description = "编辑企业资料")
@@ -232,7 +263,7 @@ public class TenantService {
tenant.getId(), tenant.getTenantCode(), tenant.getTenantName(), tenant.getStatus(),
tenant.getLegalName(), tenant.getUnifiedSocialCreditCode(), tenant.getContactName(),
tenant.getContactPhone(), tenant.getContactEmail(), tenant.getContactWechat(),
tenant.getSalesOwnerName(), tenant.getPaymentTermDays(), tenant.getEnterpriseType(),
tenant.getServicePackage(), tenant.getSalesOwnerName(), tenant.getPaymentTermDays(), tenant.getEnterpriseType(),
tenant.getEstablishedDate(), tenant.getRegisteredCapital(), tenant.getRegisteredCapitalCurrency(),
tenant.getRegisteredAddress(), tenant.getInternalRemark(), tenant.getCreatedAt(), tenant.getApprovedAt()
);
@@ -243,7 +274,7 @@ public class TenantService {
return new TenantResponse(response.id(), response.tenantCode(), response.tenantName(), response.status(),
response.legalName(), response.unifiedSocialCreditCode(), response.contactName(),
maskPhone(response.contactPhone()), maskEmail(response.contactEmail()), response.contactWechat(),
response.salesOwnerName(), response.paymentTermDays(), response.enterpriseType(),
response.servicePackage(), response.salesOwnerName(), response.paymentTermDays(), response.enterpriseType(),
response.establishedDate(), response.registeredCapital(), response.registeredCapitalCurrency(),
response.registeredAddress(), response.internalRemark(), response.createdAt(), response.approvedAt());
}
@@ -252,7 +283,7 @@ public class TenantService {
TenantResponse response = response(tenant);
return new TenantResponse(response.id(), response.tenantCode(), response.tenantName(), response.status(),
response.legalName(), response.unifiedSocialCreditCode(), response.contactName(),
response.contactPhone(), response.contactEmail(), response.contactWechat(), response.salesOwnerName(),
response.contactPhone(), response.contactEmail(), response.contactWechat(), response.servicePackage(), response.salesOwnerName(),
response.paymentTermDays(), response.enterpriseType(), response.establishedDate(),
response.registeredCapital(), response.registeredCapitalCurrency(), response.registeredAddress(),
null, response.createdAt(), response.approvedAt());
+1
View File
@@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS iam_tenant (
contact_name VARCHAR(64) NULL COMMENT '企业联系人',
contact_phone VARCHAR(32) NULL COMMENT '联系电话',
contact_email VARCHAR(128) NULL COMMENT '联系邮箱',
service_package VARCHAR(64) NOT NULL DEFAULT '基础版' COMMENT '客户服务套餐',
contact_wechat VARCHAR(64) NULL COMMENT '联系微信',
sales_owner_name VARCHAR(64) NULL COMMENT '销售负责人姓名',
payment_term_days SMALLINT UNSIGNED NULL COMMENT '账期天数',
+24 -8
View File
@@ -39,8 +39,8 @@ paths:
'422': {$ref: '#/components/responses/UnprocessableEntity'}
post:
tags: [Platform Tenant / 平台租户]
summary: Register enterprise tenant / 创建企业租户
description: Create a pending enterprise tenant and review task. Platform identity is required. / 创建待审核企业租户及审核任务,要求平台端身份。
summary: Register enterprise tenant / 创建企业客户草稿
description: Create a `PROFILE_REQUIRED` enterprise draft only. Saving the customer does not create a review task. / 仅创建待完善企业客户草稿,保存客户不会创建审核单。
requestBody:
required: true
content:
@@ -88,6 +88,19 @@ paths:
'400': {$ref: '#/components/responses/BadRequest'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
/api/platform/v1/iam/tenants/{tenantId}/submit-review:
parameters:
- {$ref: '#/components/parameters/TenantId'}
post:
tags: [Platform Tenant / 平台租户]
summary: 提交企业客户审核
description: 校验营业执照和法人身份证已上传后创建审核单,并把客户状态改为 `PENDING`;保存资料和上传文件均不会自动调用本接口。
responses:
'200': {$ref: '#/components/responses/TenantSuccess'}
'400': {$ref: '#/components/responses/BadRequest'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/NotFound'}
'409': {$ref: '#/components/responses/Conflict'}
/api/platform/v1/iam/tenants/{tenantId}/addresses:
parameters:
- {$ref: '#/components/parameters/TenantId'}
@@ -674,7 +687,7 @@ components:
data: {$ref: '#/components/schemas/InternalUserResponse'}
TenantResponse:
type: object
required: [id, tenantCode, tenantName, status]
required: [id, tenantCode, tenantName, status, servicePackage]
properties:
id: {type: string, pattern: '^\d+$', example: '190000000000000001'}
tenantCode: {type: string}
@@ -686,6 +699,7 @@ components:
contactPhone: {type: string}
contactEmail: {type: string, format: email}
contactWechat: {type: string, maxLength: 64}
servicePackage: {type: string, maxLength: 64}
salesOwnerName: {type: string, maxLength: 64}
paymentTermDays: {type: integer, minimum: 0, maximum: 365}
enterpriseType: {type: string, maxLength: 64}
@@ -730,16 +744,16 @@ components:
properties:
id: {type: string, pattern: '^\d+$'}
tenantId: {type: string, pattern: '^\d+$'}
qualificationType: {type: string}
qualificationType: {type: string, enum: [BUSINESS_LICENSE, LEGAL_PERSON_ID, FREIGHT_FORWARDER_CERTIFICATE, BANK_ACCOUNT_LICENSE]}
certificateNo: {type: string}
documentUrl: {type: string}
validFrom: {type: string, format: date}
validTo: {type: string, format: date}
status: {type: string}
validTo: {type: string, format: date, nullable: true}
status: {type: string, enum: [PENDING, APPROVED, REJECTED]}
reviewComment: {type: string}
RegisterRequest:
type: object
required: [tenantName, legalName, unifiedSocialCreditCode, contactName, contactPhone]
required: [tenantName, legalName, unifiedSocialCreditCode, contactName, contactPhone, servicePackage]
properties:
tenantName: {type: string, example: 深圳虫虫出海科技有限公司}
legalName: {type: string, example: 张三}
@@ -748,6 +762,7 @@ components:
contactPhone: {type: string, example: '13800138000'}
contactEmail: {type: string, format: email, example: contact@example.com}
contactWechat: {type: string, maxLength: 64}
servicePackage: {type: string, maxLength: 64, example: 基础版}
salesOwnerName: {type: string, maxLength: 64}
paymentTermDays: {type: integer, minimum: 0, maximum: 365}
enterpriseType: {type: string, maxLength: 64}
@@ -758,7 +773,7 @@ components:
internalRemark: {type: string, maxLength: 1000}
UpdateProfileRequest:
type: object
required: [tenantName, legalName, contactName, contactPhone]
required: [tenantName, legalName, contactName, contactPhone, servicePackage]
properties:
tenantName: {type: string}
legalName: {type: string}
@@ -775,6 +790,7 @@ components:
contactPhone: {type: string}
contactEmail: {type: string, format: email}
contactWechat: {type: string, maxLength: 64}
servicePackage: {type: string, maxLength: 64, example: 基础版}
salesOwnerName: {type: string, maxLength: 64}
paymentTermDays: {type: integer, minimum: 0, maximum: 365}
enterpriseType: {type: string, maxLength: 64}
@@ -0,0 +1,46 @@
package com.baiyue.iam.service;
import com.baiyue.iam.dto.OnboardingDtos.PlatformQualificationRegistrationRequest;
import com.baiyue.iam.entity.IamTenant;
import com.baiyue.iam.entity.IamTenantQualification;
import com.baiyue.iam.repository.ProfileResourceRepository;
import com.baiyue.iam.repository.TenantRepository;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;
import java.time.LocalDate;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class PlatformQualificationRegistrationTest {
@Test
void platformUploadRegistersMetadataWithoutCreatingReviewTicket() {
ProfileResourceRepository repository = mock(ProfileResourceRepository.class);
TenantRepository tenantRepository = mock(TenantRepository.class);
ProfileResourceService service = new ProfileResourceService();
ReflectionTestUtils.setField(service, "repository", repository);
ReflectionTestUtils.setField(service, "tenantRepository", tenantRepository);
IamTenant tenant = new IamTenant();
tenant.setId(10001L); tenant.setTenantType("MERCHANT"); tenant.setStatus("PROFILE_REQUIRED");
when(tenantRepository.findTenant(10001L)).thenReturn(tenant);
when(repository.findQualifications(10001L)).thenReturn(List.of());
var response = service.registerPlatformQualification(new PlatformQualificationRegistrationRequest(
10001L, "LEGAL_PERSON_ID", null, "/api/merchant/v1/files/qualifications/file.pdf",
LocalDate.of(2030, 12, 31)));
ArgumentCaptor<IamTenantQualification> captor = ArgumentCaptor.forClass(IamTenantQualification.class);
verify(repository).insertQualification(captor.capture());
verify(repository).saveQualification(captor.getValue());
assertEquals("LEGAL_PERSON_ID", captor.getValue().getQualificationType());
assertEquals(LocalDate.of(2030, 12, 31), captor.getValue().getValidTo());
assertEquals("PROFILE_REQUIRED", response.tenantStatus());
assertNull(response.reviewId());
}
}
@@ -4,6 +4,8 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.iam.entity.IamTenant;
import com.baiyue.iam.dto.TenantDtos.ChangeTenantStatusRequest;
import com.baiyue.iam.dto.TenantDtos.UpdateTenantRequest;
import com.baiyue.iam.dto.TenantDtos.RegisterRequest;
import com.baiyue.iam.entity.IamTenantQualification;
import com.baiyue.iam.repository.TenantRepository;
import com.baiyue.iam.repository.ProfileResourceRepository;
import org.junit.jupiter.api.Test;
@@ -18,6 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.never;
import static org.mockito.ArgumentMatchers.any;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.core.api.ResultCode;
@@ -72,12 +76,13 @@ class TenantServiceTest {
var updated = service.updateMerchant(10001L,
new UpdateTenantRequest("New name", "New legal", "Alice", "13900139000", "alice@example.com",
"alice_wechat", "Sales Zhang", 45, "LIMITED_COMPANY", LocalDate.of(2020, 1, 2),
"专业版", "alice_wechat", "Sales Zhang", 45, "LIMITED_COMPANY", LocalDate.of(2020, 1, 2),
new BigDecimal("5000000.00"), "CNY", "深圳市南山区示例路 1 号", "一期重点客户"));
var disabled = service.changeMerchantStatus(10001L, new ChangeTenantStatusRequest("DISABLED"));
assertEquals("New name", updated.tenantName());
assertEquals("alice_wechat", updated.contactWechat());
assertEquals("专业版", updated.servicePackage());
assertEquals("Sales Zhang", updated.salesOwnerName());
assertEquals(45, updated.paymentTermDays());
assertEquals("LIMITED_COMPANY", updated.enterpriseType());
@@ -103,6 +108,41 @@ class TenantServiceTest {
assertEquals(ResultCode.CONFLICT, exception.getResultCode());
}
@Test
void platformRegistrationCreatesDraftWithoutCreatingReviewTicket() {
TenantRepository repository = mock(TenantRepository.class);
TenantService service = new TenantService();
ReflectionTestUtils.setField(service, "repository", repository);
var created = service.register(new RegisterRequest(
"Example Merchant", "Example Merchant Ltd", "91330100EXAMPLE001", "Alice", "13800138000",
"alice@example.com", "基础版", "alice_wechat", "Sales Zhang", 30, "LIMITED_COMPANY",
LocalDate.of(2020, 1, 2), new BigDecimal("5000000.00"), "CNY", "深圳市南山区", "草稿客户"));
assertEquals("PROFILE_REQUIRED", created.status());
assertEquals("基础版", created.servicePackage());
verify(repository, never()).insertReview(any());
}
@Test
void explicitSubmissionRequiresBothMandatoryQualificationsAndCreatesReview() {
TenantRepository repository = mock(TenantRepository.class);
ProfileResourceRepository profileRepository = mock(ProfileResourceRepository.class);
TenantService service = new TenantService();
ReflectionTestUtils.setField(service, "repository", repository);
ReflectionTestUtils.setField(service, "profileRepository", profileRepository);
IamTenant tenant = merchant(10001L, "PROFILE_REQUIRED");
when(repository.findTenant(10001L)).thenReturn(tenant);
when(profileRepository.findQualifications(10001L)).thenReturn(List.of(
qualification("BUSINESS_LICENSE"), qualification("LEGAL_PERSON_ID")));
var submitted = service.submitMerchantReview(10001L);
assertEquals("PENDING", submitted.status());
verify(repository).insertReview(any());
verify(repository).saveTenant(tenant);
}
@Test
void 企业激活不等于具备下单资格仍需已审核资质() {
TenantRepository repository = mock(TenantRepository.class);
@@ -134,4 +174,10 @@ class TenantServiceTest {
tenant.setContactPhone("13800138000");
return tenant;
}
private IamTenantQualification qualification(String type) {
IamTenantQualification item = new IamTenantQualification();
item.setQualificationType(type); item.setStatus("PENDING");
return item;
}
}