new
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -24,3 +24,48 @@ type DecryptCommand struct {
|
||||
EncryptedData string `json:"encrypted_data" binding:"required"`
|
||||
SecretKey string `json:"secret_key" binding:"required"`
|
||||
}
|
||||
|
||||
// SaveApiCallCommand 保存API调用命令
|
||||
type SaveApiCallCommand struct {
|
||||
ApiCallID string `json:"api_call_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
Status string `json:"status"`
|
||||
Cost float64 `json:"cost"`
|
||||
ErrorType string `json:"error_type"`
|
||||
ErrorMsg string `json:"error_msg"`
|
||||
ClientIP string `json:"client_ip"`
|
||||
}
|
||||
|
||||
// ProcessDeductionCommand 处理扣款命令
|
||||
type ProcessDeductionCommand struct {
|
||||
UserID string `json:"user_id"`
|
||||
Amount string `json:"amount"`
|
||||
ApiCallID string `json:"api_call_id"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
}
|
||||
|
||||
// UpdateUsageStatsCommand 更新使用统计命令
|
||||
type UpdateUsageStatsCommand struct {
|
||||
SubscriptionID string `json:"subscription_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
Increment int `json:"increment"`
|
||||
}
|
||||
|
||||
// RecordApiLogCommand 记录API日志命令
|
||||
type RecordApiLogCommand struct {
|
||||
TransactionID string `json:"transaction_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ApiName string `json:"api_name"`
|
||||
ClientIP string `json:"client_ip"`
|
||||
ResponseSize int64 `json:"response_size"`
|
||||
}
|
||||
|
||||
// ProcessCompensationCommand 处理补偿命令
|
||||
type ProcessCompensationCommand struct {
|
||||
TransactionID string `json:"transaction_id"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
96
internal/application/api/dto/api_call_validation.go
Normal file
96
internal/application/api/dto/api_call_validation.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
api_entities "tyapi-server/internal/domains/api/entities"
|
||||
product_entities "tyapi-server/internal/domains/product/entities"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// ApiCallValidationResult API调用验证结果
|
||||
type ApiCallValidationResult struct {
|
||||
UserID string `json:"user_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
SubscriptionID string `json:"subscription_id"`
|
||||
Amount decimal.Decimal `json:"amount"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
IsValid bool `json:"is_valid"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
|
||||
// 新增字段
|
||||
ContractCode string `json:"contract_code"`
|
||||
ApiCall *api_entities.ApiCall `json:"api_call"`
|
||||
RequestParams map[string]interface{} `json:"request_params"`
|
||||
Product *product_entities.Product `json:"product"`
|
||||
}
|
||||
|
||||
// GetUserID 获取用户ID
|
||||
func (r *ApiCallValidationResult) GetUserID() string {
|
||||
return r.UserID
|
||||
}
|
||||
|
||||
// GetProductID 获取产品ID
|
||||
func (r *ApiCallValidationResult) GetProductID() string {
|
||||
return r.ProductID
|
||||
}
|
||||
|
||||
// GetSubscriptionID 获取订阅ID
|
||||
func (r *ApiCallValidationResult) GetSubscriptionID() string {
|
||||
return r.SubscriptionID
|
||||
}
|
||||
|
||||
// GetAmount 获取金额
|
||||
func (r *ApiCallValidationResult) GetAmount() decimal.Decimal {
|
||||
return r.Amount
|
||||
}
|
||||
|
||||
// GetSecretKey 获取密钥
|
||||
func (r *ApiCallValidationResult) GetSecretKey() string {
|
||||
return r.SecretKey
|
||||
}
|
||||
|
||||
// IsValidResult 检查是否有效
|
||||
func (r *ApiCallValidationResult) IsValidResult() bool {
|
||||
return r.IsValid
|
||||
}
|
||||
|
||||
// GetErrorMessage 获取错误消息
|
||||
func (r *ApiCallValidationResult) GetErrorMessage() string {
|
||||
return r.ErrorMessage
|
||||
}
|
||||
|
||||
// NewApiCallValidationResult 创建新的API调用验证结果
|
||||
func NewApiCallValidationResult() *ApiCallValidationResult {
|
||||
return &ApiCallValidationResult{
|
||||
IsValid: true,
|
||||
RequestParams: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// SetApiUser 设置API用户
|
||||
func (r *ApiCallValidationResult) SetApiUser(apiUser *api_entities.ApiUser) {
|
||||
r.UserID = apiUser.UserId
|
||||
r.SecretKey = apiUser.SecretKey
|
||||
}
|
||||
|
||||
// SetProduct 设置产品
|
||||
func (r *ApiCallValidationResult) SetProduct(product *product_entities.Product) {
|
||||
r.ProductID = product.ID
|
||||
r.Amount = product.Price
|
||||
r.Product = product
|
||||
}
|
||||
|
||||
// SetApiCall 设置API调用
|
||||
func (r *ApiCallValidationResult) SetApiCall(apiCall *api_entities.ApiCall) {
|
||||
r.ApiCall = apiCall
|
||||
}
|
||||
|
||||
// SetRequestParams 设置请求参数
|
||||
func (r *ApiCallValidationResult) SetRequestParams(params map[string]interface{}) {
|
||||
r.RequestParams = params
|
||||
}
|
||||
|
||||
// SetContractCode 设置合同代码
|
||||
func (r *ApiCallValidationResult) SetContractCode(code string) {
|
||||
r.ContractCode = code
|
||||
}
|
||||
@@ -4,38 +4,46 @@ import "errors"
|
||||
|
||||
// API调用相关错误类型
|
||||
var (
|
||||
ErrQueryEmpty = errors.New("查询为空")
|
||||
ErrSystem = errors.New("接口异常")
|
||||
ErrDecryptFail = errors.New("解密失败")
|
||||
ErrRequestParam = errors.New("请求参数结构不正确")
|
||||
ErrInvalidParam = errors.New("参数校验不正确")
|
||||
ErrInvalidIP = errors.New("未经授权的IP")
|
||||
ErrMissingAccessId = errors.New("缺少Access-Id")
|
||||
ErrInvalidAccessId = errors.New("未经授权的AccessId")
|
||||
ErrFrozenAccount = errors.New("账户已冻结")
|
||||
ErrArrears = errors.New("账户余额不足,无法请求")
|
||||
ErrProductNotFound = errors.New("产品不存在")
|
||||
ErrProductDisabled = errors.New("产品已停用")
|
||||
ErrNotSubscribed = errors.New("未订阅此产品")
|
||||
ErrBusiness = errors.New("业务失败")
|
||||
ErrQueryEmpty = errors.New("查询为空")
|
||||
ErrSystem = errors.New("接口异常")
|
||||
ErrDecryptFail = errors.New("解密失败")
|
||||
ErrRequestParam = errors.New("请求参数结构不正确")
|
||||
ErrInvalidParam = errors.New("参数校验不正确")
|
||||
ErrInvalidIP = errors.New("未经授权的IP")
|
||||
ErrMissingAccessId = errors.New("缺少Access-Id")
|
||||
ErrInvalidAccessId = errors.New("未经授权的AccessId")
|
||||
ErrFrozenAccount = errors.New("账户已冻结")
|
||||
ErrArrears = errors.New("账户余额不足,无法请求")
|
||||
ErrInsufficientBalance = errors.New("钱包余额不足")
|
||||
ErrProductNotFound = errors.New("产品不存在")
|
||||
ErrProductDisabled = errors.New("产品已停用")
|
||||
ErrNotSubscribed = errors.New("未订阅此产品")
|
||||
ErrProductNotSubscribed = errors.New("未订阅此产品")
|
||||
ErrSubscriptionExpired = errors.New("订阅已过期")
|
||||
ErrSubscriptionSuspended = errors.New("订阅已暂停")
|
||||
ErrBusiness = errors.New("业务失败")
|
||||
)
|
||||
|
||||
// 错误码映射 - 严格按照用户要求
|
||||
var ErrorCodeMap = map[error]int{
|
||||
ErrQueryEmpty: 1000,
|
||||
ErrSystem: 1001,
|
||||
ErrDecryptFail: 1002,
|
||||
ErrRequestParam: 1003,
|
||||
ErrInvalidParam: 1003,
|
||||
ErrInvalidIP: 1004,
|
||||
ErrMissingAccessId: 1005,
|
||||
ErrInvalidAccessId: 1006,
|
||||
ErrFrozenAccount: 1007,
|
||||
ErrArrears: 1007,
|
||||
ErrProductNotFound: 1008,
|
||||
ErrProductDisabled: 1008,
|
||||
ErrNotSubscribed: 1008,
|
||||
ErrBusiness: 2001,
|
||||
ErrQueryEmpty: 1000,
|
||||
ErrSystem: 1001,
|
||||
ErrDecryptFail: 1002,
|
||||
ErrRequestParam: 1003,
|
||||
ErrInvalidParam: 1003,
|
||||
ErrInvalidIP: 1004,
|
||||
ErrMissingAccessId: 1005,
|
||||
ErrInvalidAccessId: 1006,
|
||||
ErrFrozenAccount: 1007,
|
||||
ErrArrears: 1007,
|
||||
ErrInsufficientBalance: 1007,
|
||||
ErrProductNotFound: 1008,
|
||||
ErrProductDisabled: 1008,
|
||||
ErrNotSubscribed: 1008,
|
||||
ErrProductNotSubscribed: 1008,
|
||||
ErrSubscriptionExpired: 1008,
|
||||
ErrSubscriptionSuspended: 1008,
|
||||
ErrBusiness: 2001,
|
||||
}
|
||||
|
||||
// GetErrorCode 获取错误对应的错误码
|
||||
|
||||
@@ -10,20 +10,21 @@ import (
|
||||
"tyapi-server/internal/domains/article/repositories"
|
||||
repoQueries "tyapi-server/internal/domains/article/repositories/queries"
|
||||
"tyapi-server/internal/domains/article/services"
|
||||
"tyapi-server/internal/infrastructure/task"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
task_entities "tyapi-server/internal/infrastructure/task/entities"
|
||||
task_interfaces "tyapi-server/internal/infrastructure/task/interfaces"
|
||||
shared_interfaces "tyapi-server/internal/shared/interfaces"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ArticleApplicationServiceImpl 文章应用服务实现
|
||||
type ArticleApplicationServiceImpl struct {
|
||||
articleRepo repositories.ArticleRepository
|
||||
categoryRepo repositories.CategoryRepository
|
||||
tagRepo repositories.TagRepository
|
||||
articleService *services.ArticleService
|
||||
asynqClient *task.AsynqClient
|
||||
logger *zap.Logger
|
||||
articleRepo repositories.ArticleRepository
|
||||
categoryRepo repositories.CategoryRepository
|
||||
tagRepo repositories.TagRepository
|
||||
articleService *services.ArticleService
|
||||
taskManager task_interfaces.TaskManager
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewArticleApplicationService 创建文章应用服务
|
||||
@@ -32,7 +33,7 @@ func NewArticleApplicationService(
|
||||
categoryRepo repositories.CategoryRepository,
|
||||
tagRepo repositories.TagRepository,
|
||||
articleService *services.ArticleService,
|
||||
asynqClient *task.AsynqClient,
|
||||
taskManager task_interfaces.TaskManager,
|
||||
logger *zap.Logger,
|
||||
) ArticleApplicationService {
|
||||
return &ArticleApplicationServiceImpl{
|
||||
@@ -40,7 +41,7 @@ func NewArticleApplicationService(
|
||||
categoryRepo: categoryRepo,
|
||||
tagRepo: tagRepo,
|
||||
articleService: articleService,
|
||||
asynqClient: asynqClient,
|
||||
taskManager: taskManager,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@@ -337,32 +338,37 @@ func (s *ArticleApplicationServiceImpl) SchedulePublishArticle(ctx context.Conte
|
||||
return fmt.Errorf("文章不存在: %w", err)
|
||||
}
|
||||
|
||||
// 3. 如果已有定时任务,先取消
|
||||
if article.TaskID != "" {
|
||||
if err := s.asynqClient.CancelScheduledTask(ctx, article.TaskID); err != nil {
|
||||
s.logger.Warn("取消旧定时任务失败", zap.String("task_id", article.TaskID), zap.Error(err))
|
||||
}
|
||||
// 3. 取消旧任务
|
||||
if err := s.taskManager.CancelTask(ctx, cmd.ID); err != nil {
|
||||
s.logger.Warn("取消旧任务失败", zap.String("article_id", cmd.ID), zap.Error(err))
|
||||
}
|
||||
|
||||
// 4. 调度定时发布任务
|
||||
taskID, err := s.asynqClient.ScheduleArticlePublish(ctx, cmd.ID, scheduledTime)
|
||||
if err != nil {
|
||||
s.logger.Error("调度定时发布任务失败", zap.String("id", cmd.ID), zap.Error(err))
|
||||
return fmt.Errorf("调度定时发布任务失败: %w", err)
|
||||
// 4. 创建任务工厂
|
||||
taskFactory := task_entities.NewTaskFactoryWithManager(s.taskManager)
|
||||
|
||||
// 5. 创建并异步入队文章发布任务
|
||||
if err := taskFactory.CreateAndEnqueueArticlePublishTask(
|
||||
ctx,
|
||||
cmd.ID,
|
||||
scheduledTime,
|
||||
"system", // 暂时使用系统用户ID
|
||||
); err != nil {
|
||||
s.logger.Error("创建并入队文章发布任务失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. 设置定时发布
|
||||
if err := article.SchedulePublish(scheduledTime, taskID); err != nil {
|
||||
// 6. 设置定时发布
|
||||
if err := article.SchedulePublish(scheduledTime); err != nil {
|
||||
return fmt.Errorf("设置定时发布失败: %w", err)
|
||||
}
|
||||
|
||||
// 6. 保存更新
|
||||
// 7. 保存更新
|
||||
if err := s.articleRepo.Update(ctx, article); err != nil {
|
||||
s.logger.Error("更新文章失败", zap.String("id", article.ID), zap.Error(err))
|
||||
return fmt.Errorf("设置定时发布失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("设置定时发布成功", zap.String("id", article.ID), zap.Time("scheduled_time", scheduledTime), zap.String("task_id", taskID))
|
||||
s.logger.Info("设置定时发布成功", zap.String("id", article.ID), zap.Time("scheduled_time", scheduledTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -381,10 +387,9 @@ func (s *ArticleApplicationServiceImpl) CancelSchedulePublishArticle(ctx context
|
||||
}
|
||||
|
||||
// 3. 取消定时任务
|
||||
if article.TaskID != "" {
|
||||
if err := s.asynqClient.CancelScheduledTask(ctx, article.TaskID); err != nil {
|
||||
s.logger.Warn("取消定时任务失败", zap.String("task_id", article.TaskID), zap.Error(err))
|
||||
}
|
||||
if err := s.taskManager.CancelTask(ctx, cmd.ID); err != nil {
|
||||
s.logger.Warn("取消定时任务失败", zap.String("article_id", cmd.ID), zap.Error(err))
|
||||
// 不返回错误,继续执行取消定时发布
|
||||
}
|
||||
|
||||
// 4. 取消定时发布
|
||||
@@ -613,7 +618,7 @@ func (s *ArticleApplicationServiceImpl) GetCategoryByID(ctx context.Context, que
|
||||
// ListCategories 获取分类列表
|
||||
func (s *ArticleApplicationServiceImpl) ListCategories(ctx context.Context) (*responses.CategoryListResponse, error) {
|
||||
// 1. 获取分类列表
|
||||
categories, err := s.categoryRepo.List(ctx, interfaces.ListOptions{})
|
||||
categories, err := s.categoryRepo.List(ctx, shared_interfaces.ListOptions{})
|
||||
if err != nil {
|
||||
s.logger.Error("获取分类列表失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("获取分类列表失败: %w", err)
|
||||
@@ -730,7 +735,7 @@ func (s *ArticleApplicationServiceImpl) GetTagByID(ctx context.Context, query *a
|
||||
// ListTags 获取标签列表
|
||||
func (s *ArticleApplicationServiceImpl) ListTags(ctx context.Context) (*responses.TagListResponse, error) {
|
||||
// 1. 获取标签列表
|
||||
tags, err := s.tagRepo.List(ctx, interfaces.ListOptions{})
|
||||
tags, err := s.tagRepo.List(ctx, shared_interfaces.ListOptions{})
|
||||
if err != nil {
|
||||
s.logger.Error("获取标签列表失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("获取标签列表失败: %w", err)
|
||||
@@ -776,15 +781,14 @@ func (s *ArticleApplicationServiceImpl) UpdateSchedulePublishArticle(ctx context
|
||||
return fmt.Errorf("文章未设置定时发布,无法修改时间")
|
||||
}
|
||||
|
||||
// 4. 重新调度定时发布任务
|
||||
newTaskID, err := s.asynqClient.RescheduleArticlePublish(ctx, cmd.ID, article.TaskID, scheduledTime)
|
||||
if err != nil {
|
||||
s.logger.Error("重新调度定时发布任务失败", zap.String("id", cmd.ID), zap.Error(err))
|
||||
// 4. 更新数据库中的任务调度时间
|
||||
if err := s.taskManager.UpdateTaskSchedule(ctx, cmd.ID, scheduledTime); err != nil {
|
||||
s.logger.Error("更新任务调度时间失败", zap.String("id", cmd.ID), zap.Error(err))
|
||||
return fmt.Errorf("修改定时发布时间失败: %w", err)
|
||||
}
|
||||
|
||||
// 5. 更新定时发布
|
||||
if err := article.UpdateSchedulePublish(scheduledTime, newTaskID); err != nil {
|
||||
if err := article.UpdateSchedulePublish(scheduledTime); err != nil {
|
||||
return fmt.Errorf("更新定时发布失败: %w", err)
|
||||
}
|
||||
|
||||
@@ -796,8 +800,7 @@ func (s *ArticleApplicationServiceImpl) UpdateSchedulePublishArticle(ctx context
|
||||
|
||||
s.logger.Info("修改定时发布时间成功",
|
||||
zap.String("id", article.ID),
|
||||
zap.Time("new_scheduled_time", scheduledTime),
|
||||
zap.String("new_task_id", newTaskID))
|
||||
zap.Time("new_scheduled_time", scheduledTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ type CertificationApplicationService interface {
|
||||
// 申请合同签署
|
||||
ApplyContract(ctx context.Context, cmd *commands.ApplyContractCommand) (*responses.ContractSignUrlResponse, error)
|
||||
|
||||
// OCR营业执照识别
|
||||
RecognizeBusinessLicense(ctx context.Context, imageBytes []byte) (*responses.BusinessLicenseResult, error)
|
||||
|
||||
// ================ 查询用例 ================
|
||||
|
||||
// 获取认证详情
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"tyapi-server/internal/infrastructure/external/storage"
|
||||
"tyapi-server/internal/shared/database"
|
||||
"tyapi-server/internal/shared/esign"
|
||||
sharedOCR "tyapi-server/internal/shared/ocr"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -40,6 +41,7 @@ type CertificationApplicationServiceImpl struct {
|
||||
walletAggregateService finance_service.WalletAggregateService
|
||||
apiUserAggregateService api_service.ApiUserAggregateService
|
||||
enterpriseInfoSubmitRecordService *services.EnterpriseInfoSubmitRecordService
|
||||
ocrService sharedOCR.OCRService
|
||||
// 仓储依赖
|
||||
queryRepository repositories.CertificationQueryRepository
|
||||
enterpriseInfoSubmitRecordRepo repositories.EnterpriseInfoSubmitRecordRepository
|
||||
@@ -62,6 +64,7 @@ func NewCertificationApplicationService(
|
||||
walletAggregateService finance_service.WalletAggregateService,
|
||||
apiUserAggregateService api_service.ApiUserAggregateService,
|
||||
enterpriseInfoSubmitRecordService *services.EnterpriseInfoSubmitRecordService,
|
||||
ocrService sharedOCR.OCRService,
|
||||
txManager *database.TransactionManager,
|
||||
logger *zap.Logger,
|
||||
) CertificationApplicationService {
|
||||
@@ -78,6 +81,7 @@ func NewCertificationApplicationService(
|
||||
walletAggregateService: walletAggregateService,
|
||||
apiUserAggregateService: apiUserAggregateService,
|
||||
enterpriseInfoSubmitRecordService: enterpriseInfoSubmitRecordService,
|
||||
ocrService: ocrService,
|
||||
txManager: txManager,
|
||||
logger: logger,
|
||||
}
|
||||
@@ -987,3 +991,33 @@ func (s *CertificationApplicationServiceImpl) AddStatusMetadata(ctx context.Cont
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// RecognizeBusinessLicense OCR识别营业执照
|
||||
func (s *CertificationApplicationServiceImpl) RecognizeBusinessLicense(
|
||||
ctx context.Context,
|
||||
imageBytes []byte,
|
||||
) (*responses.BusinessLicenseResult, error) {
|
||||
s.logger.Info("开始OCR识别营业执照", zap.Int("image_size", len(imageBytes)))
|
||||
|
||||
// 调用OCR服务识别营业执照
|
||||
result, err := s.ocrService.RecognizeBusinessLicense(ctx, imageBytes)
|
||||
if err != nil {
|
||||
s.logger.Error("OCR识别营业执照失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("营业执照识别失败: %w", err)
|
||||
}
|
||||
|
||||
// 验证识别结果
|
||||
if err := s.ocrService.ValidateBusinessLicense(result); err != nil {
|
||||
s.logger.Error("营业执照识别结果验证失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("营业执照识别结果不完整: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("营业执照OCR识别成功",
|
||||
zap.String("company_name", result.CompanyName),
|
||||
zap.String("unified_social_code", result.UnifiedSocialCode),
|
||||
zap.String("legal_person_name", result.LegalPersonName),
|
||||
zap.Float64("confidence", result.Confidence),
|
||||
)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package finance
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"tyapi-server/internal/application/finance/dto/commands"
|
||||
"tyapi-server/internal/application/finance/dto/queries"
|
||||
"tyapi-server/internal/application/finance/dto/responses"
|
||||
@@ -12,29 +11,34 @@ import (
|
||||
|
||||
// FinanceApplicationService 财务应用服务接口
|
||||
type FinanceApplicationService interface {
|
||||
|
||||
// 钱包管理
|
||||
CreateWallet(ctx context.Context, cmd *commands.CreateWalletCommand) (*responses.WalletResponse, error)
|
||||
GetWallet(ctx context.Context, query *queries.GetWalletInfoQuery) (*responses.WalletResponse, error)
|
||||
|
||||
|
||||
// 充值管理
|
||||
CreateAlipayRechargeOrder(ctx context.Context, cmd *commands.CreateAlipayRechargeCommand) (*responses.AlipayRechargeOrderResponse, error)
|
||||
TransferRecharge(ctx context.Context, cmd *commands.TransferRechargeCommand) (*responses.RechargeRecordResponse, error)
|
||||
GiftRecharge(ctx context.Context, cmd *commands.GiftRechargeCommand) (*responses.RechargeRecordResponse, error)
|
||||
|
||||
// 交易记录
|
||||
GetUserWalletTransactions(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error)
|
||||
GetAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error)
|
||||
|
||||
// 导出功能
|
||||
ExportAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error)
|
||||
ExportAdminRechargeRecords(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error)
|
||||
|
||||
// 支付宝回调处理
|
||||
HandleAlipayCallback(ctx context.Context, r *http.Request) error
|
||||
HandleAlipayReturn(ctx context.Context, outTradeNo string) (string, error)
|
||||
GetAlipayOrderStatus(ctx context.Context, outTradeNo string) (*responses.AlipayOrderStatusResponse, error)
|
||||
|
||||
TransferRecharge(ctx context.Context, cmd *commands.TransferRechargeCommand) (*responses.RechargeRecordResponse, error)
|
||||
GiftRecharge(ctx context.Context, cmd *commands.GiftRechargeCommand) (*responses.RechargeRecordResponse, error)
|
||||
|
||||
// 获取用户钱包交易记录
|
||||
GetUserWalletTransactions(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error)
|
||||
|
||||
// 管理端消费记录
|
||||
GetAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.WalletTransactionListResponse, error)
|
||||
|
||||
// 获取用户充值记录
|
||||
// 充值记录
|
||||
GetUserRechargeRecords(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*responses.RechargeRecordListResponse, error)
|
||||
|
||||
// 管理员获取充值记录
|
||||
GetAdminRechargeRecords(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.RechargeRecordListResponse, error)
|
||||
|
||||
|
||||
// 获取充值配置
|
||||
GetRechargeConfig(ctx context.Context) (*responses.RechargeConfigResponse, error)
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
finance_services "tyapi-server/internal/domains/finance/services"
|
||||
user_repositories "tyapi-server/internal/domains/user/repositories"
|
||||
"tyapi-server/internal/shared/database"
|
||||
"tyapi-server/internal/shared/export"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
"tyapi-server/internal/shared/payment"
|
||||
|
||||
@@ -30,6 +31,7 @@ type FinanceApplicationServiceImpl struct {
|
||||
alipayOrderRepo finance_repositories.AlipayOrderRepository
|
||||
userRepo user_repositories.UserRepository
|
||||
txManager *database.TransactionManager
|
||||
exportManager *export.ExportManager
|
||||
logger *zap.Logger
|
||||
config *config.Config
|
||||
}
|
||||
@@ -45,6 +47,7 @@ func NewFinanceApplicationService(
|
||||
txManager *database.TransactionManager,
|
||||
logger *zap.Logger,
|
||||
config *config.Config,
|
||||
exportManager *export.ExportManager,
|
||||
) FinanceApplicationService {
|
||||
return &FinanceApplicationServiceImpl{
|
||||
aliPayClient: aliPayClient,
|
||||
@@ -54,6 +57,7 @@ func NewFinanceApplicationService(
|
||||
alipayOrderRepo: alipayOrderRepo,
|
||||
userRepo: userRepo,
|
||||
txManager: txManager,
|
||||
exportManager: exportManager,
|
||||
logger: logger,
|
||||
config: config,
|
||||
}
|
||||
@@ -344,6 +348,290 @@ func (s *FinanceApplicationServiceImpl) GetAdminWalletTransactions(ctx context.C
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExportAdminWalletTransactions 导出管理端钱包交易记录
|
||||
func (s *FinanceApplicationServiceImpl) ExportAdminWalletTransactions(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error) {
|
||||
const batchSize = 1000 // 每批处理1000条记录
|
||||
var allTransactions []*finance_entities.WalletTransaction
|
||||
var productNameMap map[string]string
|
||||
|
||||
// 分批获取数据
|
||||
page := 1
|
||||
for {
|
||||
// 查询当前批次的数据
|
||||
batchProductNameMap, transactions, _, err := s.walletTransactionRepository.ListWithFiltersAndProductName(ctx, filters, interfaces.ListOptions{
|
||||
Page: page,
|
||||
PageSize: batchSize,
|
||||
Sort: "created_at",
|
||||
Order: "desc",
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("查询导出钱包交易记录失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 合并产品名称映射
|
||||
if productNameMap == nil {
|
||||
productNameMap = batchProductNameMap
|
||||
} else {
|
||||
for k, v := range batchProductNameMap {
|
||||
productNameMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到总数据中
|
||||
allTransactions = append(allTransactions, transactions...)
|
||||
|
||||
// 如果当前批次数据少于批次大小,说明已经是最后一批
|
||||
if len(transactions) < batchSize {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
// 检查是否有数据
|
||||
if len(allTransactions) == 0 {
|
||||
return nil, fmt.Errorf("没有找到符合条件的数据")
|
||||
}
|
||||
|
||||
// 批量获取企业名称映射,避免N+1查询问题
|
||||
companyNameMap, err := s.batchGetCompanyNames(ctx, allTransactions)
|
||||
if err != nil {
|
||||
companyNameMap = make(map[string]string)
|
||||
}
|
||||
|
||||
// 准备导出数据
|
||||
headers := []string{"交易ID", "企业名称", "产品名称", "消费金额", "消费时间"}
|
||||
columnWidths := []float64{20, 25, 20, 15, 20}
|
||||
|
||||
data := make([][]interface{}, len(allTransactions))
|
||||
for i, transaction := range allTransactions {
|
||||
companyName := companyNameMap[transaction.UserID]
|
||||
if companyName == "" {
|
||||
companyName = "未知企业"
|
||||
}
|
||||
|
||||
productName := productNameMap[transaction.ProductID]
|
||||
if productName == "" {
|
||||
productName = "未知产品"
|
||||
}
|
||||
|
||||
data[i] = []interface{}{
|
||||
transaction.TransactionID,
|
||||
companyName,
|
||||
productName,
|
||||
transaction.Amount.String(),
|
||||
transaction.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
}
|
||||
|
||||
// 创建导出配置
|
||||
config := &export.ExportConfig{
|
||||
SheetName: "消费记录",
|
||||
Headers: headers,
|
||||
Data: data,
|
||||
ColumnWidths: columnWidths,
|
||||
}
|
||||
|
||||
// 使用导出管理器生成文件
|
||||
return s.exportManager.Export(ctx, config, format)
|
||||
}
|
||||
|
||||
// batchGetCompanyNames 批量获取企业名称映射
|
||||
func (s *FinanceApplicationServiceImpl) batchGetCompanyNames(ctx context.Context, transactions []*finance_entities.WalletTransaction) (map[string]string, error) {
|
||||
// 收集所有唯一的用户ID
|
||||
userIDSet := make(map[string]bool)
|
||||
for _, transaction := range transactions {
|
||||
userIDSet[transaction.UserID] = true
|
||||
}
|
||||
|
||||
// 转换为切片
|
||||
userIDs := make([]string, 0, len(userIDSet))
|
||||
for userID := range userIDSet {
|
||||
userIDs = append(userIDs, userID)
|
||||
}
|
||||
|
||||
// 批量查询用户信息
|
||||
users, err := s.userRepo.BatchGetByIDsWithEnterpriseInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建企业名称映射
|
||||
companyNameMap := make(map[string]string)
|
||||
for _, user := range users {
|
||||
companyName := "未知企业"
|
||||
if user.EnterpriseInfo != nil {
|
||||
companyName = user.EnterpriseInfo.CompanyName
|
||||
}
|
||||
companyNameMap[user.ID] = companyName
|
||||
}
|
||||
|
||||
return companyNameMap, nil
|
||||
}
|
||||
|
||||
// ExportAdminRechargeRecords 导出管理端充值记录
|
||||
func (s *FinanceApplicationServiceImpl) ExportAdminRechargeRecords(ctx context.Context, filters map[string]interface{}, format string) ([]byte, error) {
|
||||
const batchSize = 1000 // 每批处理1000条记录
|
||||
var allRecords []finance_entities.RechargeRecord
|
||||
|
||||
// 分批获取数据
|
||||
page := 1
|
||||
for {
|
||||
// 查询当前批次的数据
|
||||
records, err := s.rechargeRecordService.GetAll(ctx, filters, interfaces.ListOptions{
|
||||
Page: page,
|
||||
PageSize: batchSize,
|
||||
Sort: "created_at",
|
||||
Order: "desc",
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("查询导出充值记录失败", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 添加到总数据中
|
||||
allRecords = append(allRecords, records...)
|
||||
|
||||
// 如果当前批次数据少于批次大小,说明已经是最后一批
|
||||
if len(records) < batchSize {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
|
||||
// 批量获取企业名称映射,避免N+1查询问题
|
||||
companyNameMap, err := s.batchGetCompanyNamesForRechargeRecords(ctx, convertToRechargeRecordPointers(allRecords))
|
||||
if err != nil {
|
||||
s.logger.Warn("批量获取企业名称失败,使用默认值", zap.Error(err))
|
||||
companyNameMap = make(map[string]string)
|
||||
}
|
||||
|
||||
// 准备导出数据
|
||||
headers := []string{"企业名称", "充值金额", "充值类型", "状态", "支付宝订单号", "转账订单号", "备注", "充值时间"}
|
||||
columnWidths := []float64{25, 15, 15, 10, 20, 20, 20, 20}
|
||||
|
||||
data := make([][]interface{}, len(allRecords))
|
||||
for i, record := range allRecords {
|
||||
// 从映射中获取企业名称
|
||||
companyName := companyNameMap[record.UserID]
|
||||
if companyName == "" {
|
||||
companyName = "未知企业"
|
||||
}
|
||||
|
||||
// 获取订单号
|
||||
alipayOrderID := ""
|
||||
if record.AlipayOrderID != nil && *record.AlipayOrderID != "" {
|
||||
alipayOrderID = *record.AlipayOrderID
|
||||
}
|
||||
transferOrderID := ""
|
||||
if record.TransferOrderID != nil && *record.TransferOrderID != "" {
|
||||
transferOrderID = *record.TransferOrderID
|
||||
}
|
||||
|
||||
// 获取备注
|
||||
notes := ""
|
||||
if record.Notes != "" {
|
||||
notes = record.Notes
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
createdAt := record.CreatedAt.Format("2006-01-02 15:04:05")
|
||||
|
||||
data[i] = []interface{}{
|
||||
companyName,
|
||||
record.Amount.String(),
|
||||
translateRechargeType(record.RechargeType),
|
||||
translateRechargeStatus(record.Status),
|
||||
alipayOrderID,
|
||||
transferOrderID,
|
||||
notes,
|
||||
createdAt,
|
||||
}
|
||||
}
|
||||
|
||||
// 创建导出配置
|
||||
config := &export.ExportConfig{
|
||||
SheetName: "充值记录",
|
||||
Headers: headers,
|
||||
Data: data,
|
||||
ColumnWidths: columnWidths,
|
||||
}
|
||||
|
||||
// 使用导出管理器生成文件
|
||||
return s.exportManager.Export(ctx, config, format)
|
||||
}
|
||||
|
||||
// translateRechargeType 翻译充值类型为中文
|
||||
func translateRechargeType(rechargeType finance_entities.RechargeType) string {
|
||||
switch rechargeType {
|
||||
case finance_entities.RechargeTypeAlipay:
|
||||
return "支付宝充值"
|
||||
case finance_entities.RechargeTypeTransfer:
|
||||
return "对公转账"
|
||||
case finance_entities.RechargeTypeGift:
|
||||
return "赠送"
|
||||
default:
|
||||
return "未知类型"
|
||||
}
|
||||
}
|
||||
|
||||
// translateRechargeStatus 翻译充值状态为中文
|
||||
func translateRechargeStatus(status finance_entities.RechargeStatus) string {
|
||||
switch status {
|
||||
case finance_entities.RechargeStatusPending:
|
||||
return "待处理"
|
||||
case finance_entities.RechargeStatusSuccess:
|
||||
return "成功"
|
||||
case finance_entities.RechargeStatusFailed:
|
||||
return "失败"
|
||||
case finance_entities.RechargeStatusCancelled:
|
||||
return "已取消"
|
||||
default:
|
||||
return "未知状态"
|
||||
}
|
||||
}
|
||||
|
||||
// convertToRechargeRecordPointers 将RechargeRecord切片转换为指针切片
|
||||
func convertToRechargeRecordPointers(records []finance_entities.RechargeRecord) []*finance_entities.RechargeRecord {
|
||||
pointers := make([]*finance_entities.RechargeRecord, len(records))
|
||||
for i := range records {
|
||||
pointers[i] = &records[i]
|
||||
}
|
||||
return pointers
|
||||
}
|
||||
|
||||
// batchGetCompanyNamesForRechargeRecords 批量获取企业名称映射(用于充值记录)
|
||||
func (s *FinanceApplicationServiceImpl) batchGetCompanyNamesForRechargeRecords(ctx context.Context, records []*finance_entities.RechargeRecord) (map[string]string, error) {
|
||||
// 收集所有唯一的用户ID
|
||||
userIDSet := make(map[string]bool)
|
||||
for _, record := range records {
|
||||
userIDSet[record.UserID] = true
|
||||
}
|
||||
|
||||
// 转换为切片
|
||||
userIDs := make([]string, 0, len(userIDSet))
|
||||
for userID := range userIDSet {
|
||||
userIDs = append(userIDs, userID)
|
||||
}
|
||||
|
||||
// 批量查询用户信息
|
||||
users, err := s.userRepo.BatchGetByIDsWithEnterpriseInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建企业名称映射
|
||||
companyNameMap := make(map[string]string)
|
||||
for _, user := range users {
|
||||
companyName := "未知企业"
|
||||
if user.EnterpriseInfo != nil {
|
||||
companyName = user.EnterpriseInfo.CompanyName
|
||||
}
|
||||
companyNameMap[user.ID] = companyName
|
||||
}
|
||||
|
||||
return companyNameMap, nil
|
||||
}
|
||||
|
||||
// HandleAlipayCallback 处理支付宝回调
|
||||
func (s *FinanceApplicationServiceImpl) HandleAlipayCallback(ctx context.Context, r *http.Request) error {
|
||||
@@ -402,7 +690,7 @@ func (s *FinanceApplicationServiceImpl) processAlipayPaymentSuccess(ctx context.
|
||||
// 该服务内部会处理所有必要的检查、事务和更新操作
|
||||
err = s.rechargeRecordService.HandleAlipayPaymentSuccess(ctx, outTradeNo, amount, tradeNo)
|
||||
if err != nil {
|
||||
s.logger.Error("处理支付宝支付成功失败",
|
||||
s.logger.Error("处理支付宝支付成功失败",
|
||||
zap.String("out_trade_no", outTradeNo),
|
||||
zap.Error(err),
|
||||
)
|
||||
@@ -665,14 +953,14 @@ func (s *FinanceApplicationServiceImpl) GetAdminRechargeRecords(ctx context.Cont
|
||||
var items []responses.RechargeRecordResponse
|
||||
for _, record := range records {
|
||||
item := responses.RechargeRecordResponse{
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
Amount: record.Amount,
|
||||
RechargeType: string(record.RechargeType),
|
||||
Status: string(record.Status),
|
||||
Notes: record.Notes,
|
||||
CreatedAt: record.CreatedAt,
|
||||
UpdatedAt: record.UpdatedAt,
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
Amount: record.Amount,
|
||||
RechargeType: string(record.RechargeType),
|
||||
Status: string(record.Status),
|
||||
Notes: record.Notes,
|
||||
CreatedAt: record.CreatedAt,
|
||||
UpdatedAt: record.UpdatedAt,
|
||||
}
|
||||
|
||||
// 根据充值类型设置相应的订单号
|
||||
@@ -719,8 +1007,8 @@ func (s *FinanceApplicationServiceImpl) GetRechargeConfig(ctx context.Context) (
|
||||
})
|
||||
}
|
||||
return &responses.RechargeConfigResponse{
|
||||
MinAmount: s.config.Wallet.MinAmount,
|
||||
MaxAmount: s.config.Wallet.MaxAmount,
|
||||
MinAmount: s.config.Wallet.MinAmount,
|
||||
MaxAmount: s.config.Wallet.MaxAmount,
|
||||
AlipayRechargeBonus: bonus,
|
||||
}, nil
|
||||
}
|
||||
|
||||
19
internal/application/product/category_application_service.go
Normal file
19
internal/application/product/category_application_service.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/application/product/dto/commands"
|
||||
"tyapi-server/internal/application/product/dto/queries"
|
||||
"tyapi-server/internal/application/product/dto/responses"
|
||||
)
|
||||
|
||||
// CategoryApplicationService 分类应用服务接口
|
||||
type CategoryApplicationService interface {
|
||||
// 分类管理
|
||||
CreateCategory(ctx context.Context, cmd *commands.CreateCategoryCommand) error
|
||||
UpdateCategory(ctx context.Context, cmd *commands.UpdateCategoryCommand) error
|
||||
DeleteCategory(ctx context.Context, cmd *commands.DeleteCategoryCommand) error
|
||||
|
||||
GetCategoryByID(ctx context.Context, query *queries.GetCategoryQuery) (*responses.CategoryInfoResponse, error)
|
||||
ListCategories(ctx context.Context, query *queries.ListCategoriesQuery) (*responses.CategoryListResponse, error)
|
||||
}
|
||||
@@ -46,41 +46,6 @@ type ProductApplicationService interface {
|
||||
CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error
|
||||
UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error
|
||||
DeleteProductApiConfig(ctx context.Context, configID string) error
|
||||
}
|
||||
|
||||
// CategoryApplicationService 分类应用服务接口
|
||||
type CategoryApplicationService interface {
|
||||
// 分类管理
|
||||
CreateCategory(ctx context.Context, cmd *commands.CreateCategoryCommand) error
|
||||
UpdateCategory(ctx context.Context, cmd *commands.UpdateCategoryCommand) error
|
||||
DeleteCategory(ctx context.Context, cmd *commands.DeleteCategoryCommand) error
|
||||
|
||||
GetCategoryByID(ctx context.Context, query *queries.GetCategoryQuery) (*responses.CategoryInfoResponse, error)
|
||||
ListCategories(ctx context.Context, query *queries.ListCategoriesQuery) (*responses.CategoryListResponse, error)
|
||||
}
|
||||
|
||||
// SubscriptionApplicationService 订阅应用服务接口
|
||||
type SubscriptionApplicationService interface {
|
||||
// 订阅管理
|
||||
UpdateSubscriptionPrice(ctx context.Context, cmd *commands.UpdateSubscriptionPriceCommand) error
|
||||
|
||||
// 订阅管理
|
||||
CreateSubscription(ctx context.Context, cmd *commands.CreateSubscriptionCommand) error
|
||||
GetSubscriptionByID(ctx context.Context, query *queries.GetSubscriptionQuery) (*responses.SubscriptionInfoResponse, error)
|
||||
ListSubscriptions(ctx context.Context, query *queries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error)
|
||||
|
||||
// 我的订阅(用户专用)
|
||||
ListMySubscriptions(ctx context.Context, userID string, query *queries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error)
|
||||
GetMySubscriptionStats(ctx context.Context, userID string) (*responses.SubscriptionStatsResponse, error)
|
||||
|
||||
// 业务查询
|
||||
GetUserSubscriptions(ctx context.Context, query *queries.GetUserSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error)
|
||||
GetProductSubscriptions(ctx context.Context, query *queries.GetProductSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error)
|
||||
GetSubscriptionUsage(ctx context.Context, subscriptionID string) (*responses.SubscriptionUsageResponse, error)
|
||||
|
||||
// 统计
|
||||
GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error)
|
||||
|
||||
// 一键改价
|
||||
BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -584,3 +584,5 @@ func (s *ProductApplicationServiceImpl) UpdateProductApiConfig(ctx context.Conte
|
||||
func (s *ProductApplicationServiceImpl) DeleteProductApiConfig(ctx context.Context, configID string) error {
|
||||
return s.productApiConfigAppService.DeleteProductApiConfig(ctx, configID)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/application/product/dto/commands"
|
||||
"tyapi-server/internal/application/product/dto/queries"
|
||||
"tyapi-server/internal/application/product/dto/responses"
|
||||
)
|
||||
|
||||
// SubscriptionApplicationService 订阅应用服务接口
|
||||
type SubscriptionApplicationService interface {
|
||||
// 订阅管理
|
||||
UpdateSubscriptionPrice(ctx context.Context, cmd *commands.UpdateSubscriptionPriceCommand) error
|
||||
|
||||
// 订阅管理
|
||||
CreateSubscription(ctx context.Context, cmd *commands.CreateSubscriptionCommand) error
|
||||
GetSubscriptionByID(ctx context.Context, query *queries.GetSubscriptionQuery) (*responses.SubscriptionInfoResponse, error)
|
||||
ListSubscriptions(ctx context.Context, query *queries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error)
|
||||
|
||||
// 我的订阅(用户专用)
|
||||
ListMySubscriptions(ctx context.Context, userID string, query *queries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error)
|
||||
GetMySubscriptionStats(ctx context.Context, userID string) (*responses.SubscriptionStatsResponse, error)
|
||||
|
||||
// 业务查询
|
||||
GetUserSubscriptions(ctx context.Context, query *queries.GetUserSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error)
|
||||
GetProductSubscriptions(ctx context.Context, query *queries.GetProductSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error)
|
||||
GetSubscriptionUsage(ctx context.Context, subscriptionID string) (*responses.SubscriptionUsageResponse, error)
|
||||
|
||||
// 统计
|
||||
GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error)
|
||||
|
||||
// 一键改价
|
||||
BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error
|
||||
}
|
||||
412
internal/application/statistics/commands_queries.go
Normal file
412
internal/application/statistics/commands_queries.go
Normal file
@@ -0,0 +1,412 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ================ 命令对象 ================
|
||||
|
||||
// CreateMetricCommand 创建指标命令
|
||||
type CreateMetricCommand struct {
|
||||
MetricType string `json:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" validate:"required" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" comment:"统计维度"`
|
||||
Value float64 `json:"value" validate:"min=0" comment:"指标值"`
|
||||
Metadata string `json:"metadata" comment:"额外维度信息"`
|
||||
Date time.Time `json:"date" validate:"required" comment:"统计日期"`
|
||||
}
|
||||
|
||||
// UpdateMetricCommand 更新指标命令
|
||||
type UpdateMetricCommand struct {
|
||||
ID string `json:"id" validate:"required" comment:"指标ID"`
|
||||
Value float64 `json:"value" validate:"min=0" comment:"新指标值"`
|
||||
}
|
||||
|
||||
// DeleteMetricCommand 删除指标命令
|
||||
type DeleteMetricCommand struct {
|
||||
ID string `json:"id" validate:"required" comment:"指标ID"`
|
||||
}
|
||||
|
||||
// GenerateReportCommand 生成报告命令
|
||||
type GenerateReportCommand struct {
|
||||
ReportType string `json:"report_type" validate:"required" comment:"报告类型"`
|
||||
Title string `json:"title" validate:"required" comment:"报告标题"`
|
||||
Period string `json:"period" validate:"required" comment:"统计周期"`
|
||||
UserRole string `json:"user_role" validate:"required" comment:"用户角色"`
|
||||
StartDate time.Time `json:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" comment:"结束日期"`
|
||||
Filters map[string]interface{} `json:"filters" comment:"过滤条件"`
|
||||
GeneratedBy string `json:"generated_by" validate:"required" comment:"生成者ID"`
|
||||
}
|
||||
|
||||
// CreateDashboardCommand 创建仪表板命令
|
||||
type CreateDashboardCommand struct {
|
||||
Name string `json:"name" validate:"required" comment:"仪表板名称"`
|
||||
Description string `json:"description" comment:"仪表板描述"`
|
||||
UserRole string `json:"user_role" validate:"required" comment:"用户角色"`
|
||||
Layout string `json:"layout" comment:"布局配置"`
|
||||
Widgets string `json:"widgets" comment:"组件配置"`
|
||||
Settings string `json:"settings" comment:"设置配置"`
|
||||
RefreshInterval int `json:"refresh_interval" validate:"min=30" comment:"刷新间隔(秒)"`
|
||||
AccessLevel string `json:"access_level" comment:"访问级别"`
|
||||
CreatedBy string `json:"created_by" validate:"required" comment:"创建者ID"`
|
||||
}
|
||||
|
||||
// UpdateDashboardCommand 更新仪表板命令
|
||||
type UpdateDashboardCommand struct {
|
||||
ID string `json:"id" validate:"required" comment:"仪表板ID"`
|
||||
Name string `json:"name" comment:"仪表板名称"`
|
||||
Description string `json:"description" comment:"仪表板描述"`
|
||||
Layout string `json:"layout" comment:"布局配置"`
|
||||
Widgets string `json:"widgets" comment:"组件配置"`
|
||||
Settings string `json:"settings" comment:"设置配置"`
|
||||
RefreshInterval int `json:"refresh_interval" validate:"min=30" comment:"刷新间隔(秒)"`
|
||||
AccessLevel string `json:"access_level" comment:"访问级别"`
|
||||
UpdatedBy string `json:"updated_by" validate:"required" comment:"更新者ID"`
|
||||
}
|
||||
|
||||
// SetDefaultDashboardCommand 设置默认仪表板命令
|
||||
type SetDefaultDashboardCommand struct {
|
||||
DashboardID string `json:"dashboard_id" validate:"required" comment:"仪表板ID"`
|
||||
UserRole string `json:"user_role" validate:"required" comment:"用户角色"`
|
||||
UpdatedBy string `json:"updated_by" validate:"required" comment:"更新者ID"`
|
||||
}
|
||||
|
||||
// ActivateDashboardCommand 激活仪表板命令
|
||||
type ActivateDashboardCommand struct {
|
||||
DashboardID string `json:"dashboard_id" validate:"required" comment:"仪表板ID"`
|
||||
ActivatedBy string `json:"activated_by" validate:"required" comment:"激活者ID"`
|
||||
}
|
||||
|
||||
// DeactivateDashboardCommand 停用仪表板命令
|
||||
type DeactivateDashboardCommand struct {
|
||||
DashboardID string `json:"dashboard_id" validate:"required" comment:"仪表板ID"`
|
||||
DeactivatedBy string `json:"deactivated_by" validate:"required" comment:"停用者ID"`
|
||||
}
|
||||
|
||||
// DeleteDashboardCommand 删除仪表板命令
|
||||
type DeleteDashboardCommand struct {
|
||||
DashboardID string `json:"dashboard_id" validate:"required" comment:"仪表板ID"`
|
||||
DeletedBy string `json:"deleted_by" validate:"required" comment:"删除者ID"`
|
||||
}
|
||||
|
||||
// ExportDataCommand 导出数据命令
|
||||
type ExportDataCommand struct {
|
||||
Format string `json:"format" validate:"required" comment:"导出格式"`
|
||||
MetricType string `json:"metric_type" validate:"required" comment:"指标类型"`
|
||||
StartDate time.Time `json:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" validate:"required" comment:"结束日期"`
|
||||
Dimension string `json:"dimension" comment:"统计维度"`
|
||||
GroupBy string `json:"group_by" comment:"分组维度"`
|
||||
Filters map[string]interface{} `json:"filters" comment:"过滤条件"`
|
||||
Columns []string `json:"columns" comment:"导出列"`
|
||||
IncludeCharts bool `json:"include_charts" comment:"是否包含图表"`
|
||||
ExportedBy string `json:"exported_by" validate:"required" comment:"导出者ID"`
|
||||
}
|
||||
|
||||
// TriggerAggregationCommand 触发数据聚合命令
|
||||
type TriggerAggregationCommand struct {
|
||||
MetricType string `json:"metric_type" validate:"required" comment:"指标类型"`
|
||||
Period string `json:"period" validate:"required" comment:"聚合周期"`
|
||||
StartDate time.Time `json:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" comment:"结束日期"`
|
||||
Force bool `json:"force" comment:"是否强制重新聚合"`
|
||||
TriggeredBy string `json:"triggered_by" validate:"required" comment:"触发者ID"`
|
||||
}
|
||||
|
||||
// Validate 验证触发聚合命令
|
||||
func (c *TriggerAggregationCommand) Validate() error {
|
||||
if c.MetricType == "" {
|
||||
return fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
if c.Period == "" {
|
||||
return fmt.Errorf("聚合周期不能为空")
|
||||
}
|
||||
if c.TriggeredBy == "" {
|
||||
return fmt.Errorf("触发者ID不能为空")
|
||||
}
|
||||
// 验证周期类型
|
||||
validPeriods := []string{"hourly", "daily", "weekly", "monthly"}
|
||||
isValidPeriod := false
|
||||
for _, period := range validPeriods {
|
||||
if c.Period == period {
|
||||
isValidPeriod = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isValidPeriod {
|
||||
return fmt.Errorf("不支持的聚合周期: %s", c.Period)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 查询对象 ================
|
||||
|
||||
// GetMetricsQuery 获取指标查询
|
||||
type GetMetricsQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" form:"dimension" comment:"统计维度"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" comment:"结束日期"`
|
||||
Limit int `json:"limit" form:"limit" comment:"限制数量"`
|
||||
Offset int `json:"offset" form:"offset" comment:"偏移量"`
|
||||
SortBy string `json:"sort_by" form:"sort_by" comment:"排序字段"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order" comment:"排序顺序"`
|
||||
}
|
||||
|
||||
// GetRealtimeMetricsQuery 获取实时指标查询
|
||||
type GetRealtimeMetricsQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
TimeRange string `json:"time_range" form:"time_range" comment:"时间范围"`
|
||||
Dimension string `json:"dimension" form:"dimension" comment:"统计维度"`
|
||||
}
|
||||
|
||||
// GetHistoricalMetricsQuery 获取历史指标查询
|
||||
type GetHistoricalMetricsQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" form:"dimension" comment:"统计维度"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" validate:"required" comment:"结束日期"`
|
||||
Period string `json:"period" form:"period" comment:"统计周期"`
|
||||
Limit int `json:"limit" form:"limit" comment:"限制数量"`
|
||||
Offset int `json:"offset" form:"offset" comment:"偏移量"`
|
||||
AggregateBy string `json:"aggregate_by" form:"aggregate_by" comment:"聚合维度"`
|
||||
GroupBy string `json:"group_by" form:"group_by" comment:"分组维度"`
|
||||
}
|
||||
|
||||
// GetDashboardDataQuery 获取仪表板数据查询
|
||||
type GetDashboardDataQuery struct {
|
||||
UserRole string `json:"user_role" form:"user_role" validate:"required" comment:"用户角色"`
|
||||
Period string `json:"period" form:"period" comment:"统计周期"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" comment:"结束日期"`
|
||||
MetricTypes []string `json:"metric_types" form:"metric_types" comment:"指标类型列表"`
|
||||
Dimensions []string `json:"dimensions" form:"dimensions" comment:"统计维度列表"`
|
||||
}
|
||||
|
||||
// GetReportsQuery 获取报告查询
|
||||
type GetReportsQuery struct {
|
||||
ReportType string `json:"report_type" form:"report_type" comment:"报告类型"`
|
||||
UserRole string `json:"user_role" form:"user_role" comment:"用户角色"`
|
||||
Status string `json:"status" form:"status" comment:"报告状态"`
|
||||
Period string `json:"period" form:"period" comment:"统计周期"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" comment:"结束日期"`
|
||||
Limit int `json:"limit" form:"limit" comment:"限制数量"`
|
||||
Offset int `json:"offset" form:"offset" comment:"偏移量"`
|
||||
SortBy string `json:"sort_by" form:"sort_by" comment:"排序字段"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order" comment:"排序顺序"`
|
||||
GeneratedBy string `json:"generated_by" form:"generated_by" comment:"生成者ID"`
|
||||
}
|
||||
|
||||
// GetDashboardsQuery 获取仪表板查询
|
||||
type GetDashboardsQuery struct {
|
||||
UserRole string `json:"user_role" form:"user_role" comment:"用户角色"`
|
||||
IsDefault *bool `json:"is_default" form:"is_default" comment:"是否默认"`
|
||||
IsActive *bool `json:"is_active" form:"is_active" comment:"是否激活"`
|
||||
AccessLevel string `json:"access_level" form:"access_level" comment:"访问级别"`
|
||||
CreatedBy string `json:"created_by" form:"created_by" comment:"创建者ID"`
|
||||
Name string `json:"name" form:"name" comment:"仪表板名称"`
|
||||
Limit int `json:"limit" form:"limit" comment:"限制数量"`
|
||||
Offset int `json:"offset" form:"offset" comment:"偏移量"`
|
||||
SortBy string `json:"sort_by" form:"sort_by" comment:"排序字段"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order" comment:"排序顺序"`
|
||||
}
|
||||
|
||||
// GetReportQuery 获取单个报告查询
|
||||
type GetReportQuery struct {
|
||||
ReportID string `json:"report_id" form:"report_id" validate:"required" comment:"报告ID"`
|
||||
}
|
||||
|
||||
// GetDashboardQuery 获取单个仪表板查询
|
||||
type GetDashboardQuery struct {
|
||||
DashboardID string `json:"dashboard_id" form:"dashboard_id" validate:"required" comment:"仪表板ID"`
|
||||
}
|
||||
|
||||
// GetMetricQuery 获取单个指标查询
|
||||
type GetMetricQuery struct {
|
||||
MetricID string `json:"metric_id" form:"metric_id" validate:"required" comment:"指标ID"`
|
||||
}
|
||||
|
||||
// CalculateGrowthRateQuery 计算增长率查询
|
||||
type CalculateGrowthRateQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" validate:"required" comment:"指标名称"`
|
||||
CurrentPeriod time.Time `json:"current_period" form:"current_period" validate:"required" comment:"当前周期"`
|
||||
PreviousPeriod time.Time `json:"previous_period" form:"previous_period" validate:"required" comment:"上一周期"`
|
||||
}
|
||||
|
||||
// CalculateTrendQuery 计算趋势查询
|
||||
type CalculateTrendQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" validate:"required" comment:"指标名称"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" validate:"required" comment:"结束日期"`
|
||||
}
|
||||
|
||||
// CalculateCorrelationQuery 计算相关性查询
|
||||
type CalculateCorrelationQuery struct {
|
||||
MetricType1 string `json:"metric_type1" form:"metric_type1" validate:"required" comment:"指标类型1"`
|
||||
MetricName1 string `json:"metric_name1" form:"metric_name1" validate:"required" comment:"指标名称1"`
|
||||
MetricType2 string `json:"metric_type2" form:"metric_type2" validate:"required" comment:"指标类型2"`
|
||||
MetricName2 string `json:"metric_name2" form:"metric_name2" validate:"required" comment:"指标名称2"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" validate:"required" comment:"结束日期"`
|
||||
}
|
||||
|
||||
// CalculateMovingAverageQuery 计算移动平均查询
|
||||
type CalculateMovingAverageQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" validate:"required" comment:"指标名称"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" validate:"required" comment:"结束日期"`
|
||||
WindowSize int `json:"window_size" form:"window_size" validate:"min=1" comment:"窗口大小"`
|
||||
}
|
||||
|
||||
// CalculateSeasonalityQuery 计算季节性查询
|
||||
type CalculateSeasonalityQuery struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" validate:"required" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" validate:"required" comment:"指标名称"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" validate:"required" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" validate:"required" comment:"结束日期"`
|
||||
}
|
||||
|
||||
// ================ 响应对象 ================
|
||||
|
||||
// CommandResponse 命令响应
|
||||
type CommandResponse struct {
|
||||
Success bool `json:"success" comment:"是否成功"`
|
||||
Message string `json:"message" comment:"响应消息"`
|
||||
Data interface{} `json:"data" comment:"响应数据"`
|
||||
Error string `json:"error,omitempty" comment:"错误信息"`
|
||||
}
|
||||
|
||||
// QueryResponse 查询响应
|
||||
type QueryResponse struct {
|
||||
Success bool `json:"success" comment:"是否成功"`
|
||||
Message string `json:"message" comment:"响应消息"`
|
||||
Data interface{} `json:"data" comment:"响应数据"`
|
||||
Meta map[string]interface{} `json:"meta" comment:"元数据"`
|
||||
Error string `json:"error,omitempty" comment:"错误信息"`
|
||||
}
|
||||
|
||||
// ListResponse 列表响应
|
||||
type ListResponse struct {
|
||||
Success bool `json:"success" comment:"是否成功"`
|
||||
Message string `json:"message" comment:"响应消息"`
|
||||
Data ListDataDTO `json:"data" comment:"数据列表"`
|
||||
Meta map[string]interface{} `json:"meta" comment:"元数据"`
|
||||
Error string `json:"error,omitempty" comment:"错误信息"`
|
||||
}
|
||||
|
||||
// ListDataDTO 列表数据DTO
|
||||
type ListDataDTO struct {
|
||||
Total int64 `json:"total" comment:"总数"`
|
||||
Page int `json:"page" comment:"页码"`
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Items []interface{} `json:"items" comment:"数据列表"`
|
||||
}
|
||||
|
||||
// ================ 验证方法 ================
|
||||
|
||||
// Validate 验证创建指标命令
|
||||
func (c *CreateMetricCommand) Validate() error {
|
||||
if c.MetricType == "" {
|
||||
return fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
if c.MetricName == "" {
|
||||
return fmt.Errorf("指标名称不能为空")
|
||||
}
|
||||
if c.Value < 0 {
|
||||
return fmt.Errorf("指标值不能为负数")
|
||||
}
|
||||
if c.Date.IsZero() {
|
||||
return fmt.Errorf("统计日期不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 验证更新指标命令
|
||||
func (c *UpdateMetricCommand) Validate() error {
|
||||
if c.ID == "" {
|
||||
return fmt.Errorf("指标ID不能为空")
|
||||
}
|
||||
if c.Value < 0 {
|
||||
return fmt.Errorf("指标值不能为负数")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 验证生成报告命令
|
||||
func (c *GenerateReportCommand) Validate() error {
|
||||
if c.ReportType == "" {
|
||||
return fmt.Errorf("报告类型不能为空")
|
||||
}
|
||||
if c.Title == "" {
|
||||
return fmt.Errorf("报告标题不能为空")
|
||||
}
|
||||
if c.Period == "" {
|
||||
return fmt.Errorf("统计周期不能为空")
|
||||
}
|
||||
if c.UserRole == "" {
|
||||
return fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
if c.GeneratedBy == "" {
|
||||
return fmt.Errorf("生成者ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 验证创建仪表板命令
|
||||
func (c *CreateDashboardCommand) Validate() error {
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("仪表板名称不能为空")
|
||||
}
|
||||
if c.UserRole == "" {
|
||||
return fmt.Errorf("用户角色不能为空")
|
||||
}
|
||||
if c.CreatedBy == "" {
|
||||
return fmt.Errorf("创建者ID不能为空")
|
||||
}
|
||||
if c.RefreshInterval < 30 {
|
||||
return fmt.Errorf("刷新间隔不能少于30秒")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 验证更新仪表板命令
|
||||
func (c *UpdateDashboardCommand) Validate() error {
|
||||
if c.ID == "" {
|
||||
return fmt.Errorf("仪表板ID不能为空")
|
||||
}
|
||||
if c.UpdatedBy == "" {
|
||||
return fmt.Errorf("更新者ID不能为空")
|
||||
}
|
||||
if c.RefreshInterval < 30 {
|
||||
return fmt.Errorf("刷新间隔不能少于30秒")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 验证导出数据命令
|
||||
func (c *ExportDataCommand) Validate() error {
|
||||
if c.Format == "" {
|
||||
return fmt.Errorf("导出格式不能为空")
|
||||
}
|
||||
if c.MetricType == "" {
|
||||
return fmt.Errorf("指标类型不能为空")
|
||||
}
|
||||
if c.StartDate.IsZero() || c.EndDate.IsZero() {
|
||||
return fmt.Errorf("开始日期和结束日期不能为空")
|
||||
}
|
||||
if c.StartDate.After(c.EndDate) {
|
||||
return fmt.Errorf("开始日期不能晚于结束日期")
|
||||
}
|
||||
if c.ExportedBy == "" {
|
||||
return fmt.Errorf("导出者ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
258
internal/application/statistics/dtos.go
Normal file
258
internal/application/statistics/dtos.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatisticsMetricDTO 统计指标DTO
|
||||
type StatisticsMetricDTO struct {
|
||||
ID string `json:"id" comment:"统计指标唯一标识"`
|
||||
MetricType string `json:"metric_type" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" comment:"统计维度"`
|
||||
Value float64 `json:"value" comment:"指标值"`
|
||||
Metadata string `json:"metadata" comment:"额外维度信息"`
|
||||
Date time.Time `json:"date" comment:"统计日期"`
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// StatisticsReportDTO 统计报告DTO
|
||||
type StatisticsReportDTO struct {
|
||||
ID string `json:"id" comment:"报告唯一标识"`
|
||||
ReportType string `json:"report_type" comment:"报告类型"`
|
||||
Title string `json:"title" comment:"报告标题"`
|
||||
Content string `json:"content" comment:"报告内容"`
|
||||
Period string `json:"period" comment:"统计周期"`
|
||||
UserRole string `json:"user_role" comment:"用户角色"`
|
||||
Status string `json:"status" comment:"报告状态"`
|
||||
GeneratedBy string `json:"generated_by" comment:"生成者ID"`
|
||||
GeneratedAt *time.Time `json:"generated_at" comment:"生成时间"`
|
||||
ExpiresAt *time.Time `json:"expires_at" comment:"过期时间"`
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// StatisticsDashboardDTO 统计仪表板DTO
|
||||
type StatisticsDashboardDTO struct {
|
||||
ID string `json:"id" comment:"仪表板唯一标识"`
|
||||
Name string `json:"name" comment:"仪表板名称"`
|
||||
Description string `json:"description" comment:"仪表板描述"`
|
||||
UserRole string `json:"user_role" comment:"用户角色"`
|
||||
IsDefault bool `json:"is_default" comment:"是否为默认仪表板"`
|
||||
IsActive bool `json:"is_active" comment:"是否激活"`
|
||||
Layout string `json:"layout" comment:"布局配置"`
|
||||
Widgets string `json:"widgets" comment:"组件配置"`
|
||||
Settings string `json:"settings" comment:"设置配置"`
|
||||
RefreshInterval int `json:"refresh_interval" comment:"刷新间隔(秒)"`
|
||||
CreatedBy string `json:"created_by" comment:"创建者ID"`
|
||||
AccessLevel string `json:"access_level" comment:"访问级别"`
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// DashboardDataDTO 仪表板数据DTO
|
||||
type DashboardDataDTO struct {
|
||||
// API调用统计
|
||||
APICalls struct {
|
||||
TotalCount int64 `json:"total_count" comment:"总调用次数"`
|
||||
SuccessCount int64 `json:"success_count" comment:"成功调用次数"`
|
||||
FailedCount int64 `json:"failed_count" comment:"失败调用次数"`
|
||||
SuccessRate float64 `json:"success_rate" comment:"成功率"`
|
||||
AvgResponseTime float64 `json:"avg_response_time" comment:"平均响应时间"`
|
||||
} `json:"api_calls"`
|
||||
|
||||
// 用户统计
|
||||
Users struct {
|
||||
TotalCount int64 `json:"total_count" comment:"总用户数"`
|
||||
CertifiedCount int64 `json:"certified_count" comment:"认证用户数"`
|
||||
ActiveCount int64 `json:"active_count" comment:"活跃用户数"`
|
||||
CertificationRate float64 `json:"certification_rate" comment:"认证完成率"`
|
||||
RetentionRate float64 `json:"retention_rate" comment:"留存率"`
|
||||
} `json:"users"`
|
||||
|
||||
// 财务统计
|
||||
Finance struct {
|
||||
TotalAmount float64 `json:"total_amount" comment:"总金额"`
|
||||
RechargeAmount float64 `json:"recharge_amount" comment:"充值金额"`
|
||||
DeductAmount float64 `json:"deduct_amount" comment:"扣款金额"`
|
||||
NetAmount float64 `json:"net_amount" comment:"净金额"`
|
||||
} `json:"finance"`
|
||||
|
||||
// 产品统计
|
||||
Products struct {
|
||||
TotalProducts int64 `json:"total_products" comment:"总产品数"`
|
||||
ActiveProducts int64 `json:"active_products" comment:"活跃产品数"`
|
||||
TotalSubscriptions int64 `json:"total_subscriptions" comment:"总订阅数"`
|
||||
ActiveSubscriptions int64 `json:"active_subscriptions" comment:"活跃订阅数"`
|
||||
} `json:"products"`
|
||||
|
||||
// 认证统计
|
||||
Certification struct {
|
||||
TotalCertifications int64 `json:"total_certifications" comment:"总认证数"`
|
||||
CompletedCertifications int64 `json:"completed_certifications" comment:"完成认证数"`
|
||||
PendingCertifications int64 `json:"pending_certifications" comment:"待处理认证数"`
|
||||
FailedCertifications int64 `json:"failed_certifications" comment:"失败认证数"`
|
||||
CompletionRate float64 `json:"completion_rate" comment:"完成率"`
|
||||
} `json:"certification"`
|
||||
|
||||
// 时间信息
|
||||
Period struct {
|
||||
StartDate string `json:"start_date" comment:"开始日期"`
|
||||
EndDate string `json:"end_date" comment:"结束日期"`
|
||||
Period string `json:"period" comment:"统计周期"`
|
||||
} `json:"period"`
|
||||
|
||||
// 元数据
|
||||
Metadata struct {
|
||||
GeneratedAt string `json:"generated_at" comment:"生成时间"`
|
||||
UserRole string `json:"user_role" comment:"用户角色"`
|
||||
DataVersion string `json:"data_version" comment:"数据版本"`
|
||||
} `json:"metadata"`
|
||||
}
|
||||
|
||||
// RealtimeMetricsDTO 实时指标DTO
|
||||
type RealtimeMetricsDTO struct {
|
||||
MetricType string `json:"metric_type" comment:"指标类型"`
|
||||
Metrics map[string]float64 `json:"metrics" comment:"指标数据"`
|
||||
Timestamp time.Time `json:"timestamp" comment:"时间戳"`
|
||||
Metadata map[string]interface{} `json:"metadata" comment:"元数据"`
|
||||
}
|
||||
|
||||
// HistoricalMetricsDTO 历史指标DTO
|
||||
type HistoricalMetricsDTO struct {
|
||||
MetricType string `json:"metric_type" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" comment:"统计维度"`
|
||||
DataPoints []DataPointDTO `json:"data_points" comment:"数据点"`
|
||||
Summary MetricsSummaryDTO `json:"summary" comment:"汇总信息"`
|
||||
Metadata map[string]interface{} `json:"metadata" comment:"元数据"`
|
||||
}
|
||||
|
||||
// DataPointDTO 数据点DTO
|
||||
type DataPointDTO struct {
|
||||
Date time.Time `json:"date" comment:"日期"`
|
||||
Value float64 `json:"value" comment:"值"`
|
||||
Label string `json:"label" comment:"标签"`
|
||||
}
|
||||
|
||||
// MetricsSummaryDTO 指标汇总DTO
|
||||
type MetricsSummaryDTO struct {
|
||||
Total float64 `json:"total" comment:"总值"`
|
||||
Average float64 `json:"average" comment:"平均值"`
|
||||
Max float64 `json:"max" comment:"最大值"`
|
||||
Min float64 `json:"min" comment:"最小值"`
|
||||
Count int64 `json:"count" comment:"数据点数量"`
|
||||
GrowthRate float64 `json:"growth_rate" comment:"增长率"`
|
||||
Trend string `json:"trend" comment:"趋势"`
|
||||
}
|
||||
|
||||
// ReportContentDTO 报告内容DTO
|
||||
type ReportContentDTO struct {
|
||||
ReportType string `json:"report_type" comment:"报告类型"`
|
||||
Title string `json:"title" comment:"报告标题"`
|
||||
Summary map[string]interface{} `json:"summary" comment:"汇总信息"`
|
||||
Details map[string]interface{} `json:"details" comment:"详细信息"`
|
||||
Charts []ChartDTO `json:"charts" comment:"图表数据"`
|
||||
Tables []TableDTO `json:"tables" comment:"表格数据"`
|
||||
Metadata map[string]interface{} `json:"metadata" comment:"元数据"`
|
||||
}
|
||||
|
||||
// ChartDTO 图表DTO
|
||||
type ChartDTO struct {
|
||||
Type string `json:"type" comment:"图表类型"`
|
||||
Title string `json:"title" comment:"图表标题"`
|
||||
Data map[string]interface{} `json:"data" comment:"图表数据"`
|
||||
Options map[string]interface{} `json:"options" comment:"图表选项"`
|
||||
Description string `json:"description" comment:"图表描述"`
|
||||
}
|
||||
|
||||
// TableDTO 表格DTO
|
||||
type TableDTO struct {
|
||||
Title string `json:"title" comment:"表格标题"`
|
||||
Headers []string `json:"headers" comment:"表头"`
|
||||
Rows [][]interface{} `json:"rows" comment:"表格行数据"`
|
||||
Summary map[string]interface{} `json:"summary" comment:"汇总信息"`
|
||||
Description string `json:"description" comment:"表格描述"`
|
||||
}
|
||||
|
||||
// ExportDataDTO 导出数据DTO
|
||||
type ExportDataDTO struct {
|
||||
Format string `json:"format" comment:"导出格式"`
|
||||
FileName string `json:"file_name" comment:"文件名"`
|
||||
Data []map[string]interface{} `json:"data" comment:"导出数据"`
|
||||
Headers []string `json:"headers" comment:"表头"`
|
||||
Metadata map[string]interface{} `json:"metadata" comment:"元数据"`
|
||||
DownloadURL string `json:"download_url" comment:"下载链接"`
|
||||
}
|
||||
|
||||
// StatisticsQueryDTO 统计查询DTO
|
||||
type StatisticsQueryDTO struct {
|
||||
MetricType string `json:"metric_type" form:"metric_type" comment:"指标类型"`
|
||||
MetricName string `json:"metric_name" form:"metric_name" comment:"指标名称"`
|
||||
Dimension string `json:"dimension" form:"dimension" comment:"统计维度"`
|
||||
StartDate time.Time `json:"start_date" form:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" form:"end_date" comment:"结束日期"`
|
||||
Period string `json:"period" form:"period" comment:"统计周期"`
|
||||
UserRole string `json:"user_role" form:"user_role" comment:"用户角色"`
|
||||
Limit int `json:"limit" form:"limit" comment:"限制数量"`
|
||||
Offset int `json:"offset" form:"offset" comment:"偏移量"`
|
||||
SortBy string `json:"sort_by" form:"sort_by" comment:"排序字段"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order" comment:"排序顺序"`
|
||||
}
|
||||
|
||||
// ReportGenerationDTO 报告生成DTO
|
||||
type ReportGenerationDTO struct {
|
||||
ReportType string `json:"report_type" comment:"报告类型"`
|
||||
Title string `json:"title" comment:"报告标题"`
|
||||
Period string `json:"period" comment:"统计周期"`
|
||||
UserRole string `json:"user_role" comment:"用户角色"`
|
||||
StartDate time.Time `json:"start_date" comment:"开始日期"`
|
||||
EndDate time.Time `json:"end_date" comment:"结束日期"`
|
||||
Filters map[string]interface{} `json:"filters" comment:"过滤条件"`
|
||||
Format string `json:"format" comment:"输出格式"`
|
||||
GeneratedBy string `json:"generated_by" comment:"生成者ID"`
|
||||
}
|
||||
|
||||
// DashboardConfigDTO 仪表板配置DTO
|
||||
type DashboardConfigDTO struct {
|
||||
Name string `json:"name" comment:"仪表板名称"`
|
||||
Description string `json:"description" comment:"仪表板描述"`
|
||||
UserRole string `json:"user_role" comment:"用户角色"`
|
||||
Layout string `json:"layout" comment:"布局配置"`
|
||||
Widgets string `json:"widgets" comment:"组件配置"`
|
||||
Settings string `json:"settings" comment:"设置配置"`
|
||||
RefreshInterval int `json:"refresh_interval" comment:"刷新间隔(秒)"`
|
||||
AccessLevel string `json:"access_level" comment:"访问级别"`
|
||||
CreatedBy string `json:"created_by" comment:"创建者ID"`
|
||||
}
|
||||
|
||||
// StatisticsResponseDTO 统计响应DTO
|
||||
type StatisticsResponseDTO struct {
|
||||
Success bool `json:"success" comment:"是否成功"`
|
||||
Message string `json:"message" comment:"响应消息"`
|
||||
Data interface{} `json:"data" comment:"响应数据"`
|
||||
Meta map[string]interface{} `json:"meta" comment:"元数据"`
|
||||
Error string `json:"error,omitempty" comment:"错误信息"`
|
||||
}
|
||||
|
||||
// PaginationDTO 分页DTO
|
||||
type PaginationDTO struct {
|
||||
Page int `json:"page" comment:"当前页"`
|
||||
PageSize int `json:"page_size" comment:"每页大小"`
|
||||
Total int64 `json:"total" comment:"总数量"`
|
||||
Pages int `json:"pages" comment:"总页数"`
|
||||
HasNext bool `json:"has_next" comment:"是否有下一页"`
|
||||
HasPrev bool `json:"has_prev" comment:"是否有上一页"`
|
||||
}
|
||||
|
||||
// StatisticsListResponseDTO 统计列表响应DTO
|
||||
type StatisticsListResponseDTO struct {
|
||||
Success bool `json:"success" comment:"是否成功"`
|
||||
Message string `json:"message" comment:"响应消息"`
|
||||
Data []interface{} `json:"data" comment:"数据列表"`
|
||||
Pagination PaginationDTO `json:"pagination" comment:"分页信息"`
|
||||
Meta map[string]interface{} `json:"meta" comment:"元数据"`
|
||||
Error string `json:"error,omitempty" comment:"错误信息"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatisticsApplicationService 统计应用服务接口
|
||||
// 负责统计功能的业务逻辑编排和协调
|
||||
type StatisticsApplicationService interface {
|
||||
// ================ 指标管理 ================
|
||||
|
||||
// CreateMetric 创建统计指标
|
||||
CreateMetric(ctx context.Context, cmd *CreateMetricCommand) (*CommandResponse, error)
|
||||
|
||||
// UpdateMetric 更新统计指标
|
||||
UpdateMetric(ctx context.Context, cmd *UpdateMetricCommand) (*CommandResponse, error)
|
||||
|
||||
// DeleteMetric 删除统计指标
|
||||
DeleteMetric(ctx context.Context, cmd *DeleteMetricCommand) (*CommandResponse, error)
|
||||
|
||||
// GetMetric 获取单个指标
|
||||
GetMetric(ctx context.Context, query *GetMetricQuery) (*QueryResponse, error)
|
||||
|
||||
// GetMetrics 获取指标列表
|
||||
GetMetrics(ctx context.Context, query *GetMetricsQuery) (*ListResponse, error)
|
||||
|
||||
// ================ 实时统计 ================
|
||||
|
||||
// GetRealtimeMetrics 获取实时指标
|
||||
GetRealtimeMetrics(ctx context.Context, query *GetRealtimeMetricsQuery) (*QueryResponse, error)
|
||||
|
||||
// UpdateRealtimeMetric 更新实时指标
|
||||
UpdateRealtimeMetric(ctx context.Context, metricType, metricName string, value float64) error
|
||||
|
||||
// ================ 历史统计 ================
|
||||
|
||||
// GetHistoricalMetrics 获取历史指标
|
||||
GetHistoricalMetrics(ctx context.Context, query *GetHistoricalMetricsQuery) (*QueryResponse, error)
|
||||
|
||||
// AggregateMetrics 聚合指标
|
||||
AggregateMetrics(ctx context.Context, metricType, dimension string, startDate, endDate time.Time) error
|
||||
|
||||
// ================ 仪表板管理 ================
|
||||
|
||||
// CreateDashboard 创建仪表板
|
||||
CreateDashboard(ctx context.Context, cmd *CreateDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// UpdateDashboard 更新仪表板
|
||||
UpdateDashboard(ctx context.Context, cmd *UpdateDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// DeleteDashboard 删除仪表板
|
||||
DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// GetDashboard 获取单个仪表板
|
||||
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*QueryResponse, error)
|
||||
|
||||
// GetDashboards 获取仪表板列表
|
||||
GetDashboards(ctx context.Context, query *GetDashboardsQuery) (*ListResponse, error)
|
||||
|
||||
// SetDefaultDashboard 设置默认仪表板
|
||||
SetDefaultDashboard(ctx context.Context, cmd *SetDefaultDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// ActivateDashboard 激活仪表板
|
||||
ActivateDashboard(ctx context.Context, cmd *ActivateDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// DeactivateDashboard 停用仪表板
|
||||
DeactivateDashboard(ctx context.Context, cmd *DeactivateDashboardCommand) (*CommandResponse, error)
|
||||
|
||||
// GetDashboardData 获取仪表板数据
|
||||
GetDashboardData(ctx context.Context, query *GetDashboardDataQuery) (*QueryResponse, error)
|
||||
|
||||
// ================ 报告管理 ================
|
||||
|
||||
// GenerateReport 生成报告
|
||||
GenerateReport(ctx context.Context, cmd *GenerateReportCommand) (*CommandResponse, error)
|
||||
|
||||
// GetReport 获取单个报告
|
||||
GetReport(ctx context.Context, query *GetReportQuery) (*QueryResponse, error)
|
||||
|
||||
// GetReports 获取报告列表
|
||||
GetReports(ctx context.Context, query *GetReportsQuery) (*ListResponse, error)
|
||||
|
||||
// DeleteReport 删除报告
|
||||
DeleteReport(ctx context.Context, reportID string) (*CommandResponse, error)
|
||||
|
||||
// ================ 统计分析 ================
|
||||
|
||||
// CalculateGrowthRate 计算增长率
|
||||
CalculateGrowthRate(ctx context.Context, query *CalculateGrowthRateQuery) (*QueryResponse, error)
|
||||
|
||||
// CalculateTrend 计算趋势
|
||||
CalculateTrend(ctx context.Context, query *CalculateTrendQuery) (*QueryResponse, error)
|
||||
|
||||
// CalculateCorrelation 计算相关性
|
||||
CalculateCorrelation(ctx context.Context, query *CalculateCorrelationQuery) (*QueryResponse, error)
|
||||
|
||||
// CalculateMovingAverage 计算移动平均
|
||||
CalculateMovingAverage(ctx context.Context, query *CalculateMovingAverageQuery) (*QueryResponse, error)
|
||||
|
||||
// CalculateSeasonality 计算季节性
|
||||
CalculateSeasonality(ctx context.Context, query *CalculateSeasonalityQuery) (*QueryResponse, error)
|
||||
|
||||
// ================ 数据导出 ================
|
||||
|
||||
// ExportData 导出数据
|
||||
ExportData(ctx context.Context, cmd *ExportDataCommand) (*CommandResponse, error)
|
||||
|
||||
// ================ 定时任务 ================
|
||||
|
||||
// ProcessHourlyAggregation 处理小时级聚合
|
||||
ProcessHourlyAggregation(ctx context.Context, date time.Time) error
|
||||
|
||||
// ProcessDailyAggregation 处理日级聚合
|
||||
ProcessDailyAggregation(ctx context.Context, date time.Time) error
|
||||
|
||||
// ProcessWeeklyAggregation 处理周级聚合
|
||||
ProcessWeeklyAggregation(ctx context.Context, date time.Time) error
|
||||
|
||||
// ProcessMonthlyAggregation 处理月级聚合
|
||||
ProcessMonthlyAggregation(ctx context.Context, date time.Time) error
|
||||
|
||||
// CleanupExpiredData 清理过期数据
|
||||
CleanupExpiredData(ctx context.Context) error
|
||||
|
||||
// ================ 管理员专用方法 ================
|
||||
|
||||
// AdminGetSystemStatistics 管理员获取系统统计
|
||||
AdminGetSystemStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error)
|
||||
|
||||
// AdminTriggerAggregation 管理员触发数据聚合
|
||||
AdminTriggerAggregation(ctx context.Context, cmd *TriggerAggregationCommand) (*CommandResponse, error)
|
||||
|
||||
// AdminGetUserStatistics 管理员获取单个用户统计
|
||||
AdminGetUserStatistics(ctx context.Context, userID string) (*QueryResponse, error)
|
||||
|
||||
// ================ 管理员独立域统计接口 ================
|
||||
|
||||
// AdminGetUserDomainStatistics 管理员获取用户域统计
|
||||
AdminGetUserDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error)
|
||||
|
||||
// AdminGetApiDomainStatistics 管理员获取API域统计
|
||||
AdminGetApiDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error)
|
||||
|
||||
// AdminGetConsumptionDomainStatistics 管理员获取消费域统计
|
||||
AdminGetConsumptionDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error)
|
||||
|
||||
// AdminGetRechargeDomainStatistics 管理员获取充值域统计
|
||||
AdminGetRechargeDomainStatistics(ctx context.Context, period, startDate, endDate string) (*QueryResponse, error)
|
||||
|
||||
// ================ 公开和用户统计方法 ================
|
||||
|
||||
// GetPublicStatistics 获取公开统计信息
|
||||
GetPublicStatistics(ctx context.Context) (*QueryResponse, error)
|
||||
|
||||
// GetUserStatistics 获取用户统计信息
|
||||
GetUserStatistics(ctx context.Context, userID string) (*QueryResponse, error)
|
||||
|
||||
// ================ 独立统计接口 ================
|
||||
|
||||
// GetApiCallsStatistics 获取API调用统计
|
||||
GetApiCallsStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error)
|
||||
|
||||
// GetConsumptionStatistics 获取消费统计
|
||||
GetConsumptionStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error)
|
||||
|
||||
// GetRechargeStatistics 获取充值统计
|
||||
GetRechargeStatistics(ctx context.Context, userID string, startDate, endDate time.Time, unit string) (*QueryResponse, error)
|
||||
|
||||
// GetLatestProducts 获取最新产品推荐
|
||||
GetLatestProducts(ctx context.Context, limit int) (*QueryResponse, error)
|
||||
|
||||
// ================ 管理员排行榜接口 ================
|
||||
|
||||
// AdminGetUserCallRanking 获取用户调用排行榜
|
||||
AdminGetUserCallRanking(ctx context.Context, rankingType, period string, limit int) (*QueryResponse, error)
|
||||
|
||||
// AdminGetRechargeRanking 获取充值排行榜
|
||||
AdminGetRechargeRanking(ctx context.Context, period string, limit int) (*QueryResponse, error)
|
||||
|
||||
// AdminGetApiPopularityRanking 获取API受欢迎程度排行榜
|
||||
AdminGetApiPopularityRanking(ctx context.Context, period string, limit int) (*QueryResponse, error)
|
||||
|
||||
// AdminGetTodayCertifiedEnterprises 获取今日认证企业列表
|
||||
AdminGetTodayCertifiedEnterprises(ctx context.Context, limit int) (*QueryResponse, error)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user