feat(p1r): 增加 Account DTO 与 DAL 骨架

This commit is contained in:
zizi 2026-05-29 02:01:20 +08:00
parent 3c6cdb1f5e
commit 50c8cf5ddc
74 changed files with 2731 additions and 0 deletions

View File

@ -0,0 +1,34 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - Account 用户账户摘要 Response VO")
@Data
public class AdminAccountUserSummaryRespVO {
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "用户昵称")
private String nickname;
@Schema(description = "头像 URL")
private String avatar;
@Schema(description = "账号状态")
private String status;
@Schema(description = "权益来源")
private String entitlementSource;
@Schema(description = "配额状态")
private String quotaStatus;
@Schema(description = "New-API 网关绑定状态")
private String newApiBindingStatus;
@Schema(description = "风险标记列表")
private List<String> riskFlags;
@Schema(description = "注册时间")
private LocalDateTime createdAt;
@Schema(description = "最近活跃时间")
private LocalDateTime lastActiveAt;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account 余额和权益快照 Response VO")
@Data
public class AdminBalanceSnapshotEntryRespVO {
@Schema(description = "快照 ID")
private String snapshotId;
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "余额")
private BigDecimal balance;
@Schema(description = "已使用配额")
private Integer usedQuota;
@Schema(description = "总配额")
private Integer totalQuota;
@Schema(description = "到期时间")
private LocalDateTime expiresAt;
@Schema(description = "快照捕获时间")
private LocalDateTime capturedAt;
@Schema(description = "快照来源")
private String source;
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - Account 外部调用详情 Response VO")
@Data
public class AdminIntegrationCallDetailRespVO {
@Schema(description = "外部调用关联 ID")
private String correlationId;
@Schema(description = "调用状态")
private String status;
@Schema(description = "请求类型")
private String requestType;
@Schema(description = "重试组内的调用 ID 列表")
private List<String> retryGroup;
@Schema(description = "归属状态")
private String attributionStatus;
@Schema(description = "归属用户 ID")
private String attributedUserId;
@Schema(description = "首次调用时间")
private LocalDateTime createdAt;
@Schema(description = "最近重试时间")
private LocalDateTime lastRetryAt;
@Schema(description = "错误信息")
private String errorMessage;
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account 购买记录 Response VO")
@Data
public class AdminPurchaseRecordRespVO {
@Schema(description = "记录 ID")
private String recordId;
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "用户昵称")
private String nickname;
@Schema(description = "资产类型")
private String assetType;
@Schema(description = "资产名称")
private String assetName;
@Schema(description = "购买类型")
private String purchaseType;
@Schema(description = "金额")
private BigDecimal amount;
@Schema(description = "购买状态")
private String status;
@Schema(description = "外部订单引用,已脱敏")
private String externalOrderRef;
@Schema(description = "授权结果摘要")
private String authorizationResult;
@Schema(description = "购买时间")
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - Account 额度配置请求创建 Request VO")
@Data
public class AdminQuotaRequestCreateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "额度配置类型")
private String requestType;
@Schema(description = "请求原因")
private String reason;
@Schema(description = "目标额度配置")
private TargetQuotaVO targetQuota;
@Schema(description = "目标额度配置 VO")
@Data
public static class TargetQuotaVO {
@Schema(description = "资源类型")
private String resourceType;
@Schema(description = "额度上限")
private Integer limit;
}
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - Account 额度请求创建结果 Response VO")
@Data
public class AdminQuotaRequestResultRespVO {
@Schema(description = "额度请求 ID")
private String requestId;
@Schema(description = "外部调用关联 ID")
private String correlationId;
@Schema(description = "请求初始状态")
private String status;
}

View File

@ -0,0 +1,58 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - Account 用量记录 Response VO")
@Data
public class AdminUsageRecordRespVO {
@Schema(description = "记录 ID")
private String recordId;
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "用户昵称")
private String nickname;
@Schema(description = "统计周期")
private String period;
@Schema(description = "总输入 Token 数")
private Integer totalInputTokens;
@Schema(description = "总输出 Token 数")
private Integer totalOutputTokens;
@Schema(description = "总 Token 数")
private Integer totalTokens;
@Schema(description = "待归属记录数")
private Integer pendingAttributionCount;
@Schema(description = "归属失败记录数")
private Integer failedAttributionCount;
@Schema(description = "归属状态")
private String attributionStatus;
@Schema(description = "按作品归属摘要")
private List<WorkUsageVO> byWork;
@Schema(description = "按智能体归属摘要")
private List<AgentUsageVO> byAgent;
@Schema(description = "作品用量摘要 VO")
@Data
public static class WorkUsageVO {
@Schema(description = "作品 ID")
private String workId;
@Schema(description = "作品标题")
private String workTitle;
@Schema(description = "Token 数")
private Integer tokens;
}
@Schema(description = "智能体用量摘要 VO")
@Data
public static class AgentUsageVO {
@Schema(description = "智能体 ID")
private String agentId;
@Schema(description = "智能体名称")
private String agentName;
@Schema(description = "Token 数")
private Integer tokens;
}
}

View File

@ -0,0 +1,65 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - Account 用户权益和配额详情 Response VO")
@Data
public class AdminUserEntitlementDetailRespVO {
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "权益列表")
private List<EntitlementVO> entitlements;
@Schema(description = "配额列表")
private List<QuotaVO> quotas;
@Schema(description = "权益来源说明")
private String entitlementSource;
@Schema(description = "最近权益变更时间")
private LocalDateTime lastModifiedAt;
@Schema(description = "最近变更操作者摘要")
private String lastModifiedBy;
@Schema(description = "Account 权益项 VO")
@Data
public static class EntitlementVO {
@Schema(description = "权益 ID")
private String id;
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "额度上限")
private Integer limit;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余额度")
private Integer remaining;
@Schema(description = "权益来源")
private String source;
@Schema(description = "到期时间")
private LocalDateTime expiresAt;
}
@Schema(description = "Account 配额项 VO")
@Data
public static class QuotaVO {
@Schema(description = "配额 ID")
private String id;
@Schema(description = "配额资源类型")
private String resourceType;
@Schema(description = "总配额")
private Integer total;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余配额")
private Integer remaining;
@Schema(description = "配额重置时间")
private LocalDateTime resetAt;
@Schema(description = "频率限制")
private Integer limitPerMinute;
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - Account 调用归属 Job 创建 Request VO")
@Data
public class CallAttributionJobCreateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "New-API 调用关联 ID")
private String correlationId;
@Schema(description = "New-API callId 白名单")
private List<String> callIds;
@Schema(description = "调用审计记录 revision 乐观锁")
private Integer expectedCallRevision;
@Schema(description = "归因校验模式")
private String verificationMode;
@Schema(description = "归属原因")
private String reason;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account 调用归属 Job 详情 Response VO")
@Data
public class CallAttributionJobDetailRespVO {
@Schema(description = "调用归属 job ID")
private String jobId;
@Schema(description = "执行状态")
private String status;
@Schema(description = "归属用户 ID")
private String userId;
@Schema(description = "已归属调用数")
private Integer attributedCallCount;
@Schema(description = "待归属调用数")
private Integer pendingCallCount;
@Schema(description = "归属失败调用数")
private Integer failedCallCount;
@Schema(description = "失败原因")
private String failedReason;
@Schema(description = "补偿建议")
private String compensationSuggestion;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
@Schema(description = "完成时间")
private LocalDateTime completedAt;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - Account 调用归属 Job 创建结果 Response VO")
@Data
public class CallAttributionJobResultRespVO {
@Schema(description = "调用归属 job ID")
private String jobId;
@Schema(description = "job 初始状态")
private String status;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - Account New-API 绑定创建 Request VO")
@Data
public class NewApiBindingCreateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "是否强制刷新已有绑定")
private Boolean forceRefresh;
@Schema(description = "创建或刷新原因")
private String reason;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account New-API 绑定结果 Response VO")
@Data
public class NewApiBindingResultRespVO {
@Schema(description = "绑定记录 ID")
private String bindingId;
@Schema(description = "绑定操作结果状态")
private String status;
@Schema(description = "请求幂等键")
private String commandId;
@Schema(description = "绑定时间")
private LocalDateTime boundAt;
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account New-API 绑定摘要 Response VO")
@Data
public class NewApiBindingSummaryRespVO {
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "用户昵称")
private String nickname;
@Schema(description = "绑定状态")
private String bindingStatus;
@Schema(description = "绑定时间")
private LocalDateTime boundAt;
@Schema(description = "最近同步时间")
private LocalDateTime lastSyncAt;
@Schema(description = "同步失败原因")
private String syncErrorMessage;
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Account 配额调整 Ledger Response VO")
@Data
public class QuotaAdjustmentLedgerEntryRespVO {
@Schema(description = "调整记录 ID")
private String adjustmentId;
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "调整来源类型")
private String sourceType;
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "调整增量")
private Integer delta;
@Schema(description = "调整前快照")
private SnapshotVO beforeSnapshot;
@Schema(description = "调整后快照")
private SnapshotVO afterSnapshot;
@Schema(description = "关联外部调用 ID")
private String correlationId;
@Schema(description = "调整原因")
private String reason;
@Schema(description = "操作者摘要")
private String operatorSummary;
@Schema(description = "审计状态")
private String auditStatus;
@Schema(description = "调整时间")
private LocalDateTime createdAt;
@Schema(description = "权益快照 VO")
@Data
public static class SnapshotVO {
@Schema(description = "额度上限")
private Integer limit;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余额度")
private Integer remaining;
}
}

View File

@ -0,0 +1,49 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - Account 配额调整 Request VO")
@Data
public class QuotaAdjustmentReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "调整项列表")
private List<AdjustmentVO> adjustments;
@Schema(description = "调整原因")
private String reason;
@Schema(description = "关联外部调用 ID")
private String correlationId;
@Schema(description = "审批引用")
private String approvalRef;
@Schema(description = "配额调整项 VO")
@Data
public static class AdjustmentVO {
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "调整增量")
private Integer delta;
@Schema(description = "调整前权益快照")
private SnapshotVO beforeSnapshot;
@Schema(description = "新的到期时间")
private LocalDateTime expiresAt;
}
@Schema(description = "权益快照 VO")
@Data
public static class SnapshotVO {
@Schema(description = "额度上限")
private Integer limit;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余额度")
private Integer remaining;
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.muse.module.member.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - Account 配额调整结果 Response VO")
@Data
public class QuotaAdjustmentResultRespVO {
@Schema(description = "调整记录 ID")
private String adjustmentId;
@Schema(description = "请求幂等键")
private String commandId;
@Schema(description = "调整状态")
private String status;
@Schema(description = "调整后权益快照")
private AfterSnapshotVO afterSnapshot;
@Schema(description = "调整后权益快照 VO")
@Data
public static class AfterSnapshotVO {
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "额度上限")
private Integer limit;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余额度")
private Integer remaining;
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
@Schema(description = "用户 APP - Account 导出任务创建 Request VO")
@Data
public class AccountExportTaskCreateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "导出类型")
private String exportType;
@Schema(description = "时间范围")
private DateRangeVO dateRange;
@Schema(description = "脱敏规则")
private String desensitizationRule;
@Schema(description = "导出时间范围 VO")
@Data
public static class DateRangeVO {
@Schema(description = "开始日期")
private LocalDate startDate;
@Schema(description = "结束日期")
private LocalDate endDate;
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 导出任务详情 Response VO")
@Data
public class AccountExportTaskDetailRespVO {
@Schema(description = "导出任务 ID")
private String taskId;
@Schema(description = "导出类型")
private String exportType;
@Schema(description = "任务状态")
private String status;
@Schema(description = "下载凭证 ID")
private String downloadCredentialId;
@Schema(description = "下载凭证过期时间")
private LocalDateTime downloadExpiresAt;
@Schema(description = "失败原因")
private String failedReason;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
@Schema(description = "完成时间")
private LocalDateTime completedAt;
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 导出任务创建结果 Response VO")
@Data
public class AccountExportTaskResultRespVO {
@Schema(description = "导出任务 ID")
private String taskId;
@Schema(description = "任务初始状态")
private String status;
@Schema(description = "预计完成时间")
private LocalDateTime estimatedCompletionAt;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 余额和权益快照 Response VO")
@Data
public class AppBalanceSnapshotEntryRespVO {
@Schema(description = "快照 ID")
private String snapshotId;
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "余额")
private BigDecimal balance;
@Schema(description = "已使用配额")
private Integer usedQuota;
@Schema(description = "总配额")
private Integer totalQuota;
@Schema(description = "到期时间")
private LocalDateTime expiresAt;
@Schema(description = "快照捕获时间")
private LocalDateTime capturedAt;
@Schema(description = "快照来源")
private String source;
}

View File

@ -0,0 +1,61 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 当前用户摘要 Response VO")
@Data
public class AppCurrentUserSummaryRespVO {
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "用户昵称")
private String nickname;
@Schema(description = "头像 URL")
private String avatar;
@Schema(description = "账号状态")
private String status;
@Schema(description = "邮箱是否已验证")
private Boolean emailVerified;
@Schema(description = "手机号是否已验证")
private Boolean phoneVerified;
@Schema(description = "是否启用二次验证")
private Boolean mfaEnabled;
@Schema(description = "权益摘要")
private EntitlementSummaryVO entitlementSummary;
@Schema(description = "待处理安全风险数")
private Integer securityRiskCount;
@Schema(description = "默认入口路径")
private String defaultEntry;
@Schema(description = "可见产品空间列表")
private List<ProductSpaceVO> visibleProductSpaces;
@Schema(description = "当前用户权益摘要 VO")
@Data
public static class EntitlementSummaryVO {
@Schema(description = "当前套餐名")
private String planName;
@Schema(description = "剩余配额")
private Integer remainingQuota;
@Schema(description = "配额状态")
private String quotaStatus;
@Schema(description = "权益到期时间")
private LocalDateTime expiresAt;
}
@Schema(description = "产品空间入口 VO")
@Data
public static class ProductSpaceVO {
@Schema(description = "空间标识")
private String spaceKey;
@Schema(description = "空间显示名称")
private String spaceName;
@Schema(description = "空间入口路径")
private String entryPath;
}
}

View File

@ -0,0 +1,89 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 权益和配额详情 Response VO")
@Data
public class AppEntitlementRespVO {
@Schema(description = "当前套餐名")
private String planName;
@Schema(description = "权益来源说明")
private String entitlementSource;
@Schema(description = "权益列表")
private List<EntitlementVO> entitlements;
@Schema(description = "配额列表")
private List<QuotaVO> quotas;
@Schema(description = "限流信息")
private List<RateLimitVO> rateLimits;
@Schema(description = "发布能力")
private PublishCapabilityVO publishCapability;
@Schema(description = "权益到期时间")
private LocalDateTime expiresAt;
@Schema(description = "Account 权益项 VO")
@Data
public static class EntitlementVO {
@Schema(description = "权益 ID")
private String id;
@Schema(description = "权益资源类型")
private String resourceType;
@Schema(description = "额度上限")
private Integer limit;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余额度")
private Integer remaining;
@Schema(description = "权益来源")
private String source;
@Schema(description = "到期时间")
private LocalDateTime expiresAt;
}
@Schema(description = "Account 配额项 VO")
@Data
public static class QuotaVO {
@Schema(description = "配额 ID")
private String id;
@Schema(description = "配额资源类型")
private String resourceType;
@Schema(description = "总配额")
private Integer total;
@Schema(description = "已使用量")
private Integer used;
@Schema(description = "剩余配额")
private Integer remaining;
@Schema(description = "配额重置时间")
private LocalDateTime resetAt;
@Schema(description = "频率限制")
private Integer limitPerMinute;
}
@Schema(description = "限流信息 VO")
@Data
public static class RateLimitVO {
@Schema(description = "资源类型")
private String resourceType;
@Schema(description = "每分钟限制")
private Integer limitPerMinute;
@Schema(description = "当前用量")
private Integer currentUsage;
}
@Schema(description = "发布能力 VO")
@Data
public static class PublishCapabilityVO {
@Schema(description = "最大可发布资产数")
private Integer maxPublishableAssets;
@Schema(description = "当前已发布数量")
private Integer currentPublished;
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 外部调用详情 Response VO")
@Data
public class AppIntegrationCallDetailRespVO {
@Schema(description = "外部调用关联 ID")
private String correlationId;
@Schema(description = "调用状态")
private String status;
@Schema(description = "请求类型")
private String requestType;
@Schema(description = "重试组内的调用 ID 列表")
private List<String> retryGroup;
@Schema(description = "归属状态")
private String attributionStatus;
@Schema(description = "归属用户 ID")
private String attributedUserId;
@Schema(description = "首次调用时间")
private LocalDateTime createdAt;
@Schema(description = "最近重试时间")
private LocalDateTime lastRetryAt;
@Schema(description = "错误信息")
private String errorMessage;
}

View File

@ -0,0 +1,50 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 授权记录 Response VO")
@Data
public class AppLicenseRecordRespVO {
@Schema(description = "授权记录 ID")
private String licenseId;
@Schema(description = "资产类型")
private String assetType;
@Schema(description = "资产名称")
private String assetName;
@Schema(description = "发布者")
private String publisherName;
@Schema(description = "版本")
private String version;
@Schema(description = "许可范围摘要")
private String licenseScope;
@Schema(description = "授权状态")
private String licenseStatus;
@Schema(description = "动作策略")
private ActionPolicyVO actionPolicy;
@Schema(description = "获取时间")
private LocalDateTime acquiredAt;
@Schema(description = "到期时间")
private LocalDateTime expiresAt;
@Schema(description = "安装状态摘要")
private String installSummary;
@Schema(description = "绑定状态摘要")
private String bindingSummary;
@Schema(description = "异常原因")
private String anomalyReason;
@Schema(description = "授权动作策略 VO")
@Data
public static class ActionPolicyVO {
@Schema(description = "需要重验的原因列表")
private List<String> recheckReasons;
@Schema(description = "安装策略")
private String installPolicy;
@Schema(description = "绑定策略")
private String bindPolicy;
}
}

View File

@ -0,0 +1,13 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account New-API 绑定重验 Request VO")
@Data
public class AppNewApiBindingRecheckReqVO {
@Schema(description = "幂等键")
private String commandId;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account New-API 绑定重验 Response VO")
@Data
public class AppNewApiBindingRecheckRespVO {
@Schema(description = "异步任务 ID")
private String jobId;
@Schema(description = "任务初始状态")
private String status;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account New-API 绑定摘要 Response VO")
@Data
public class AppNewApiBindingSummaryRespVO {
@Schema(description = "绑定状态")
private String bindingStatus;
@Schema(description = "绑定时间")
private LocalDateTime boundAt;
@Schema(description = "最近同步时间")
private LocalDateTime lastSyncAt;
@Schema(description = "是否可以发起重验")
private Boolean canRecheck;
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 个人资料 Response VO")
@Data
public class AppProfileRespVO {
@Schema(description = "用户 ID")
private String userId;
@Schema(description = "昵称")
private String nickname;
@Schema(description = "头像 URL")
private String avatar;
@Schema(description = "公开署名")
private String publicPenName;
@Schema(description = "邮箱脱敏值")
private String emailMasked;
@Schema(description = "手机号脱敏值")
private String phoneMasked;
@Schema(description = "邮箱是否已验证")
private Boolean emailVerified;
@Schema(description = "手机号是否已验证")
private Boolean phoneVerified;
@Schema(description = "账号状态")
private String status;
@Schema(description = "注册时间")
private LocalDateTime createdAt;
@Schema(description = "最近资料更新时间")
private LocalDateTime lastProfileUpdatedAt;
@Schema(description = "资料版本号")
private Integer version;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account 修改资料 Request VO")
@Data
public class AppProfileUpdateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "期望资料版本号")
private Integer expectedVersion;
@Schema(description = "新昵称")
private String nickname;
@Schema(description = "新头像 URL")
private String avatar;
@Schema(description = "新公开署名")
private String publicPenName;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 发布记录 Response VO")
@Data
public class AppPublishRecordRespVO {
@Schema(description = "发布记录 ID")
private String recordId;
@Schema(description = "资产类型")
private String assetType;
@Schema(description = "资产名称")
private String assetName;
@Schema(description = "版本")
private String version;
@Schema(description = "审核状态")
private String reviewStatus;
@Schema(description = "市场状态")
private String marketStatus;
@Schema(description = "阻断原因摘要")
private String blockingReason;
@Schema(description = "提交时间")
private LocalDateTime submittedAt;
@Schema(description = "最近更新时间")
private LocalDateTime lastUpdatedAt;
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 购买记录 Response VO")
@Data
public class AppPurchaseRecordRespVO {
@Schema(description = "记录编号")
private String recordId;
@Schema(description = "资产类型")
private String assetType;
@Schema(description = "资产名称")
private String assetName;
@Schema(description = "购买类型")
private String purchaseType;
@Schema(description = "金额")
private BigDecimal amount;
@Schema(description = "购买状态")
private String status;
@Schema(description = "外部订单引用,已脱敏")
private String externalOrderRef;
@Schema(description = "授权结果摘要")
private String authorizationResultSummary;
@Schema(description = "购买时间")
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account 额度请求创建 Request VO")
@Data
public class AppQuotaRequestCreateReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "请求类型")
private String requestType;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account 额度请求创建结果 Response VO")
@Data
public class AppQuotaRequestResultRespVO {
@Schema(description = "额度请求 ID")
private String requestId;
@Schema(description = "外部调用关联 ID")
private String correlationId;
@Schema(description = "请求初始状态")
private String status;
}

View File

@ -0,0 +1,52 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
@Schema(description = "用户 APP - Account 用量摘要 Response VO")
@Data
public class AppUsageSummaryRespVO {
@Schema(description = "统计周期")
private String period;
@Schema(description = "总输入 Token 数")
private Integer totalInputTokens;
@Schema(description = "总输出 Token 数")
private Integer totalOutputTokens;
@Schema(description = "待归属记录数")
private Integer pendingAttributionCount;
@Schema(description = "计量异常记录数")
private Integer anomalyCount;
@Schema(description = "按模型的用量分布")
private Map<String, Integer> byModel;
@Schema(description = "按日期的用量分布")
private List<DateUsageVO> byDate;
@Schema(description = "按归属对象类型的用量分布")
private List<AttributionUsageVO> byAttribution;
@Schema(description = "日期用量 VO")
@Data
public static class DateUsageVO {
@Schema(description = "日期")
private LocalDate date;
@Schema(description = "输入 Token 数")
private Integer inputTokens;
@Schema(description = "输出 Token 数")
private Integer outputTokens;
}
@Schema(description = "归属用量 VO")
@Data
public static class AttributionUsageVO {
@Schema(description = "归属类型")
private String attributionType;
@Schema(description = "Token 数")
private Integer tokens;
@Schema(description = "显示标签")
private String label;
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 额度请求状态 Response VO")
@Data
public class QuotaRequestStatusRespVO {
@Schema(description = "额度请求 ID")
private String requestId;
@Schema(description = "外部调用关联 ID")
private String correlationId;
@Schema(description = "请求状态")
private String status;
@Schema(description = "失败原因")
private String failedReason;
@Schema(description = "完成时间")
private LocalDateTime completedAt;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - Account 安全事件确认 Request VO")
@Data
public class SecurityEventAcknowledgeReqVO {
@Schema(description = "幂等键")
private String commandId;
@Schema(description = "确认动作")
private String action;
@Schema(description = "用户备注")
private String note;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 安全事件确认结果 Response VO")
@Data
public class SecurityEventAcknowledgeResultRespVO {
@Schema(description = "安全事件 ID")
private String eventId;
@Schema(description = "是否确认成功")
private Boolean acknowledged;
@Schema(description = "确认动作")
private String action;
@Schema(description = "确认时间")
private LocalDateTime acknowledgedAt;
@Schema(description = "处理后风险摘要")
private String riskSummary;
@Schema(description = "后续建议")
private List<String> nextSteps;
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 APP - Account 安全事件详情 Response VO")
@Data
public class SecurityEventDetailRespVO {
@Schema(description = "安全事件 ID")
private String eventId;
@Schema(description = "事件类型")
private String eventType;
@Schema(description = "严重程度")
private String severity;
@Schema(description = "事件发生时间")
private LocalDateTime occurredAt;
@Schema(description = "来源 IP 地址,已脱敏")
private String sourceIp;
@Schema(description = "设备信息摘要")
private DeviceInfoVO deviceInfo;
@Schema(description = "影响范围描述")
private String affectedScope;
@Schema(description = "事件详细描述")
private String description;
@Schema(description = "建议处理措施列表")
private List<String> suggestedActions;
@Schema(description = "用户确认时间")
private LocalDateTime acknowledgedAt;
@Schema(description = "用户确认时选择的处理措施")
private String acknowledgedAction;
@Schema(description = "设备信息摘要 VO")
@Data
public static class DeviceInfoVO {
@Schema(description = "User-Agent 摘要")
private String userAgent;
@Schema(description = "平台")
private String platform;
@Schema(description = "地理位置摘要")
private String location;
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.controller.app.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - Account 安全事件摘要 Response VO")
@Data
public class SecurityEventSummaryRespVO {
@Schema(description = "安全事件 ID")
private String eventId;
@Schema(description = "事件类型")
private String eventType;
@Schema(description = "严重程度")
private String severity;
@Schema(description = "事件发生时间")
private LocalDateTime occurredAt;
@Schema(description = "事件简短描述")
private String description;
@Schema(description = "是否已确认")
private Boolean acknowledged;
}

View File

@ -0,0 +1,190 @@
package cn.iocoder.muse.module.member.convert.account;
import cn.iocoder.muse.framework.common.pojo.PageResult;
import cn.iocoder.muse.framework.common.util.json.JsonUtils;
import cn.iocoder.muse.module.member.controller.admin.account.vo.AdminPurchaseRecordRespVO;
import cn.iocoder.muse.module.member.controller.app.account.vo.AppProfileRespVO;
import cn.iocoder.muse.module.member.controller.app.account.vo.SecurityEventDetailRespVO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountProfileDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountRecordProjectionDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountSecurityEventAckDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberSecurityEventDO;
import com.fasterxml.jackson.databind.JsonNode;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
* Account DTO 装配器。
*
* <p>Account 读模型会承接多来源数据,Convert 层统一做 ID 字符串化和敏感字段脱敏,避免 Controller
* 或后续 Service 直接把 DO / JSONB 快照泄露给前端。</p>
*/
@Mapper
public interface AccountConvert {
AccountConvert INSTANCE = Mappers.getMapper(AccountConvert.class);
default AppProfileRespVO convertProfile(AccountProfileDO profile, String email, String phone,
Boolean emailVerified, Boolean phoneVerified, String status,
LocalDateTime createdAt) {
if (profile == null) {
return null;
}
AppProfileRespVO vo = new AppProfileRespVO();
vo.setUserId(stringId(profile.getAccountUserId()));
vo.setNickname(profile.getDisplayName());
vo.setAvatar(profile.getAvatarUrl());
vo.setPublicPenName(profile.getPublicPenName());
vo.setEmailMasked(maskEmail(email));
vo.setPhoneMasked(maskPhone(phone));
vo.setEmailVerified(emailVerified);
vo.setPhoneVerified(phoneVerified);
vo.setStatus(status);
vo.setCreatedAt(createdAt);
vo.setLastProfileUpdatedAt(profile.getUpdateTime());
vo.setVersion(profile.getProfileVersion());
return vo;
}
default PageResult<AdminPurchaseRecordRespVO> convertAdminPurchaseRecordPage(
PageResult<AccountRecordProjectionDO> pageResult) {
if (pageResult == null) {
return null;
}
List<AdminPurchaseRecordRespVO> list = pageResult.getList() == null ? Collections.emptyList()
: pageResult.getList().stream().map(this::convertAdminPurchaseRecord).toList();
return new PageResult<>(list, pageResult.getTotal());
}
default AdminPurchaseRecordRespVO convertAdminPurchaseRecord(AccountRecordProjectionDO projection) {
if (projection == null) {
return null;
}
JsonNode snapshot = parseSnapshot(projection.getProjectionSnapshot());
AdminPurchaseRecordRespVO vo = new AdminPurchaseRecordRespVO();
vo.setRecordId(projection.getRecordId());
vo.setUserId(stringId(projection.getAccountUserId()));
vo.setNickname(text(snapshot, "nickname"));
vo.setAssetType(text(snapshot, "assetType"));
vo.setAssetName(projection.getTitle());
vo.setPurchaseType(text(snapshot, "purchaseType"));
vo.setAmount(toAmount(projection.getAmount()));
vo.setStatus(projection.getSourceStatus());
vo.setExternalOrderRef(maskExternalRef(text(snapshot, "externalOrderRef")));
vo.setAuthorizationResult(text(snapshot, "authorizationResult"));
vo.setCreatedAt(firstNonNull(projection.getOccurredAt(), projection.getCreateTime()));
return vo;
}
default SecurityEventDetailRespVO convertSecurityEventDetail(MemberSecurityEventDO event,
AccountSecurityEventAckDO ack) {
if (event == null) {
return null;
}
JsonNode deviceNode = parseSnapshot(event.getDeviceInfo());
SecurityEventDetailRespVO vo = new SecurityEventDetailRespVO();
vo.setEventId(stringId(event.getId()));
vo.setEventType(event.getEventType());
vo.setSeverity(event.getSeverity());
vo.setOccurredAt(event.getCreateTime());
vo.setSourceIp(maskIp(event.getIpAddress()));
vo.setDeviceInfo(convertDeviceInfo(deviceNode));
vo.setAffectedScope(text(deviceNode, "affectedScope"));
vo.setDescription(text(deviceNode, "description"));
vo.setSuggestedActions(Collections.emptyList());
if (ack != null) {
vo.setAcknowledgedAt(ack.getCreateTime());
vo.setAcknowledgedAction(ack.getAction());
} else if (Boolean.TRUE.equals(event.getAcknowledged())) {
vo.setAcknowledgedAt(event.getAcknowledgedAt());
}
return vo;
}
default SecurityEventDetailRespVO.DeviceInfoVO convertDeviceInfo(JsonNode deviceNode) {
SecurityEventDetailRespVO.DeviceInfoVO vo = new SecurityEventDetailRespVO.DeviceInfoVO();
vo.setUserAgent(maskUserAgent(text(deviceNode, "userAgent")));
vo.setPlatform(text(deviceNode, "platform"));
vo.setLocation(text(deviceNode, "location"));
return vo;
}
default String stringId(Long id) {
return id == null ? null : String.valueOf(id);
}
default String maskEmail(String email) {
if (email == null || email.isBlank()) {
return email;
}
int atIndex = email.indexOf('@');
if (atIndex <= 0) {
return maskExternalRef(email);
}
String prefix = email.substring(0, 1);
return prefix + "***" + email.substring(atIndex);
}
default String maskPhone(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}
default String maskExternalRef(String value) {
if (value == null || value.length() <= 8) {
return value == null ? null : "***";
}
return value.substring(0, 4) + "***" + value.substring(value.length() - 4);
}
default String maskIp(String ip) {
if (ip == null || ip.isBlank()) {
return ip;
}
String[] parts = ip.split("\\.");
if (parts.length == 4) {
return parts[0] + "." + parts[1] + "." + parts[2] + ".***";
}
return "***";
}
default String maskUserAgent(String userAgent) {
if (userAgent == null || userAgent.isBlank()) {
return userAgent;
}
int blankIndex = userAgent.indexOf(' ');
if (blankIndex < 0) {
return userAgent;
}
return userAgent.substring(0, blankIndex) + " ***";
}
default JsonNode parseSnapshot(String json) {
if (json == null || json.isBlank()) {
return null;
}
return JsonUtils.parseTree(json);
}
default String text(JsonNode node, String fieldName) {
if (node == null || !node.hasNonNull(fieldName)) {
return null;
}
return node.get(fieldName).asText();
}
default BigDecimal toAmount(Long amount) {
return amount == null ? null : BigDecimal.valueOf(amount);
}
default <T> T firstNonNull(T first, T second) {
return first != null ? first : second;
}
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* Account 余额快照 DO。
*/
@TableName(value = "muse_account_balance_snapshot", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountBalanceSnapshotDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String sourceType;
private String sourceId;
private String balanceType;
private Long totalAmount;
private Long usedAmount;
private Long remainingAmount;
private LocalDateTime snapshotAt;
private String correlationId;
/** 快照中可能带上游细节,Account VO 只暴露合同允许字段。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String snapshot;
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Account 调用归因明细 DO。
*/
@TableName(value = "muse_account_call_attribution_item", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountCallAttributionItemDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String jobId;
private Long integrationCallId;
private Long accountUserId;
private String correlationId;
private String callId;
private String attributionType;
private String attributedTargetType;
private Long attributedTargetId;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String sourceSnapshot;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String attributionSnapshot;
private String status;
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Account 调用归因任务 DO。
*/
@TableName(value = "muse_account_call_attribution_job", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountCallAttributionJobDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String jobId;
private Long accountUserId;
private Long actorUserId;
private String commandId;
private String requestHash;
private String correlationId;
private String status;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String sourceSnapshot;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String resultSnapshot;
private String errorCode;
private String errorMessage;
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* Account 导出下载凭证 DO。
*/
@TableName(value = "muse_account_download_credential", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountDownloadCredentialDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String credentialHash;
private Long accountUserId;
private String taskId;
private LocalDateTime expiresAt;
private LocalDateTime consumedAt;
private LocalDateTime revokedAt;
private Boolean sourceBlocked;
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* Account 导出任务 DO。
*/
@TableName(value = "muse_account_export_task", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountExportTaskDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String taskId;
private Long accountUserId;
private String commandId;
private String requestHash;
private String exportType;
private String status;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String sourceSnapshot;
private String fileRef;
private Boolean sourceBlocked;
private String errorCode;
private String errorMessage;
private LocalDateTime completedAt;
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* Account 外部集成调用 DO。
*/
@TableName(value = "muse_account_integration_call", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountIntegrationCallDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String correlationId;
private Long accountUserId;
private String provider;
private String operationId;
private Long bindingId;
private String jobId;
private String commandId;
private String requestHash;
private String status;
private Integer retryCount;
private LocalDateTime nextRetryAt;
private String externalCallId;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String requestSummary;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String responseSummary;
private String errorCode;
private String errorMessage;
private LocalDateTime startedAt;
private LocalDateTime finishedAt;
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Account 资料扩展 DO。
*
* <p>资料扩展和乐观锁版本独立放在 Account 子域,避免污染 member_user 的通用会员事实。</p>
*/
@TableName(value = "muse_account_profile", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountProfileDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String displayName;
private String publicPenName;
private String avatarUrl;
private String bio;
/** 资料快照保留 JSONB 原文,后续服务层按 OpenAPI DTO 做白名单输出。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String profileSnapshot;
private Integer profileVersion;
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Account 额度请求 DO。
*/
@TableName(value = "muse_account_quota_request", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountQuotaRequestDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String requestId;
private Long accountUserId;
private Long requestedBy;
private String side;
private String quotaType;
private Long requestedAmount;
private String status;
private String commandId;
private String requestHash;
private String correlationId;
private Long integrationCallId;
private String failureReason;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String resultSnapshot;
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* Account 购买/授权/发布记录投影 DO。
*/
@TableName(value = "muse_account_record_projection", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountRecordProjectionDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String recordType;
private String recordId;
private String sourceTable;
private String sourceId;
private String sourceRevision;
private String sourceStatus;
private String title;
private Long amount;
private LocalDateTime occurredAt;
private String correlationId;
/** 投影快照只作为白名单 DTO 装配来源,不能原样返回给前端。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String projectionSnapshot;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* Account 安全事件确认 DO。
*/
@TableName(value = "muse_account_security_event_ack", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountSecurityEventAckDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long eventId;
private Long accountUserId;
private Long actorUserId;
private String action;
private String note;
private String commandId;
private String requestHash;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String riskSummary;
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* 会员权益调整审计 DO。
*/
@TableName(value = "muse_member_entitlement_audit_log", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberEntitlementAuditLogDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String changeType;
private String sourceOwner;
private Long sourceId;
private String idempotencyKey;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String beforeValueSnapshot;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String deltaValueSnapshot;
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String afterValueSnapshot;
private String reasonCode;
private String reasonMessage;
private Long operatorUserId;
private Long auditEventId;
}

View File

@ -0,0 +1,49 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 会员权益 DO。
*/
@TableName(value = "muse_member_entitlement", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberEntitlementDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String planType;
private LocalDateTime planStartedAt;
private LocalDateTime planExpiresAt;
private Integer aiCallQuota;
private Integer aiCallUsed;
private Long storageQuotaMb;
private Long storageUsedMb;
private Integer kbCountQuota;
private Integer workCountQuota;
private Integer exportQuota;
/** features_json 是权益扩展能力快照,Task 3 只建读模型骨架。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String featuresJson;
private Integer revision;
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 会员 New-API 绑定 DO。
*/
@TableName(value = "muse_member_new_api_binding", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberNewApiBindingDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String newapiUserId;
private String bindingStatus;
private String commandId;
private LocalDateTime lastSyncAt;
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 会员配额 DO。
*/
@TableName(value = "muse_member_quota", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberQuotaDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String quotaType;
private Long totalAmount;
private Long usedAmount;
private String resetCycle;
private LocalDateTime lastResetAt;
private LocalDateTime nextResetAt;
private Integer revision;
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 会员安全事件 DO。
*/
@TableName(value = "muse_member_security_event", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberSecurityEventDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String eventType;
private String severity;
/** 设备信息包含 userAgent 等敏感值,Controller 不应直接返回该 JSON。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String deviceInfo;
private String ipAddress;
private Boolean acknowledged;
private LocalDateTime acknowledgedAt;
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.muse.module.member.dal.dataobject.account;
import cn.iocoder.muse.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.muse.module.member.dal.type.JsonbStringTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* 会员用量记录 DO。
*/
@TableName(value = "muse_member_usage_record", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberUsageRecordDO extends TenantBaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private Long accountUserId;
private String usageType;
private Long workId;
private Long taskId;
private Integer tokenInput;
private Integer tokenOutput;
private String costReference;
private String gatewayRequestId;
private String correlationId;
/** 归因快照是 Account 读模型来源,输出前必须由 Convert/Service 过滤。 */
@TableField(typeHandler = JsonbStringTypeHandler.class)
private String attributionSnapshot;
private String status;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountBalanceSnapshotDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Account 余额快照 Mapper。
*/
@Mapper
public interface AccountBalanceSnapshotMapper extends BaseMapperX<AccountBalanceSnapshotDO> {
default List<AccountBalanceSnapshotDO> selectListByAccountUserId(Long accountUserId) {
return selectList(new LambdaQueryWrapperX<AccountBalanceSnapshotDO>()
.eq(AccountBalanceSnapshotDO::getAccountUserId, accountUserId)
.orderByDesc(AccountBalanceSnapshotDO::getSnapshotAt));
}
default List<AccountBalanceSnapshotDO> selectListByCorrelationId(String correlationId) {
return selectList(AccountBalanceSnapshotDO::getCorrelationId, correlationId);
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountCallAttributionItemDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Account 调用归因明细 Mapper。
*/
@Mapper
public interface AccountCallAttributionItemMapper extends BaseMapperX<AccountCallAttributionItemDO> {
default List<AccountCallAttributionItemDO> selectListByJobId(String jobId) {
return selectList(AccountCallAttributionItemDO::getJobId, jobId);
}
default List<AccountCallAttributionItemDO> selectListByCorrelationId(String correlationId) {
return selectList(AccountCallAttributionItemDO::getCorrelationId, correlationId);
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountCallAttributionJobDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Account 调用归因任务 Mapper。
*/
@Mapper
public interface AccountCallAttributionJobMapper extends BaseMapperX<AccountCallAttributionJobDO> {
default AccountCallAttributionJobDO selectByJobId(String jobId) {
return selectOne(AccountCallAttributionJobDO::getJobId, jobId);
}
default AccountCallAttributionJobDO selectByCommandId(String commandId) {
return selectOne(AccountCallAttributionJobDO::getCommandId, commandId);
}
default AccountCallAttributionJobDO selectByCorrelationId(String correlationId) {
return selectOne(AccountCallAttributionJobDO::getCorrelationId, correlationId);
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountDownloadCredentialDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Account 下载凭证 Mapper。
*/
@Mapper
public interface AccountDownloadCredentialMapper extends BaseMapperX<AccountDownloadCredentialDO> {
default AccountDownloadCredentialDO selectByCredentialHash(String credentialHash) {
return selectOne(AccountDownloadCredentialDO::getCredentialHash, credentialHash);
}
default List<AccountDownloadCredentialDO> selectListByTaskId(String taskId) {
return selectList(AccountDownloadCredentialDO::getTaskId, taskId);
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountExportTaskDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Account 导出任务 Mapper。
*/
@Mapper
public interface AccountExportTaskMapper extends BaseMapperX<AccountExportTaskDO> {
default AccountExportTaskDO selectByTaskId(String taskId) {
return selectOne(AccountExportTaskDO::getTaskId, taskId);
}
default AccountExportTaskDO selectByCommandId(String commandId) {
return selectOne(AccountExportTaskDO::getCommandId, commandId);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountIntegrationCallDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Account 外部集成调用 Mapper。
*/
@Mapper
public interface AccountIntegrationCallMapper extends BaseMapperX<AccountIntegrationCallDO> {
default AccountIntegrationCallDO selectByCorrelationId(String correlationId) {
return selectOne(AccountIntegrationCallDO::getCorrelationId, correlationId);
}
default List<AccountIntegrationCallDO> selectListByAccountUserId(Long accountUserId) {
return selectList(new LambdaQueryWrapperX<AccountIntegrationCallDO>()
.eq(AccountIntegrationCallDO::getAccountUserId, accountUserId)
.orderByDesc(AccountIntegrationCallDO::getCreateTime));
}
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountProfileDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Account 资料扩展 Mapper。
*/
@Mapper
public interface AccountProfileMapper extends BaseMapperX<AccountProfileDO> {
default AccountProfileDO selectByAccountUserId(Long accountUserId) {
return selectOne(AccountProfileDO::getAccountUserId, accountUserId);
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountQuotaRequestDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Account 额度请求 Mapper。
*/
@Mapper
public interface AccountQuotaRequestMapper extends BaseMapperX<AccountQuotaRequestDO> {
default AccountQuotaRequestDO selectByRequestId(String requestId) {
return selectOne(AccountQuotaRequestDO::getRequestId, requestId);
}
default AccountQuotaRequestDO selectByCommandId(String commandId) {
return selectOne(AccountQuotaRequestDO::getCommandId, commandId);
}
default AccountQuotaRequestDO selectByCorrelationId(String correlationId) {
return selectOne(AccountQuotaRequestDO::getCorrelationId, correlationId);
}
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountRecordProjectionDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Account 购买/授权/发布记录投影 Mapper。
*/
@Mapper
public interface AccountRecordProjectionMapper extends BaseMapperX<AccountRecordProjectionDO> {
default List<AccountRecordProjectionDO> selectListByAccountUserIdAndRecordType(Long accountUserId,
String recordType) {
return selectList(new LambdaQueryWrapperX<AccountRecordProjectionDO>()
.eq(AccountRecordProjectionDO::getAccountUserId, accountUserId)
.eq(AccountRecordProjectionDO::getRecordType, recordType)
.orderByDesc(AccountRecordProjectionDO::getOccurredAt));
}
default AccountRecordProjectionDO selectByRecordTypeAndRecordId(String recordType, String recordId) {
return selectOne(AccountRecordProjectionDO::getRecordType, recordType,
AccountRecordProjectionDO::getRecordId, recordId);
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountSecurityEventAckDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Account 安全事件确认 Mapper。
*/
@Mapper
public interface AccountSecurityEventAckMapper extends BaseMapperX<AccountSecurityEventAckDO> {
default AccountSecurityEventAckDO selectByEventId(Long eventId) {
return selectOne(AccountSecurityEventAckDO::getEventId, eventId);
}
default AccountSecurityEventAckDO selectByCommandId(String commandId) {
return selectOne(AccountSecurityEventAckDO::getCommandId, commandId);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberEntitlementAuditLogDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员权益调整审计 Mapper。
*/
@Mapper
public interface MemberEntitlementAuditLogMapper extends BaseMapperX<MemberEntitlementAuditLogDO> {
default List<MemberEntitlementAuditLogDO> selectListByAccountUserId(Long accountUserId) {
return selectList(new LambdaQueryWrapperX<MemberEntitlementAuditLogDO>()
.eq(MemberEntitlementAuditLogDO::getAccountUserId, accountUserId)
.orderByDesc(MemberEntitlementAuditLogDO::getCreateTime));
}
default MemberEntitlementAuditLogDO selectByIdempotencyKey(String idempotencyKey) {
return selectOne(MemberEntitlementAuditLogDO::getIdempotencyKey, idempotencyKey);
}
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberEntitlementDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员权益 Mapper。
*/
@Mapper
public interface MemberEntitlementMapper extends BaseMapperX<MemberEntitlementDO> {
default MemberEntitlementDO selectByAccountUserId(Long accountUserId) {
return selectOne(MemberEntitlementDO::getAccountUserId, accountUserId);
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberNewApiBindingDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员 New-API 绑定 Mapper。
*/
@Mapper
public interface MemberNewApiBindingMapper extends BaseMapperX<MemberNewApiBindingDO> {
default MemberNewApiBindingDO selectByAccountUserId(Long accountUserId) {
return selectOne(MemberNewApiBindingDO::getAccountUserId, accountUserId);
}
default MemberNewApiBindingDO selectByCommandId(String commandId) {
return selectOne(MemberNewApiBindingDO::getCommandId, commandId);
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberQuotaDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员配额 Mapper。
*/
@Mapper
public interface MemberQuotaMapper extends BaseMapperX<MemberQuotaDO> {
default List<MemberQuotaDO> selectListByAccountUserId(Long accountUserId) {
return selectList(MemberQuotaDO::getAccountUserId, accountUserId);
}
default MemberQuotaDO selectByAccountUserIdAndQuotaType(Long accountUserId, String quotaType) {
return selectOne(MemberQuotaDO::getAccountUserId, accountUserId, MemberQuotaDO::getQuotaType, quotaType);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberSecurityEventDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员安全事件 Mapper。
*/
@Mapper
public interface MemberSecurityEventMapper extends BaseMapperX<MemberSecurityEventDO> {
default List<MemberSecurityEventDO> selectListByAccountUserId(Long accountUserId) {
return selectList(new LambdaQueryWrapperX<MemberSecurityEventDO>()
.eq(MemberSecurityEventDO::getAccountUserId, accountUserId)
.orderByDesc(MemberSecurityEventDO::getCreateTime));
}
default MemberSecurityEventDO selectByIdAndAccountUserId(Long id, Long accountUserId) {
return selectOne(MemberSecurityEventDO::getId, id, MemberSecurityEventDO::getAccountUserId, accountUserId);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.muse.module.member.dal.mysql.account;
import cn.iocoder.muse.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.muse.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberUsageRecordDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 会员用量记录 Mapper。
*/
@Mapper
public interface MemberUsageRecordMapper extends BaseMapperX<MemberUsageRecordDO> {
default List<MemberUsageRecordDO> selectListByAccountUserId(Long accountUserId) {
return selectList(new LambdaQueryWrapperX<MemberUsageRecordDO>()
.eq(MemberUsageRecordDO::getAccountUserId, accountUserId)
.orderByDesc(MemberUsageRecordDO::getCreateTime));
}
default List<MemberUsageRecordDO> selectListByCorrelationId(String correlationId) {
return selectList(MemberUsageRecordDO::getCorrelationId, correlationId);
}
}

View File

@ -0,0 +1,154 @@
package cn.iocoder.muse.module.member.convert.account;
import cn.iocoder.muse.framework.common.pojo.PageResult;
import cn.iocoder.muse.module.member.controller.admin.account.vo.AdminPurchaseRecordRespVO;
import cn.iocoder.muse.module.member.controller.app.account.vo.AppProfileRespVO;
import cn.iocoder.muse.module.member.controller.app.account.vo.SecurityEventDetailRespVO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountProfileDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountRecordProjectionDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.AccountSecurityEventAckDO;
import cn.iocoder.muse.module.member.dal.dataobject.account.MemberSecurityEventDO;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
/**
* Account DTO 装配测试。
*/
class AccountConvertTest {
private final AccountConvert convert = AccountConvert.INSTANCE;
@Test
void should_convertProfileWithStringIdAndMaskedContacts() {
LocalDateTime createdAt = LocalDateTime.of(2026, 5, 28, 10, 0);
LocalDateTime updatedAt = LocalDateTime.of(2026, 5, 28, 11, 0);
AccountProfileDO profile = AccountProfileDO.builder()
.accountUserId(1001L)
.displayName("Muse 用户")
.publicPenName("MusePen")
.avatarUrl("https://example.test/avatar.png")
.profileVersion(3)
.build();
profile.setUpdateTime(updatedAt);
AppProfileRespVO vo = convert.convertProfile(profile, "alice@example.com", "13812345678",
true, false, "active", createdAt);
assertEquals("1001", vo.getUserId());
assertEquals("Muse 用户", vo.getNickname());
assertEquals("MusePen", vo.getPublicPenName());
assertEquals("a***@example.com", vo.getEmailMasked());
assertEquals("138****5678", vo.getPhoneMasked());
assertEquals(true, vo.getEmailVerified());
assertEquals(false, vo.getPhoneVerified());
assertEquals("active", vo.getStatus());
assertEquals(createdAt, vo.getCreatedAt());
assertEquals(updatedAt, vo.getLastProfileUpdatedAt());
assertEquals(3, vo.getVersion());
}
@Test
void should_keepPageListAndTotal_when_convertingPurchaseRecords() {
AccountRecordProjectionDO first = purchaseProjection("purchase-1", "order-1", "授权成功");
AccountRecordProjectionDO second = purchaseProjection("purchase-2", "order-2", "发放完成");
PageResult<AccountRecordProjectionDO> page = new PageResult<>(List.of(first, second), 9L);
PageResult<AdminPurchaseRecordRespVO> result = convert.convertAdminPurchaseRecordPage(page);
assertEquals(9L, result.getTotal());
assertEquals(2, result.getList().size());
assertEquals("purchase-1", result.getList().get(0).getRecordId());
assertEquals("purchase-2", result.getList().get(1).getRecordId());
}
@Test
void should_maskExternalOrderRefAndKeepNormalProjectionMetadata() {
AccountRecordProjectionDO projection = purchaseProjection("purchase-1",
"EXT-ORDER-SECRET-123456", "授权发放完成");
AdminPurchaseRecordRespVO vo = convert.convertAdminPurchaseRecord(projection);
assertEquals("purchase-1", vo.getRecordId());
assertEquals("1001", vo.getUserId());
assertEquals("agent", vo.getAssetType());
assertEquals("Agent Pro", vo.getAssetName());
assertEquals("paid", vo.getPurchaseType());
assertNotEquals("EXT-ORDER-SECRET-123456", vo.getExternalOrderRef());
assertFalse(vo.getExternalOrderRef().contains("SECRET"));
assertEquals("授权发放完成", vo.getAuthorizationResult());
}
@Test
void should_maskSecuritySourceAndDeviceButKeepPlainDeviceMetadata() {
LocalDateTime occurredAt = LocalDateTime.of(2026, 5, 28, 12, 0);
MemberSecurityEventDO event = MemberSecurityEventDO.builder()
.id(88L)
.accountUserId(1001L)
.eventType("login_anomaly")
.severity("warning")
.ipAddress("203.0.113.10")
.deviceInfo("{\"userAgent\":\"Mozilla/5.0 SecretAgent\",\"platform\":\"macOS\",\"location\":\"Shanghai\"}")
.acknowledged(false)
.build();
event.setCreateTime(occurredAt);
SecurityEventDetailRespVO vo = convert.convertSecurityEventDetail(event, null);
assertEquals("88", vo.getEventId());
assertEquals("login_anomaly", vo.getEventType());
assertEquals("warning", vo.getSeverity());
assertEquals(occurredAt, vo.getOccurredAt());
assertEquals("203.0.113.***", vo.getSourceIp());
assertFalse(vo.getDeviceInfo().getUserAgent().contains("SecretAgent"));
assertEquals("macOS", vo.getDeviceInfo().getPlatform());
assertEquals("Shanghai", vo.getDeviceInfo().getLocation());
}
@Test
void should_applySecurityAcknowledgementFields_when_ackExists() {
LocalDateTime acknowledgedAt = LocalDateTime.of(2026, 5, 28, 13, 0);
MemberSecurityEventDO event = MemberSecurityEventDO.builder()
.id(88L)
.accountUserId(1001L)
.eventType("sensitive_export")
.severity("critical")
.ipAddress("203.0.113.10")
.deviceInfo("{}")
.acknowledged(true)
.build();
event.setCreateTime(LocalDateTime.of(2026, 5, 28, 12, 0));
AccountSecurityEventAckDO ack = AccountSecurityEventAckDO.builder()
.eventId(88L)
.action("password_changed")
.build();
ack.setCreateTime(acknowledgedAt);
SecurityEventDetailRespVO vo = convert.convertSecurityEventDetail(event, ack);
assertEquals(acknowledgedAt, vo.getAcknowledgedAt());
assertEquals("password_changed", vo.getAcknowledgedAction());
}
private AccountRecordProjectionDO purchaseProjection(String recordId, String externalOrderRef,
String authorizationResult) {
AccountRecordProjectionDO projection = AccountRecordProjectionDO.builder()
.accountUserId(1001L)
.recordType("purchase")
.recordId(recordId)
.title("Agent Pro")
.amount(1999L)
.sourceStatus("completed")
.projectionSnapshot("{\"nickname\":\"Muse 用户\",\"assetType\":\"agent\",\"purchaseType\":\"paid\","
+ "\"externalOrderRef\":\"" + externalOrderRef + "\","
+ "\"authorizationResult\":\"" + authorizationResult + "\"}")
.build();
projection.setCreateTime(LocalDateTime.of(2026, 5, 28, 10, 30));
return projection;
}
}