This commit is contained in:
2026-08-03 13:16:19 +08:00
parent 481747d9dc
commit 9eb03d4bce
54 changed files with 1925 additions and 52 deletions
@@ -56,6 +56,11 @@ public class InternalAccountController {
@PathVariable Long id, @Valid @RequestBody ResetPasswordRequest request) {
verify(token); service.resetPassword(id, request); return ApiResponse.success(null);
}
@PutMapping("/{id}/scope")
public ApiResponse<Void> scope(@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
@PathVariable Long id, @Valid @RequestBody UpdateAccountScopeRequest request) {
verify(token); service.updateScope(id, request); return ApiResponse.success(null);
}
@DeleteMapping("/{id}/compensation")
public ApiResponse<Void> compensate(@RequestHeader(value="X-Internal-Service-Token", required=false) String token,
@PathVariable Long id) {
@@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDateTime;
/** 账号安全及内部账号管理契约。 */
public final class AccountManagementDtos {
private AccountManagementDtos() { }
@@ -19,10 +21,24 @@ public final class AccountManagementDtos {
@NotBlank String displayName, @NotNull Long tenantId,
@NotNull Long membershipId,
@Pattern(regexp = "MERCHANT|PLATFORM", message = "应用端只能是 MERCHANT 或 PLATFORM")
String appCode) { }
String appCode,
@Pattern(regexp = "ACTIVE|DISABLED", message = "账号状态只能是 ACTIVE 或 DISABLED")
String accountStatus) {
public CreateAccountRequest(Long accountId, String username, String password, String displayName,
Long tenantId, Long membershipId, String appCode) {
this(accountId, username, password, displayName, tenantId, membershipId, appCode, "ACTIVE");
}
}
public record UpdateAccountProfileRequest(@NotBlank @Size(max = 64) String displayName) { }
public record UpdateAccountStatusRequest(@NotBlank String status) { }
public record ResetPasswordRequest(@NotBlank @Size(min = 8, max = 64)
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d).+$", message = "密码必须同时包含字母和数字") String newPassword) { }
public record AccountSummary(Long accountId, String username, String displayName, String status) { }
public record UpdateAccountScopeRequest(@NotNull Long tenantId, @NotNull Long membershipId,
@NotBlank @Pattern(regexp = "MERCHANT|PLATFORM") String appCode) { }
public record AccountSummary(Long accountId, String username, String displayName, String status,
LocalDateTime lastLoginAt, Long tenantId, Long membershipId, String appCode) {
public AccountSummary(Long accountId, String username, String displayName, String status) {
this(accountId, username, displayName, status, null, null, null, null);
}
}
}
@@ -54,7 +54,7 @@ public class AccountManagementService {
account.setUsername(request.username());
account.setPasswordHash(passwordEncoder.encode(request.password()));
account.setDisplayName(request.displayName());
account.setAccountStatus("ACTIVE");
account.setAccountStatus(request.accountStatus() == null ? "ACTIVE" : request.accountStatus());
account.setFailedLoginCount(0);
account.setTokenVersion(1);
account.setDefaultTenantId(request.tenantId());
@@ -93,6 +93,20 @@ public class AccountManagementService {
setPasswordAndRevoke(required(accountId), request.newPassword());
}
@Transactional
@BusinessAudit(event = "ACCOUNT_SCOPE_CHANGE", resourceType = "ACCOUNT", resourceId = "#accountId",
description = "修改账号所属租户", captureAfter = false)
public void updateScope(Long accountId, UpdateAccountScopeRequest request) {
String appCode = request.appCode().trim().toUpperCase(Locale.ROOT);
validateScope(appCode, request.tenantId());
AuthAccount account = required(accountId);
account.setDefaultTenantId(request.tenantId());
account.setOperatorTenantId(request.tenantId());
account.setDefaultMembershipId(request.membershipId());
account.setDefaultAppCode(appCode);
bumpVersion(account);
}
@Transactional
@BusinessAudit(event = "ACCOUNT_COMPENSATE_DELETE", resourceType = "ACCOUNT",
resourceId = "#accountId", description = "补偿删除认证账号", captureAfter = false)
@@ -127,7 +141,8 @@ public class AccountManagementService {
}
private AccountSummary summary(AuthAccount a) {
return new AccountSummary(a.getId(), a.getUsername(), a.getDisplayName(), a.getAccountStatus());
return new AccountSummary(a.getId(), a.getUsername(), a.getDisplayName(), a.getAccountStatus(),
a.getLastLoginAt(), a.getDefaultTenantId(), a.getDefaultMembershipId(), a.getDefaultAppCode());
}
private void validateScope(String appCode, Long tenantId) {