add购买记录功能

This commit is contained in:
2025-12-22 18:32:34 +08:00
parent 65a61d0336
commit 7f8554fa12
314 changed files with 4029 additions and 83496 deletions

View File

@@ -0,0 +1,352 @@
package repositories
import (
"context"
"errors"
"time"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories"
"tyapi-server/internal/shared/database"
"tyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
PurchaseOrdersTable = "ty_purchase_orders"
)
type GormPurchaseOrderRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ repositories.PurchaseOrderRepository = (*GormPurchaseOrderRepository)(nil)
func NewGormPurchaseOrderRepository(db *gorm.DB, logger *zap.Logger) repositories.PurchaseOrderRepository {
return &GormPurchaseOrderRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, PurchaseOrdersTable),
}
}
func (r *GormPurchaseOrderRepository) Create(ctx context.Context, order *entities.PurchaseOrder) (*entities.PurchaseOrder, error) {
err := r.CreateEntity(ctx, order)
if err != nil {
return nil, err
}
return order, nil
}
func (r *GormPurchaseOrderRepository) Update(ctx context.Context, order *entities.PurchaseOrder) error {
return r.UpdateEntity(ctx, order)
}
func (r *GormPurchaseOrderRepository) GetByID(ctx context.Context, id string) (*entities.PurchaseOrder, error) {
var order entities.PurchaseOrder
err := r.SmartGetByID(ctx, id, &order)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &order, nil
}
func (r *GormPurchaseOrderRepository) GetByOrderNo(ctx context.Context, orderNo string) (*entities.PurchaseOrder, error) {
var order entities.PurchaseOrder
err := r.GetDB(ctx).Where("order_no = ?", orderNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &order, nil
}
func (r *GormPurchaseOrderRepository) GetByUserID(ctx context.Context, userID string, limit, offset int) ([]*entities.PurchaseOrder, int64, error) {
var orders []entities.PurchaseOrder
var count int64
db := r.GetDB(ctx).Where("user_id = ?", userID)
// 获取总数
err := db.Model(&entities.PurchaseOrder{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
err = db.Order("created_at DESC").
Limit(limit).
Offset(offset).
Find(&orders).Error
if err != nil {
return nil, 0, err
}
result := make([]*entities.PurchaseOrder, len(orders))
for i := range orders {
result[i] = &orders[i]
}
return result, count, nil
}
func (r *GormPurchaseOrderRepository) GetByUserIDAndProductID(ctx context.Context, userID, productID string) (*entities.PurchaseOrder, error) {
var order entities.PurchaseOrder
err := r.GetDB(ctx).
Where("user_id = ? AND product_id = ? AND status = ?", userID, productID, entities.PurchaseOrderStatusPaid).
First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &order, nil
}
func (r *GormPurchaseOrderRepository) GetByPaymentTypeAndTransactionID(ctx context.Context, paymentType, transactionID string) (*entities.PurchaseOrder, error) {
var order entities.PurchaseOrder
err := r.GetDB(ctx).
Where("payment_type = ? AND trade_no = ?", paymentType, transactionID).
First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &order, nil
}
func (r *GormPurchaseOrderRepository) GetByTradeNo(ctx context.Context, tradeNo string) (*entities.PurchaseOrder, error) {
var order entities.PurchaseOrder
err := r.GetDB(ctx).Where("trade_no = ?", tradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &order, nil
}
func (r *GormPurchaseOrderRepository) UpdatePaymentStatus(ctx context.Context, orderID string, status entities.PurchaseOrderStatus, tradeNo *string, payAmount, receiptAmount *string, paymentTime *time.Time) error {
updates := map[string]interface{}{
"status": status,
}
if tradeNo != nil {
updates["trade_no"] = *tradeNo
}
if payAmount != nil {
updates["pay_amount"] = *payAmount
}
if receiptAmount != nil {
updates["receipt_amount"] = *receiptAmount
}
if paymentTime != nil {
updates["pay_time"] = *paymentTime
updates["notify_time"] = *paymentTime
}
err := r.GetDB(ctx).
Model(&entities.PurchaseOrder{}).
Where("id = ?", orderID).
Updates(updates).Error
return err
}
func (r *GormPurchaseOrderRepository) GetUserPurchasedProductCodes(ctx context.Context, userID string) ([]string, error) {
var orders []entities.PurchaseOrder
err := r.GetDB(ctx).
Select("product_code").
Where("user_id = ? AND status = ?", userID, entities.PurchaseOrderStatusPaid).
Find(&orders).Error
if err != nil {
return nil, err
}
codesMap := make(map[string]bool)
for _, order := range orders {
// 添加主产品编号
if order.ProductCode != "" {
codesMap[order.ProductCode] = true
}
}
codes := make([]string, 0, len(codesMap))
for code := range codesMap {
codes = append(codes, code)
}
return codes, nil
}
func (r *GormPurchaseOrderRepository) GetUserPaidProductIDs(ctx context.Context, userID string) ([]string, error) {
var orders []entities.PurchaseOrder
err := r.GetDB(ctx).
Select("product_id").
Where("user_id = ? AND status = ?", userID, entities.PurchaseOrderStatusPaid).
Find(&orders).Error
if err != nil {
return nil, err
}
idsMap := make(map[string]bool)
for _, order := range orders {
// 添加主产品ID
if order.ProductID != "" {
idsMap[order.ProductID] = true
}
}
ids := make([]string, 0, len(idsMap))
for id := range idsMap {
ids = append(ids, id)
}
return ids, nil
}
func (r *GormPurchaseOrderRepository) HasUserPurchased(ctx context.Context, userID string, productCode string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.PurchaseOrder{}).
Where("user_id = ? AND product_code = ? AND status = ?", userID, productCode, entities.PurchaseOrderStatusPaid).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (r *GormPurchaseOrderRepository) GetExpiringOrders(ctx context.Context, before time.Time, limit int) ([]*entities.PurchaseOrder, error) {
// 购买订单实体没有过期时间字段,此方法返回空结果
return []*entities.PurchaseOrder{}, nil
}
func (r *GormPurchaseOrderRepository) GetExpiredOrders(ctx context.Context, limit int) ([]*entities.PurchaseOrder, error) {
// 购买订单实体没有过期时间字段,此方法返回空结果
return []*entities.PurchaseOrder{}, nil
}
func (r *GormPurchaseOrderRepository) GetByStatus(ctx context.Context, status entities.PurchaseOrderStatus, limit, offset int) ([]*entities.PurchaseOrder, int64, error) {
var orders []entities.PurchaseOrder
var count int64
db := r.GetDB(ctx).Where("status = ?", status)
// 获取总数
err := db.Model(&entities.PurchaseOrder{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
err = db.Order("created_at DESC").
Limit(limit).
Offset(offset).
Find(&orders).Error
if err != nil {
return nil, 0, err
}
result := make([]*entities.PurchaseOrder, len(orders))
for i := range orders {
result[i] = &orders[i]
}
return result, count, nil
}
func (r *GormPurchaseOrderRepository) GetByFilters(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.PurchaseOrder, error) {
var orders []entities.PurchaseOrder
db := r.GetDB(ctx)
// 应用筛选条件
if filters != nil {
if userID, ok := filters["user_id"]; ok {
db = db.Where("user_id = ?", userID)
}
if status, ok := filters["status"]; ok && status != "" {
db = db.Where("status = ?", status)
}
if paymentType, ok := filters["payment_type"]; ok && paymentType != "" {
db = db.Where("payment_type = ?", paymentType)
}
if payChannel, ok := filters["pay_channel"]; ok && payChannel != "" {
db = db.Where("pay_channel = ?", payChannel)
}
if startTime, ok := filters["start_time"]; ok && startTime != "" {
db = db.Where("created_at >= ?", startTime)
}
if endTime, ok := filters["end_time"]; ok && endTime != "" {
db = db.Where("created_at <= ?", endTime)
}
}
// 应用排序和分页
// 默认按创建时间倒序
db = db.Order("created_at DESC")
// 应用分页
if options.PageSize > 0 {
db = db.Limit(options.PageSize)
}
if options.Page > 0 {
db = db.Offset((options.Page - 1) * options.PageSize)
}
// 执行查询
err := db.Find(&orders).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.PurchaseOrder, len(orders))
for i := range orders {
result[i] = &orders[i]
}
return result, nil
}
func (r *GormPurchaseOrderRepository) CountByFilters(ctx context.Context, filters map[string]interface{}) (int64, error) {
var count int64
db := r.GetDB(ctx).Model(&entities.PurchaseOrder{})
// 应用筛选条件
if filters != nil {
if userID, ok := filters["user_id"]; ok {
db = db.Where("user_id = ?", userID)
}
if status, ok := filters["status"]; ok && status != "" {
db = db.Where("status = ?", status)
}
if paymentType, ok := filters["payment_type"]; ok && paymentType != "" {
db = db.Where("payment_type = ?", paymentType)
}
if payChannel, ok := filters["pay_channel"]; ok && payChannel != "" {
db = db.Where("pay_channel = ?", payChannel)
}
if startTime, ok := filters["start_time"]; ok && startTime != "" {
db = db.Where("created_at >= ?", startTime)
}
if endTime, ok := filters["end_time"]; ok && endTime != "" {
db = db.Where("created_at <= ?", endTime)
}
}
// 执行计数
err := db.Count(&count).Error
return count, err
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"time"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
@@ -29,12 +30,8 @@ func NewGormComponentReportRepository(db *gorm.DB, logger *zap.Logger) repositor
}
}
func (r *GormComponentReportRepository) CreateDownload(ctx context.Context, download *entities.ComponentReportDownload) (*entities.ComponentReportDownload, error) {
err := r.CreateEntity(ctx, download)
if err != nil {
return nil, err
}
return download, nil
func (r *GormComponentReportRepository) Create(ctx context.Context, download *entities.ComponentReportDownload) error {
return r.CreateEntity(ctx, download)
}
func (r *GormComponentReportRepository) UpdateDownload(ctx context.Context, download *entities.ComponentReportDownload) error {
@@ -55,7 +52,7 @@ func (r *GormComponentReportRepository) GetDownloadByID(ctx context.Context, id
func (r *GormComponentReportRepository) GetUserDownloads(ctx context.Context, userID string, productID *string) ([]*entities.ComponentReportDownload, error) {
var downloads []entities.ComponentReportDownload
query := r.GetDB(ctx).Where("user_id = ? AND payment_status = ?", userID, "success")
query := r.GetDB(ctx).Where("user_id = ?", userID)
if productID != nil && *productID != "" {
query = query.Where("product_id = ?", *productID)
@@ -76,7 +73,7 @@ func (r *GormComponentReportRepository) GetUserDownloads(ctx context.Context, us
func (r *GormComponentReportRepository) HasUserDownloaded(ctx context.Context, userID string, productCode string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).
Where("user_id = ? AND product_code = ? AND payment_status = ?", userID, productCode, "success").
Where("user_id = ? AND product_code = ?", userID, productCode).
Count(&count).Error
if err != nil {
return false, err
@@ -88,7 +85,7 @@ func (r *GormComponentReportRepository) GetUserDownloadedProductCodes(ctx contex
var downloads []entities.ComponentReportDownload
err := r.GetDB(ctx).
Select("DISTINCT sub_product_codes").
Where("user_id = ? AND payment_status = ?", userID, "success").
Where("user_id = ?", userID).
Find(&downloads).Error
if err != nil {
return nil, err
@@ -119,7 +116,7 @@ func (r *GormComponentReportRepository) GetUserDownloadedProductCodes(ctx contex
func (r *GormComponentReportRepository) GetDownloadByPaymentOrderID(ctx context.Context, orderID string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
err := r.GetDB(ctx).Where("payment_order_id = ?", orderID).First(&download).Error
err := r.GetDB(ctx).Where("order_number = ?", orderID).First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
@@ -128,3 +125,65 @@ func (r *GormComponentReportRepository) GetDownloadByPaymentOrderID(ctx context.
}
return &download, nil
}
// GetActiveDownload 获取用户有效的下载记录
func (r *GormComponentReportRepository) GetActiveDownload(ctx context.Context, userID, productID string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
// 先尝试查找有支付订单号的下载记录(已支付)
err := r.GetDB(ctx).
Where("user_id = ? AND product_id = ? AND order_number IS NOT NULL AND deleted_at IS NULL", userID, productID).
Order("created_at DESC").
First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 如果没有找到有支付订单号的记录,尝试查找任何有效的下载记录
err = r.GetDB(ctx).
Where("user_id = ? AND product_id = ? AND deleted_at IS NULL", userID, productID).
Order("created_at DESC").
First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
} else {
return nil, err
}
}
// 如果找到了下载记录,检查关联的购买订单状态
if download.OrderID != nil {
// 这里需要查询购买订单状态,但当前仓库没有依赖购买订单仓库
// 所以只检查是否有过期时间设置,如果有则认为已支付
if download.ExpiresAt == nil {
return nil, nil // 没有过期时间,表示未支付
}
}
// 检查是否已过期
if download.IsExpired() {
return nil, nil
}
return &download, nil
}
// UpdateFilePath 更新下载记录文件路径
func (r *GormComponentReportRepository) UpdateFilePath(ctx context.Context, downloadID, filePath string) error {
return r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).Where("id = ?", downloadID).Update("file_path", filePath).Error
}
// IncrementDownloadCount 增加下载次数
func (r *GormComponentReportRepository) IncrementDownloadCount(ctx context.Context, downloadID string) error {
now := time.Now()
return r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).
Where("id = ?", downloadID).
Updates(map[string]interface{}{
"download_count": gorm.Expr("download_count + 1"),
"last_download_at": &now,
}).Error
}