This commit is contained in:
2026-04-21 22:36:48 +08:00
commit 488c695fdf
748 changed files with 266838 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/finance/entities"
domain_finance_repo "hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
AlipayOrdersTable = "typay_orders"
)
type GormAlipayOrderRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.AlipayOrderRepository = (*GormAlipayOrderRepository)(nil)
func NewGormAlipayOrderRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.AlipayOrderRepository {
return &GormAlipayOrderRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, AlipayOrdersTable),
}
}
func (r *GormAlipayOrderRepository) Create(ctx context.Context, order entities.AlipayOrder) (entities.AlipayOrder, error) {
err := r.CreateEntity(ctx, &order)
return order, err
}
func (r *GormAlipayOrderRepository) GetByID(ctx context.Context, id string) (entities.AlipayOrder, error) {
var order entities.AlipayOrder
err := r.SmartGetByID(ctx, id, &order)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.AlipayOrder{}, gorm.ErrRecordNotFound
}
return entities.AlipayOrder{}, err
}
return order, nil
}
func (r *GormAlipayOrderRepository) GetByOutTradeNo(ctx context.Context, outTradeNo string) (*entities.AlipayOrder, error) {
var order entities.AlipayOrder
err := r.GetDB(ctx).Where("out_trade_no = ?", outTradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormAlipayOrderRepository) GetByRechargeID(ctx context.Context, rechargeID string) (*entities.AlipayOrder, error) {
var order entities.AlipayOrder
err := r.GetDB(ctx).Where("recharge_id = ?", rechargeID).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormAlipayOrderRepository) GetByUserID(ctx context.Context, userID string) ([]entities.AlipayOrder, error) {
var orders []entities.AlipayOrder
err := r.GetDB(ctx).
Joins("JOIN recharge_records ON typay_orders.recharge_id = recharge_records.id").
Where("recharge_records.user_id = ?", userID).
Order("typay_orders.created_at DESC").
Find(&orders).Error
return orders, err
}
func (r *GormAlipayOrderRepository) Update(ctx context.Context, order entities.AlipayOrder) error {
return r.UpdateEntity(ctx, &order)
}
func (r *GormAlipayOrderRepository) UpdateStatus(ctx context.Context, id string, status entities.AlipayOrderStatus) error {
return r.GetDB(ctx).Model(&entities.AlipayOrder{}).Where("id = ?", id).Update("status", status).Error
}
func (r *GormAlipayOrderRepository) Delete(ctx context.Context, id string) error {
return r.GetDB(ctx).Delete(&entities.AlipayOrder{}, "id = ?", id).Error
}
func (r *GormAlipayOrderRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.AlipayOrder{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}

View File

@@ -0,0 +1,352 @@
package repositories
import (
"context"
"errors"
"time"
"hyapi-server/internal/domains/finance/entities"
"hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"hyapi-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

@@ -0,0 +1,509 @@
package repositories
import (
"context"
"errors"
"fmt"
"strings"
"time"
"hyapi-server/internal/domains/finance/entities"
domain_finance_repo "hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
RechargeRecordsTable = "recharge_records"
)
type GormRechargeRecordRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.RechargeRecordRepository = (*GormRechargeRecordRepository)(nil)
func NewGormRechargeRecordRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.RechargeRecordRepository {
return &GormRechargeRecordRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, RechargeRecordsTable),
}
}
func (r *GormRechargeRecordRepository) Create(ctx context.Context, record entities.RechargeRecord) (entities.RechargeRecord, error) {
err := r.CreateEntity(ctx, &record)
return record, err
}
func (r *GormRechargeRecordRepository) GetByID(ctx context.Context, id string) (entities.RechargeRecord, error) {
var record entities.RechargeRecord
err := r.SmartGetByID(ctx, id, &record)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.RechargeRecord{}, gorm.ErrRecordNotFound
}
return entities.RechargeRecord{}, err
}
return record, nil
}
func (r *GormRechargeRecordRepository) GetByUserID(ctx context.Context, userID string) ([]entities.RechargeRecord, error) {
var records []entities.RechargeRecord
err := r.GetDB(ctx).Where("user_id = ?", userID).Order("created_at DESC").Find(&records).Error
return records, err
}
func (r *GormRechargeRecordRepository) GetByAlipayOrderID(ctx context.Context, alipayOrderID string) (*entities.RechargeRecord, error) {
var record entities.RechargeRecord
err := r.GetDB(ctx).Where("alipay_order_id = ?", alipayOrderID).First(&record).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &record, nil
}
func (r *GormRechargeRecordRepository) GetByTransferOrderID(ctx context.Context, transferOrderID string) (*entities.RechargeRecord, error) {
var record entities.RechargeRecord
err := r.GetDB(ctx).Where("transfer_order_id = ?", transferOrderID).First(&record).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &record, nil
}
func (r *GormRechargeRecordRepository) Update(ctx context.Context, record entities.RechargeRecord) error {
return r.UpdateEntity(ctx, &record)
}
func (r *GormRechargeRecordRepository) UpdateStatus(ctx context.Context, id string, status entities.RechargeStatus) error {
return r.GetDB(ctx).Model(&entities.RechargeRecord{}).Where("id = ?", id).Update("status", status).Error
}
func (r *GormRechargeRecordRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
// 检查是否有 company_name 筛选,如果有则需要 JOIN 表
hasCompanyNameFilter := false
if options.Filters != nil {
if companyName, ok := options.Filters["company_name"].(string); ok && companyName != "" {
hasCompanyNameFilter = true
}
}
var query *gorm.DB
if hasCompanyNameFilter {
// 使用 JOIN 查询以支持企业名称筛选
query = r.GetDB(ctx).Table("recharge_records rr").
Joins("LEFT JOIN users u ON rr.user_id = u.id").
Joins("LEFT JOIN enterprise_infos ei ON u.id = ei.user_id")
} else {
// 普通查询
query = r.GetDB(ctx).Model(&entities.RechargeRecord{})
}
if options.Filters != nil {
for key, value := range options.Filters {
// 特殊处理时间范围过滤器
if key == "start_time" {
if startTime, ok := value.(time.Time); ok {
if hasCompanyNameFilter {
query = query.Where("rr.created_at >= ?", startTime)
} else {
query = query.Where("created_at >= ?", startTime)
}
}
} else if key == "end_time" {
if endTime, ok := value.(time.Time); ok {
if hasCompanyNameFilter {
query = query.Where("rr.created_at <= ?", endTime)
} else {
query = query.Where("created_at <= ?", endTime)
}
}
} else if key == "company_name" {
// 处理企业名称筛选
if companyName, ok := value.(string); ok && companyName != "" {
query = query.Where("ei.company_name LIKE ?", "%"+companyName+"%")
}
} else if key == "min_amount" {
// 处理最小金额支持string、int、int64类型
if amount, err := r.parseAmount(value); err == nil {
if hasCompanyNameFilter {
query = query.Where("rr.amount >= ?", amount)
} else {
query = query.Where("amount >= ?", amount)
}
}
} else if key == "max_amount" {
// 处理最大金额支持string、int、int64类型
if amount, err := r.parseAmount(value); err == nil {
if hasCompanyNameFilter {
query = query.Where("rr.amount <= ?", amount)
} else {
query = query.Where("amount <= ?", amount)
}
}
} else {
// 其他过滤器使用等值查询
if hasCompanyNameFilter {
query = query.Where("rr."+key+" = ?", value)
} else {
query = query.Where(key+" = ?", value)
}
}
}
}
if options.Search != "" {
if hasCompanyNameFilter {
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ? OR rr.wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
} else {
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ? OR wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
}
}
return count, query.Count(&count).Error
}
func (r *GormRechargeRecordRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
func (r *GormRechargeRecordRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.RechargeRecord, error) {
var records []entities.RechargeRecord
// 检查是否有 company_name 筛选,如果有则需要 JOIN 表
hasCompanyNameFilter := false
if options.Filters != nil {
if companyName, ok := options.Filters["company_name"].(string); ok && companyName != "" {
hasCompanyNameFilter = true
}
}
var query *gorm.DB
if hasCompanyNameFilter {
// 使用 JOIN 查询以支持企业名称筛选
query = r.GetDB(ctx).Table("recharge_records rr").
Select("rr.*").
Joins("LEFT JOIN users u ON rr.user_id = u.id").
Joins("LEFT JOIN enterprise_infos ei ON u.id = ei.user_id")
} else {
// 普通查询
query = r.GetDB(ctx).Model(&entities.RechargeRecord{})
}
if options.Filters != nil {
for key, value := range options.Filters {
// 特殊处理 user_ids 过滤器
if key == "user_ids" {
if userIds, ok := value.(string); ok && userIds != "" {
if hasCompanyNameFilter {
query = query.Where("rr.user_id IN ?", strings.Split(userIds, ","))
} else {
query = query.Where("user_id IN ?", strings.Split(userIds, ","))
}
}
} else if key == "company_name" {
// 处理企业名称筛选
if companyName, ok := value.(string); ok && companyName != "" {
query = query.Where("ei.company_name LIKE ?", "%"+companyName+"%")
}
} else if key == "start_time" {
// 处理开始时间范围
if startTime, ok := value.(time.Time); ok {
if hasCompanyNameFilter {
query = query.Where("rr.created_at >= ?", startTime)
} else {
query = query.Where("created_at >= ?", startTime)
}
}
} else if key == "end_time" {
// 处理结束时间范围
if endTime, ok := value.(time.Time); ok {
if hasCompanyNameFilter {
query = query.Where("rr.created_at <= ?", endTime)
} else {
query = query.Where("created_at <= ?", endTime)
}
}
} else if key == "min_amount" {
// 处理最小金额支持string、int、int64类型
if amount, err := r.parseAmount(value); err == nil {
if hasCompanyNameFilter {
query = query.Where("rr.amount >= ?", amount)
} else {
query = query.Where("amount >= ?", amount)
}
}
} else if key == "max_amount" {
// 处理最大金额支持string、int、int64类型
if amount, err := r.parseAmount(value); err == nil {
if hasCompanyNameFilter {
query = query.Where("rr.amount <= ?", amount)
} else {
query = query.Where("amount <= ?", amount)
}
}
} else {
// 其他过滤器使用等值查询
if hasCompanyNameFilter {
query = query.Where("rr."+key+" = ?", value)
} else {
query = query.Where(key+" = ?", value)
}
}
}
}
if options.Search != "" {
if hasCompanyNameFilter {
query = query.Where("rr.user_id LIKE ? OR rr.transfer_order_id LIKE ? OR rr.alipay_order_id LIKE ? OR rr.wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
} else {
query = query.Where("user_id LIKE ? OR transfer_order_id LIKE ? OR alipay_order_id LIKE ? OR wechat_order_id LIKE ?",
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
}
}
if options.Sort != "" {
order := "ASC"
if options.Order == "desc" || options.Order == "DESC" {
order = "DESC"
}
if hasCompanyNameFilter {
query = query.Order("rr." + options.Sort + " " + order)
} else {
query = query.Order(options.Sort + " " + order)
}
} else {
if hasCompanyNameFilter {
query = query.Order("rr.created_at DESC")
} else {
query = query.Order("created_at DESC")
}
}
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
err := query.Find(&records).Error
return records, err
}
func (r *GormRechargeRecordRepository) CreateBatch(ctx context.Context, records []entities.RechargeRecord) error {
return r.GetDB(ctx).Create(&records).Error
}
func (r *GormRechargeRecordRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.RechargeRecord, error) {
var records []entities.RechargeRecord
err := r.GetDB(ctx).Where("id IN ?", ids).Find(&records).Error
return records, err
}
func (r *GormRechargeRecordRepository) UpdateBatch(ctx context.Context, records []entities.RechargeRecord) error {
return r.GetDB(ctx).Save(&records).Error
}
func (r *GormRechargeRecordRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.GetDB(ctx).Delete(&entities.RechargeRecord{}, "id IN ?", ids).Error
}
func (r *GormRechargeRecordRepository) WithTx(tx interface{}) interfaces.Repository[entities.RechargeRecord] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormRechargeRecordRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), RechargeRecordsTable),
}
}
return r
}
func (r *GormRechargeRecordRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.RechargeRecord{})
}
func (r *GormRechargeRecordRepository) SoftDelete(ctx context.Context, id string) error {
return r.SoftDeleteEntity(ctx, id, &entities.RechargeRecord{})
}
func (r *GormRechargeRecordRepository) Restore(ctx context.Context, id string) error {
return r.RestoreEntity(ctx, id, &entities.RechargeRecord{})
}
// GetTotalAmountByUserId 获取用户总充值金额(排除赠送)
func (r *GormRechargeRecordRepository) GetTotalAmountByUserId(ctx context.Context, userId string) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
Select("COALESCE(SUM(amount), 0)").
Where("user_id = ? AND status = ? AND recharge_type != ?", userId, entities.RechargeStatusSuccess, entities.RechargeTypeGift).
Scan(&total).Error
return total, err
}
// GetTotalAmountByUserIdAndDateRange 按用户ID和日期范围获取总充值金额排除赠送
func (r *GormRechargeRecordRepository) GetTotalAmountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
Select("COALESCE(SUM(amount), 0)").
Where("user_id = ? AND status = ? AND recharge_type != ? AND created_at >= ? AND created_at < ?", userId, entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate, endDate).
Scan(&total).Error
return total, err
}
// GetDailyStatsByUserId 获取用户每日充值统计(排除赠送)
func (r *GormRechargeRecordRepository) GetDailyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
// 构建SQL查询 - 使用PostgreSQL语法使用具体的日期范围
sql := `
SELECT
DATE(created_at) as date,
COALESCE(SUM(amount), 0) as amount
FROM recharge_records
WHERE user_id = $1
AND status = $2
AND recharge_type != $3
AND DATE(created_at) >= $4
AND DATE(created_at) <= $5
GROUP BY DATE(created_at)
ORDER BY date ASC
`
err := r.GetDB(ctx).Raw(sql, userId, entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetMonthlyStatsByUserId 获取用户每月充值统计(排除赠送)
func (r *GormRechargeRecordRepository) GetMonthlyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
// 构建SQL查询 - 使用PostgreSQL语法使用具体的日期范围
sql := `
SELECT
TO_CHAR(created_at, 'YYYY-MM') as month,
COALESCE(SUM(amount), 0) as amount
FROM recharge_records
WHERE user_id = $1
AND status = $2
AND recharge_type != $3
AND created_at >= $4
AND created_at <= $5
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
ORDER BY month ASC
`
err := r.GetDB(ctx).Raw(sql, userId, entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate, endDate).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetSystemTotalAmount 获取系统总充值金额(排除赠送)
func (r *GormRechargeRecordRepository) GetSystemTotalAmount(ctx context.Context) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
Where("status = ? AND recharge_type != ?", entities.RechargeStatusSuccess, entities.RechargeTypeGift).
Select("COALESCE(SUM(amount), 0)").
Scan(&total).Error
return total, err
}
// GetSystemAmountByDateRange 获取系统指定时间范围内的充值金额(排除赠送)
// endDate 应该是结束日期当天的次日00:00:00日统计或下个月1号00:00:00月统计使用 < 而不是 <=
func (r *GormRechargeRecordRepository) GetSystemAmountByDateRange(ctx context.Context, startDate, endDate time.Time) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.RechargeRecord{}).
Where("status = ? AND recharge_type != ? AND created_at >= ? AND created_at < ?", entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate, endDate).
Select("COALESCE(SUM(amount), 0)").
Scan(&total).Error
return total, err
}
// GetSystemDailyStats 获取系统每日充值统计(排除赠送)
// startDate 和 endDate 应该是时间对象endDate 应该是结束日期当天的次日00:00:00使用 < 而不是 <=
func (r *GormRechargeRecordRepository) GetSystemDailyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
sql := `
SELECT
DATE(created_at) as date,
COALESCE(SUM(amount), 0) as amount
FROM recharge_records
WHERE status = ?
AND recharge_type != ?
AND created_at >= ?
AND created_at < ?
GROUP BY DATE(created_at)
ORDER BY date ASC
`
err := r.GetDB(ctx).Raw(sql, entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate, endDate).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetSystemMonthlyStats 获取系统每月充值统计(排除赠送)
func (r *GormRechargeRecordRepository) GetSystemMonthlyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
sql := `
SELECT
TO_CHAR(created_at, 'YYYY-MM') as month,
COALESCE(SUM(amount), 0) as amount
FROM recharge_records
WHERE status = ?
AND recharge_type != ?
AND created_at >= ?
AND created_at < ?
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
ORDER BY month ASC
`
err := r.GetDB(ctx).Raw(sql, entities.RechargeStatusSuccess, entities.RechargeTypeGift, startDate, endDate).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// parseAmount 解析金额值支持string、int、int64类型转换为decimal.Decimal
func (r *GormRechargeRecordRepository) parseAmount(value interface{}) (decimal.Decimal, error) {
switch v := value.(type) {
case string:
if v == "" {
return decimal.Zero, fmt.Errorf("empty string")
}
return decimal.NewFromString(v)
case int:
return decimal.NewFromInt(int64(v)), nil
case int64:
return decimal.NewFromInt(v), nil
case float64:
return decimal.NewFromFloat(v), nil
case decimal.Decimal:
return v, nil
default:
return decimal.Zero, fmt.Errorf("unsupported type: %T", value)
}
}

View File

@@ -0,0 +1,348 @@
package repositories
import (
"context"
"errors"
"fmt"
"time"
"hyapi-server/internal/domains/finance/entities"
domain_finance_repo "hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
WalletsTable = "wallets"
)
type GormWalletRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.WalletRepository = (*GormWalletRepository)(nil)
func NewGormWalletRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.WalletRepository {
return &GormWalletRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, WalletsTable),
}
}
func (r *GormWalletRepository) Create(ctx context.Context, wallet entities.Wallet) (entities.Wallet, error) {
err := r.CreateEntity(ctx, &wallet)
return wallet, err
}
func (r *GormWalletRepository) GetByID(ctx context.Context, id string) (entities.Wallet, error) {
var wallet entities.Wallet
err := r.SmartGetByID(ctx, id, &wallet)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.Wallet{}, gorm.ErrRecordNotFound
}
return entities.Wallet{}, err
}
return wallet, nil
}
func (r *GormWalletRepository) Update(ctx context.Context, wallet entities.Wallet) error {
return r.UpdateEntity(ctx, &wallet)
}
func (r *GormWalletRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.Wallet{})
}
func (r *GormWalletRepository) SoftDelete(ctx context.Context, id string) error {
return r.SoftDeleteEntity(ctx, id, &entities.Wallet{})
}
func (r *GormWalletRepository) Restore(ctx context.Context, id string) error {
return r.RestoreEntity(ctx, id, &entities.Wallet{})
}
func (r *GormWalletRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
query := r.GetDB(ctx).Model(&entities.Wallet{})
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
if options.Search != "" {
query = query.Where("user_id LIKE ?", "%"+options.Search+"%")
}
return count, query.Count(&count).Error
}
func (r *GormWalletRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
func (r *GormWalletRepository) CreateBatch(ctx context.Context, wallets []entities.Wallet) error {
return r.GetDB(ctx).Create(&wallets).Error
}
func (r *GormWalletRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.Wallet, error) {
var wallets []entities.Wallet
err := r.GetDB(ctx).Where("id IN ?", ids).Order("created_at DESC").Find(&wallets).Error
return wallets, err
}
func (r *GormWalletRepository) UpdateBatch(ctx context.Context, wallets []entities.Wallet) error {
return r.GetDB(ctx).Save(&wallets).Error
}
func (r *GormWalletRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.GetDB(ctx).Delete(&entities.Wallet{}, "id IN ?", ids).Error
}
func (r *GormWalletRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.Wallet, error) {
var wallets []entities.Wallet
query := r.GetDB(ctx).Model(&entities.Wallet{})
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
if options.Search != "" {
query = query.Where("user_id LIKE ?", "%"+options.Search+"%")
}
if options.Sort != "" {
order := options.Sort
if options.Order == "desc" {
order += " DESC"
} else {
order += " ASC"
}
query = query.Order(order)
} else {
// 默认按创建时间倒序
query = query.Order("created_at DESC")
}
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
return wallets, query.Find(&wallets).Error
}
func (r *GormWalletRepository) WithTx(tx interface{}) interfaces.Repository[entities.Wallet] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormWalletRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), WalletsTable),
}
}
return r
}
func (r *GormWalletRepository) FindByUserID(ctx context.Context, userID string) (*entities.Wallet, error) {
var wallet entities.Wallet
err := r.GetDB(ctx).Where("user_id = ?", userID).First(&wallet).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &wallet, nil
}
func (r *GormWalletRepository) ExistsByUserID(ctx context.Context, userID string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("user_id = ?", userID).Count(&count).Error
return count > 0, err
}
func (r *GormWalletRepository) GetTotalBalance(ctx context.Context) (interface{}, error) {
var total decimal.Decimal
err := r.GetDB(ctx).Model(&entities.Wallet{}).Select("COALESCE(SUM(balance), 0)").Scan(&total).Error
return total, err
}
func (r *GormWalletRepository) GetActiveWalletCount(ctx context.Context) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("is_active = ?", true).Count(&count).Error
return count, err
}
// ================ 接口要求的方法 ================
func (r *GormWalletRepository) GetByUserID(ctx context.Context, userID string) (*entities.Wallet, error) {
var wallet entities.Wallet
err := r.GetDB(ctx).Where("user_id = ?", userID).First(&wallet).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &wallet, nil
}
// UpdateBalanceWithVersion 乐观锁自动重试最大重试maxRetry次
func (r *GormWalletRepository) UpdateBalanceWithVersion(ctx context.Context, walletID string, amount decimal.Decimal, operation string) (bool, error) {
maxRetry := 10
for i := 0; i < maxRetry; i++ {
// 每次重试都重新获取最新的钱包信息
var wallet entities.Wallet
err := r.GetDB(ctx).Where("id = ?", walletID).First(&wallet).Error
if err != nil {
return false, fmt.Errorf("获取钱包信息失败: %w", err)
}
// 重新计算新余额
var newBalance decimal.Decimal
switch operation {
case "add":
newBalance = wallet.Balance.Add(amount)
case "subtract":
newBalance = wallet.Balance.Sub(amount)
default:
return false, fmt.Errorf("不支持的操作类型: %s", operation)
}
// 乐观锁更新
result := r.GetDB(ctx).Model(&entities.Wallet{}).
Where("id = ? AND version = ?", walletID, wallet.Version).
Updates(map[string]interface{}{
"balance": newBalance.String(),
"version": wallet.Version + 1,
})
if result.Error != nil {
return false, fmt.Errorf("更新钱包余额失败: %w", result.Error)
}
if result.RowsAffected == 1 {
return true, nil
}
// 乐观锁冲突,继续重试
// 注意这里可以添加日志记录但需要确保logger可用
}
return false, fmt.Errorf("高并发下余额变动失败,已达到最大重试次数 %d", maxRetry)
}
// UpdateBalanceByUserID 乐观锁更新通过用户ID直接更新使用原生SQL
func (r *GormWalletRepository) UpdateBalanceByUserID(ctx context.Context, userID string, amount decimal.Decimal, operation string) (bool, error) {
maxRetry := 20 // 增加重试次数
baseDelay := 1 // 基础延迟毫秒
for i := 0; i < maxRetry; i++ {
// 每次重试都重新获取最新的钱包信息
var wallet entities.Wallet
err := r.GetDB(ctx).Where("user_id = ?", userID).First(&wallet).Error
if err != nil {
return false, fmt.Errorf("获取钱包信息失败: %w", err)
}
// 重新计算新余额
var newBalance decimal.Decimal
switch operation {
case "add":
newBalance = wallet.Balance.Add(amount)
case "subtract":
newBalance = wallet.Balance.Sub(amount)
default:
return false, fmt.Errorf("不支持的操作类型: %s", operation)
}
// 使用原生SQL进行乐观锁更新
newVersion := wallet.Version + 1
result := r.GetDB(ctx).Exec(`
UPDATE wallets
SET balance = ?, version = ?, updated_at = NOW()
WHERE user_id = ? AND version = ?
`, newBalance.String(), newVersion, userID, wallet.Version)
if result.Error != nil {
return false, fmt.Errorf("更新钱包余额失败: %w", result.Error)
}
if result.RowsAffected == 1 {
return true, nil
}
// 乐观锁冲突,添加指数退避延迟
if i < maxRetry-1 {
delay := baseDelay * (1 << i) // 指数退避: 1ms, 2ms, 4ms, 8ms...
if delay > 50 {
delay = 50 // 最大延迟50ms
}
time.Sleep(time.Duration(delay) * time.Millisecond)
}
}
return false, fmt.Errorf("高并发下余额变动失败,已达到最大重试次数 %d", maxRetry)
}
func (r *GormWalletRepository) UpdateBalance(ctx context.Context, walletID string, balance string) error {
return r.GetDB(ctx).Model(&entities.Wallet{}).Where("id = ?", walletID).Update("balance", balance).Error
}
func (r *GormWalletRepository) ActivateWallet(ctx context.Context, walletID string) error {
return r.GetDB(ctx).Model(&entities.Wallet{}).Where("id = ?", walletID).Update("is_active", true).Error
}
func (r *GormWalletRepository) DeactivateWallet(ctx context.Context, walletID string) error {
return r.GetDB(ctx).Model(&entities.Wallet{}).Where("id = ?", walletID).Update("is_active", false).Error
}
func (r *GormWalletRepository) GetStats(ctx context.Context) (*domain_finance_repo.FinanceStats, error) {
var stats domain_finance_repo.FinanceStats
// 总钱包数
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Count(&stats.TotalWallets).Error; err != nil {
return nil, err
}
// 激活钱包数
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("is_active = ?", true).Count(&stats.ActiveWallets).Error; err != nil {
return nil, err
}
// 总余额
var totalBalance decimal.Decimal
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Select("COALESCE(SUM(balance), 0)").Scan(&totalBalance).Error; err != nil {
return nil, err
}
stats.TotalBalance = totalBalance.String()
// 今日交易数(这里需要根据实际业务逻辑实现)
stats.TodayTransactions = 0
return &stats, nil
}
func (r *GormWalletRepository) GetUserWalletStats(ctx context.Context, userID string) (*domain_finance_repo.FinanceStats, error) {
var stats domain_finance_repo.FinanceStats
// 用户钱包数
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("user_id = ?", userID).Count(&stats.TotalWallets).Error; err != nil {
return nil, err
}
// 用户激活钱包数
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("user_id = ? AND is_active = ?", userID, true).Count(&stats.ActiveWallets).Error; err != nil {
return nil, err
}
// 用户总余额
var totalBalance decimal.Decimal
if err := r.GetDB(ctx).Model(&entities.Wallet{}).Where("user_id = ?", userID).Select("COALESCE(SUM(balance), 0)").Scan(&totalBalance).Error; err != nil {
return nil, err
}
stats.TotalBalance = totalBalance.String()
// 用户今日交易数(这里需要根据实际业务逻辑实现)
stats.TodayTransactions = 0
return &stats, nil
}

View File

@@ -0,0 +1,643 @@
package repositories
import (
"context"
"strings"
"time"
"hyapi-server/internal/domains/finance/entities"
domain_finance_repo "hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
// WalletTransactionWithProduct 包含产品名称的钱包交易记录
type WalletTransactionWithProduct struct {
entities.WalletTransaction
ProductName string `json:"product_name" gorm:"column:product_name"`
}
const (
WalletTransactionsTable = "wallet_transactions"
)
type GormWalletTransactionRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.WalletTransactionRepository = (*GormWalletTransactionRepository)(nil)
func NewGormWalletTransactionRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.WalletTransactionRepository {
return &GormWalletTransactionRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, WalletTransactionsTable),
}
}
func (r *GormWalletTransactionRepository) Create(ctx context.Context, transaction entities.WalletTransaction) (entities.WalletTransaction, error) {
err := r.CreateEntity(ctx, &transaction)
return transaction, err
}
func (r *GormWalletTransactionRepository) Update(ctx context.Context, transaction entities.WalletTransaction) error {
return r.UpdateEntity(ctx, &transaction)
}
func (r *GormWalletTransactionRepository) GetByID(ctx context.Context, id string) (entities.WalletTransaction, error) {
var transaction entities.WalletTransaction
err := r.SmartGetByID(ctx, id, &transaction)
return transaction, err
}
func (r *GormWalletTransactionRepository) GetByUserID(ctx context.Context, userID string, limit, offset int) ([]*entities.WalletTransaction, error) {
var transactions []*entities.WalletTransaction
options := database.CacheListOptions{
Where: "user_id = ?",
Args: []interface{}{userID},
Order: "created_at DESC",
Limit: limit,
Offset: offset,
}
err := r.ListWithCache(ctx, &transactions, 10*time.Minute, options)
return transactions, err
}
func (r *GormWalletTransactionRepository) GetByApiCallID(ctx context.Context, apiCallID string) (*entities.WalletTransaction, error) {
var transaction entities.WalletTransaction
err := r.FindOne(ctx, &transaction, "api_call_id = ?", apiCallID)
if err != nil {
return nil, err
}
return &transaction, nil
}
func (r *GormWalletTransactionRepository) ListByUserId(ctx context.Context, userId string, options interfaces.ListOptions) ([]*entities.WalletTransaction, int64, error) {
var transactions []*entities.WalletTransaction
var total int64
// 构建查询条件
whereCondition := "user_id = ?"
whereArgs := []interface{}{userId}
// 获取总数
count, err := r.CountWhere(ctx, &entities.WalletTransaction{}, whereCondition, whereArgs...)
if err != nil {
return nil, 0, err
}
total = count
// 使用基础仓储的分页查询方法
err = r.ListWithOptions(ctx, &entities.WalletTransaction{}, &transactions, options)
return transactions, total, err
}
func (r *GormWalletTransactionRepository) ListByUserIdWithFilters(ctx context.Context, userId string, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.WalletTransaction, int64, error) {
var transactions []*entities.WalletTransaction
var total int64
// 构建基础查询条件
whereCondition := "user_id = ?"
whereArgs := []interface{}{userId}
// 应用筛选条件
if filters != nil {
// 时间范围筛选
if startTime, ok := filters["start_time"].(time.Time); ok {
whereCondition += " AND created_at >= ?"
whereArgs = append(whereArgs, startTime)
}
if endTime, ok := filters["end_time"].(time.Time); ok {
whereCondition += " AND created_at <= ?"
whereArgs = append(whereArgs, endTime)
}
// 关键词筛选支持transaction_id和product_name
if keyword, ok := filters["keyword"].(string); ok && keyword != "" {
whereCondition += " AND (transaction_id LIKE ? OR product_id IN (SELECT id FROM product WHERE name LIKE ?))"
whereArgs = append(whereArgs, "%"+keyword+"%", "%"+keyword+"%")
}
// API调用ID筛选
if apiCallId, ok := filters["api_call_id"].(string); ok && apiCallId != "" {
whereCondition += " AND api_call_id LIKE ?"
whereArgs = append(whereArgs, "%"+apiCallId+"%")
}
// 金额范围筛选
if minAmount, ok := filters["min_amount"].(string); ok && minAmount != "" {
whereCondition += " AND amount >= ?"
whereArgs = append(whereArgs, minAmount)
}
if maxAmount, ok := filters["max_amount"].(string); ok && maxAmount != "" {
whereCondition += " AND amount <= ?"
whereArgs = append(whereArgs, maxAmount)
}
}
// 获取总数
count, err := r.CountWhere(ctx, &entities.WalletTransaction{}, whereCondition, whereArgs...)
if err != nil {
return nil, 0, err
}
total = count
// 使用基础仓储的分页查询方法
err = r.ListWithOptions(ctx, &entities.WalletTransaction{}, &transactions, options)
return transactions, total, err
}
func (r *GormWalletTransactionRepository) CountByUserId(ctx context.Context, userId string) (int64, error) {
return r.CountWhere(ctx, &entities.WalletTransaction{}, "user_id = ?", userId)
}
// CountByUserIdAndDateRange 按用户ID和日期范围统计钱包交易次数
func (r *GormWalletTransactionRepository) CountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (int64, error) {
return r.CountWhere(ctx, &entities.WalletTransaction{}, "user_id = ? AND created_at >= ? AND created_at < ?", userId, startDate, endDate)
}
// GetTotalAmountByUserId 获取用户总消费金额
func (r *GormWalletTransactionRepository) GetTotalAmountByUserId(ctx context.Context, userId string) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
Select("COALESCE(SUM(amount), 0)").
Where("user_id = ?", userId).
Scan(&total).Error
return total, err
}
// GetTotalAmountByUserIdAndDateRange 按用户ID和日期范围获取总消费金额
func (r *GormWalletTransactionRepository) GetTotalAmountByUserIdAndDateRange(ctx context.Context, userId string, startDate, endDate time.Time) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
Select("COALESCE(SUM(amount), 0)").
Where("user_id = ? AND created_at >= ? AND created_at < ?", userId, startDate, endDate).
Scan(&total).Error
return total, err
}
// GetDailyStatsByUserId 获取用户每日消费统计
func (r *GormWalletTransactionRepository) GetDailyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
// 构建SQL查询 - 使用PostgreSQL语法使用具体的日期范围
sql := `
SELECT
DATE(created_at) as date,
COALESCE(SUM(amount), 0) as amount
FROM wallet_transactions
WHERE user_id = $1
AND DATE(created_at) >= $2
AND DATE(created_at) <= $3
GROUP BY DATE(created_at)
ORDER BY date ASC
`
err := r.GetDB(ctx).Raw(sql, userId, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetMonthlyStatsByUserId 获取用户每月消费统计
func (r *GormWalletTransactionRepository) GetMonthlyStatsByUserId(ctx context.Context, userId string, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
// 构建SQL查询 - 使用PostgreSQL语法使用具体的日期范围
sql := `
SELECT
TO_CHAR(created_at, 'YYYY-MM') as month,
COALESCE(SUM(amount), 0) as amount
FROM wallet_transactions
WHERE user_id = $1
AND created_at >= $2
AND created_at <= $3
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
ORDER BY month ASC
`
err := r.GetDB(ctx).Raw(sql, userId, startDate, endDate).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// 实现interfaces.Repository接口的其他方法
func (r *GormWalletTransactionRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.WalletTransaction{})
}
func (r *GormWalletTransactionRepository) Exists(ctx context.Context, id string) (bool, error) {
return r.ExistsEntity(ctx, id, &entities.WalletTransaction{})
}
func (r *GormWalletTransactionRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.WalletTransaction, error) {
var transactions []entities.WalletTransaction
err := r.ListWithOptions(ctx, &entities.WalletTransaction{}, &transactions, options)
return transactions, err
}
func (r *GormWalletTransactionRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
return r.CountWithOptions(ctx, &entities.WalletTransaction{}, options)
}
func (r *GormWalletTransactionRepository) CreateBatch(ctx context.Context, transactions []entities.WalletTransaction) error {
return r.CreateBatchEntity(ctx, &transactions)
}
func (r *GormWalletTransactionRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.WalletTransaction, error) {
var transactions []entities.WalletTransaction
err := r.GetEntitiesByIDs(ctx, ids, &transactions)
return transactions, err
}
func (r *GormWalletTransactionRepository) UpdateBatch(ctx context.Context, transactions []entities.WalletTransaction) error {
return r.UpdateBatchEntity(ctx, &transactions)
}
func (r *GormWalletTransactionRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.DeleteBatchEntity(ctx, ids, &entities.WalletTransaction{})
}
func (r *GormWalletTransactionRepository) WithTx(tx interface{}) interfaces.Repository[entities.WalletTransaction] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormWalletTransactionRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), WalletTransactionsTable),
}
}
return r
}
func (r *GormWalletTransactionRepository) SoftDelete(ctx context.Context, id string) error {
return r.SoftDeleteEntity(ctx, id, &entities.WalletTransaction{})
}
func (r *GormWalletTransactionRepository) Restore(ctx context.Context, id string) error {
return r.RestoreEntity(ctx, id, &entities.WalletTransaction{})
}
func (r *GormWalletTransactionRepository) ListByUserIdWithFiltersAndProductName(ctx context.Context, userId string, filters map[string]interface{}, options interfaces.ListOptions) (map[string]string, []*entities.WalletTransaction, int64, error) {
var transactionsWithProduct []*WalletTransactionWithProduct
var total int64
// 构建基础查询条件
whereCondition := "wt.user_id = ?"
whereArgs := []interface{}{userId}
// 应用筛选条件
if filters != nil {
// 时间范围筛选
if startTime, ok := filters["start_time"].(time.Time); ok {
whereCondition += " AND wt.created_at >= ?"
whereArgs = append(whereArgs, startTime)
}
if endTime, ok := filters["end_time"].(time.Time); ok {
whereCondition += " AND wt.created_at <= ?"
whereArgs = append(whereArgs, endTime)
}
// 交易ID筛选
if transactionId, ok := filters["transaction_id"].(string); ok && transactionId != "" {
whereCondition += " AND wt.transaction_id LIKE ?"
whereArgs = append(whereArgs, "%"+transactionId+"%")
}
// 产品名称筛选
if productName, ok := filters["product_name"].(string); ok && productName != "" {
whereCondition += " AND p.name LIKE ?"
whereArgs = append(whereArgs, "%"+productName+"%")
}
// 金额范围筛选
if minAmount, ok := filters["min_amount"].(string); ok && minAmount != "" {
whereCondition += " AND wt.amount >= ?"
whereArgs = append(whereArgs, minAmount)
}
if maxAmount, ok := filters["max_amount"].(string); ok && maxAmount != "" {
whereCondition += " AND wt.amount <= ?"
whereArgs = append(whereArgs, maxAmount)
}
}
// 构建JOIN查询
query := r.GetDB(ctx).Table("wallet_transactions wt").
Select("wt.*, p.name as product_name").
Joins("LEFT JOIN product p ON wt.product_id = p.id").
Where(whereCondition, whereArgs...)
// 获取总数
var count int64
err := query.Count(&count).Error
if err != nil {
return nil, nil, 0, err
}
total = count
// 应用排序和分页
if options.Sort != "" {
query = query.Order("wt." + options.Sort + " " + options.Order)
} else {
query = query.Order("wt.created_at DESC")
}
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
// 执行查询
err = query.Find(&transactionsWithProduct).Error
if err != nil {
return nil, nil, 0, err
}
// 转换为entities.WalletTransaction并构建产品名称映射
var transactions []*entities.WalletTransaction
productNameMap := make(map[string]string)
for _, t := range transactionsWithProduct {
transaction := t.WalletTransaction
transactions = append(transactions, &transaction)
// 构建产品ID到产品名称的映射
if t.ProductName != "" {
productNameMap[transaction.ProductID] = t.ProductName
}
}
return productNameMap, transactions, total, nil
}
// ListWithFiltersAndProductName 管理端:根据条件筛选所有钱包交易记录(包含产品名称)
func (r *GormWalletTransactionRepository) ListWithFiltersAndProductName(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (map[string]string, []*entities.WalletTransaction, int64, error) {
var transactionsWithProduct []*WalletTransactionWithProduct
var total int64
// 构建基础查询条件
whereCondition := "1=1"
whereArgs := []interface{}{}
// 应用筛选条件
if filters != nil {
// 用户ID筛选支持单个和多个
if userIds, ok := filters["user_ids"].(string); ok && userIds != "" {
// 多个用户ID逗号分隔
userIdsList := strings.Split(userIds, ",")
whereCondition += " AND wt.user_id IN ?"
whereArgs = append(whereArgs, userIdsList)
} else if userId, ok := filters["user_id"].(string); ok && userId != "" {
// 单个用户ID
whereCondition += " AND wt.user_id = ?"
whereArgs = append(whereArgs, userId)
}
// 产品ID筛选支持多个
if productIds, ok := filters["product_ids"].(string); ok && productIds != "" {
// 多个产品ID逗号分隔
productIdsList := strings.Split(productIds, ",")
whereCondition += " AND wt.product_id IN ?"
whereArgs = append(whereArgs, productIdsList)
}
// 时间范围筛选
if startTime, ok := filters["start_time"].(time.Time); ok {
whereCondition += " AND wt.created_at >= ?"
whereArgs = append(whereArgs, startTime)
}
if endTime, ok := filters["end_time"].(time.Time); ok {
whereCondition += " AND wt.created_at <= ?"
whereArgs = append(whereArgs, endTime)
}
// 交易ID筛选
if transactionId, ok := filters["transaction_id"].(string); ok && transactionId != "" {
whereCondition += " AND wt.transaction_id LIKE ?"
whereArgs = append(whereArgs, "%"+transactionId+"%")
}
// 产品名称筛选
if productName, ok := filters["product_name"].(string); ok && productName != "" {
whereCondition += " AND p.name LIKE ?"
whereArgs = append(whereArgs, "%"+productName+"%")
}
// 企业名称筛选
if companyName, ok := filters["company_name"].(string); ok && companyName != "" {
whereCondition += " AND ei.company_name LIKE ?"
whereArgs = append(whereArgs, "%"+companyName+"%")
}
// 金额范围筛选
if minAmount, ok := filters["min_amount"].(string); ok && minAmount != "" {
whereCondition += " AND wt.amount >= ?"
whereArgs = append(whereArgs, minAmount)
}
if maxAmount, ok := filters["max_amount"].(string); ok && maxAmount != "" {
whereCondition += " AND wt.amount <= ?"
whereArgs = append(whereArgs, maxAmount)
}
}
// 构建JOIN查询
// 需要JOIN product表获取产品名称JOIN users和enterprise_infos表获取企业名称
query := r.GetDB(ctx).Table("wallet_transactions wt").
Select("wt.*, p.name as product_name").
Joins("LEFT JOIN product p ON wt.product_id = p.id").
Joins("LEFT JOIN users u ON wt.user_id = u.id").
Joins("LEFT JOIN enterprise_infos ei ON u.id = ei.user_id").
Where(whereCondition, whereArgs...)
// 获取总数
var count int64
err := query.Count(&count).Error
if err != nil {
return nil, nil, 0, err
}
total = count
// 应用排序和分页
if options.Sort != "" {
query = query.Order("wt." + options.Sort + " " + options.Order)
} else {
query = query.Order("wt.created_at DESC")
}
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
// 执行查询
err = query.Find(&transactionsWithProduct).Error
if err != nil {
return nil, nil, 0, err
}
// 转换为entities.WalletTransaction并构建产品名称映射
var transactions []*entities.WalletTransaction
productNameMap := make(map[string]string)
for _, t := range transactionsWithProduct {
transaction := t.WalletTransaction
transactions = append(transactions, &transaction)
// 构建产品ID到产品名称的映射
if t.ProductName != "" {
productNameMap[transaction.ProductID] = t.ProductName
}
}
return productNameMap, transactions, total, nil
}
// ExportWithFiltersAndProductName 导出钱包交易记录(包含产品名称和企业信息)
func (r *GormWalletTransactionRepository) ExportWithFiltersAndProductName(ctx context.Context, filters map[string]interface{}) ([]*entities.WalletTransaction, error) {
var transactionsWithProduct []WalletTransactionWithProduct
// 构建查询
query := r.GetDB(ctx).Table("wallet_transactions wt").
Select("wt.*, p.name as product_name").
Joins("LEFT JOIN product p ON wt.product_id = p.id")
// 构建WHERE条件
var whereConditions []string
var whereArgs []interface{}
// 用户ID筛选
if userIds, ok := filters["user_ids"].(string); ok && userIds != "" {
whereConditions = append(whereConditions, "wt.user_id IN (?)")
whereArgs = append(whereArgs, strings.Split(userIds, ","))
} else if userId, ok := filters["user_id"].(string); ok && userId != "" {
whereConditions = append(whereConditions, "wt.user_id = ?")
whereArgs = append(whereArgs, userId)
}
// 时间范围筛选
if startTime, ok := filters["start_time"].(time.Time); ok {
whereConditions = append(whereConditions, "wt.created_at >= ?")
whereArgs = append(whereArgs, startTime)
}
if endTime, ok := filters["end_time"].(time.Time); ok {
whereConditions = append(whereConditions, "wt.created_at <= ?")
whereArgs = append(whereArgs, endTime)
}
// 交易ID筛选
if transactionId, ok := filters["transaction_id"].(string); ok && transactionId != "" {
whereConditions = append(whereConditions, "wt.transaction_id LIKE ?")
whereArgs = append(whereArgs, "%"+transactionId+"%")
}
// 产品名称筛选
if productName, ok := filters["product_name"].(string); ok && productName != "" {
whereConditions = append(whereConditions, "p.name LIKE ?")
whereArgs = append(whereArgs, "%"+productName+"%")
}
// 产品ID列表筛选
if productIds, ok := filters["product_ids"].(string); ok && productIds != "" {
whereConditions = append(whereConditions, "wt.product_id IN (?)")
whereArgs = append(whereArgs, strings.Split(productIds, ","))
}
// 金额范围筛选
if minAmount, ok := filters["min_amount"].(string); ok && minAmount != "" {
whereConditions = append(whereConditions, "wt.amount >= ?")
whereArgs = append(whereArgs, minAmount)
}
if maxAmount, ok := filters["max_amount"].(string); ok && maxAmount != "" {
whereConditions = append(whereConditions, "wt.amount <= ?")
whereArgs = append(whereArgs, maxAmount)
}
// 应用WHERE条件
if len(whereConditions) > 0 {
query = query.Where(strings.Join(whereConditions, " AND "), whereArgs...)
}
// 排序
query = query.Order("wt.created_at DESC")
// 执行查询
err := query.Find(&transactionsWithProduct).Error
if err != nil {
return nil, err
}
// 转换为entities.WalletTransaction
var transactions []*entities.WalletTransaction
for _, t := range transactionsWithProduct {
transaction := t.WalletTransaction
transactions = append(transactions, &transaction)
}
return transactions, nil
}
// GetSystemTotalAmount 获取系统总消费金额
func (r *GormWalletTransactionRepository) GetSystemTotalAmount(ctx context.Context) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
Select("COALESCE(SUM(amount), 0)").
Scan(&total).Error
return total, err
}
// GetSystemAmountByDateRange 获取系统指定时间范围内的消费金额
// endDate 应该是结束日期当天的次日00:00:00日统计或下个月1号00:00:00月统计使用 < 而不是 <=
func (r *GormWalletTransactionRepository) GetSystemAmountByDateRange(ctx context.Context, startDate, endDate time.Time) (float64, error) {
var total float64
err := r.GetDB(ctx).Model(&entities.WalletTransaction{}).
Where("created_at >= ? AND created_at < ?", startDate, endDate).
Select("COALESCE(SUM(amount), 0)").
Scan(&total).Error
return total, err
}
// GetSystemDailyStats 获取系统每日消费统计
func (r *GormWalletTransactionRepository) GetSystemDailyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
sql := `
SELECT
DATE(created_at) as date,
COALESCE(SUM(amount), 0) as amount
FROM wallet_transactions
WHERE DATE(created_at) >= ?
AND DATE(created_at) <= ?
GROUP BY DATE(created_at)
ORDER BY date ASC
`
err := r.GetDB(ctx).Raw(sql, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}
// GetSystemMonthlyStats 获取系统每月消费统计
func (r *GormWalletTransactionRepository) GetSystemMonthlyStats(ctx context.Context, startDate, endDate time.Time) ([]map[string]interface{}, error) {
var results []map[string]interface{}
sql := `
SELECT
TO_CHAR(created_at, 'YYYY-MM') as month,
COALESCE(SUM(amount), 0) as amount
FROM wallet_transactions
WHERE created_at >= ?
AND created_at < ?
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
ORDER BY month ASC
`
err := r.GetDB(ctx).Raw(sql, startDate, endDate).Scan(&results).Error
if err != nil {
return nil, err
}
return results, nil
}

View File

@@ -0,0 +1,93 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/finance/entities"
domain_finance_repo "hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
WechatOrdersTable = "typay_orders"
)
type GormWechatOrderRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ domain_finance_repo.WechatOrderRepository = (*GormWechatOrderRepository)(nil)
func NewGormWechatOrderRepository(db *gorm.DB, logger *zap.Logger) domain_finance_repo.WechatOrderRepository {
return &GormWechatOrderRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, WechatOrdersTable),
}
}
func (r *GormWechatOrderRepository) Create(ctx context.Context, order entities.WechatOrder) (entities.WechatOrder, error) {
err := r.CreateEntity(ctx, &order)
return order, err
}
func (r *GormWechatOrderRepository) GetByID(ctx context.Context, id string) (entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.SmartGetByID(ctx, id, &order)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.WechatOrder{}, gorm.ErrRecordNotFound
}
return entities.WechatOrder{}, err
}
return order, nil
}
func (r *GormWechatOrderRepository) GetByOutTradeNo(ctx context.Context, outTradeNo string) (*entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.GetDB(ctx).Where("out_trade_no = ?", outTradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormWechatOrderRepository) GetByRechargeID(ctx context.Context, rechargeID string) (*entities.WechatOrder, error) {
var order entities.WechatOrder
err := r.GetDB(ctx).Where("recharge_id = ?", rechargeID).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &order, nil
}
func (r *GormWechatOrderRepository) GetByUserID(ctx context.Context, userID string) ([]entities.WechatOrder, error) {
var orders []entities.WechatOrder
// 需要通过充值记录关联查询,这里简化处理
err := r.GetDB(ctx).Find(&orders).Error
return orders, err
}
func (r *GormWechatOrderRepository) Update(ctx context.Context, order entities.WechatOrder) error {
return r.UpdateEntity(ctx, &order)
}
func (r *GormWechatOrderRepository) UpdateStatus(ctx context.Context, id string, status entities.WechatOrderStatus) error {
return r.GetDB(ctx).Model(&entities.WechatOrder{}).Where("id = ?", id).Update("status", status).Error
}
func (r *GormWechatOrderRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.WechatOrder{})
}
func (r *GormWechatOrderRepository) Exists(ctx context.Context, id string) (bool, error) {
return r.ExistsEntity(ctx, id, &entities.WechatOrder{})
}

View File

@@ -0,0 +1,342 @@
package repositories
import (
"context"
"fmt"
"time"
"hyapi-server/internal/domains/finance/entities"
"hyapi-server/internal/domains/finance/repositories"
"hyapi-server/internal/domains/finance/value_objects"
"gorm.io/gorm"
)
// GormInvoiceApplicationRepository 发票申请仓储的GORM实现
type GormInvoiceApplicationRepository struct {
db *gorm.DB
}
// NewGormInvoiceApplicationRepository 创建发票申请仓储
func NewGormInvoiceApplicationRepository(db *gorm.DB) repositories.InvoiceApplicationRepository {
return &GormInvoiceApplicationRepository{
db: db,
}
}
// Create 创建发票申请
func (r *GormInvoiceApplicationRepository) Create(ctx context.Context, application *entities.InvoiceApplication) error {
return r.db.WithContext(ctx).Create(application).Error
}
// Update 更新发票申请
func (r *GormInvoiceApplicationRepository) Update(ctx context.Context, application *entities.InvoiceApplication) error {
return r.db.WithContext(ctx).Save(application).Error
}
// Save 保存发票申请
func (r *GormInvoiceApplicationRepository) Save(ctx context.Context, application *entities.InvoiceApplication) error {
return r.db.WithContext(ctx).Save(application).Error
}
// FindByID 根据ID查找发票申请
func (r *GormInvoiceApplicationRepository) FindByID(ctx context.Context, id string) (*entities.InvoiceApplication, error) {
var application entities.InvoiceApplication
err := r.db.WithContext(ctx).Where("id = ?", id).First(&application).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &application, nil
}
// FindByUserID 根据用户ID查找发票申请列表
func (r *GormInvoiceApplicationRepository) FindByUserID(ctx context.Context, userID string, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
// 获取总数
err := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).Where("user_id = ?", userID).Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = r.db.WithContext(ctx).Where("user_id = ?", userID).
Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindPendingApplications 查找待处理的发票申请
func (r *GormInvoiceApplicationRepository) FindPendingApplications(ctx context.Context, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
// 获取总数
err := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).
Where("status = ?", entities.ApplicationStatusPending).
Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = r.db.WithContext(ctx).
Where("status = ?", entities.ApplicationStatusPending).
Order("created_at ASC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindByUserIDAndStatus 根据用户ID和状态查找发票申请
func (r *GormInvoiceApplicationRepository) FindByUserIDAndStatus(ctx context.Context, userID string, status entities.ApplicationStatus, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).Where("user_id = ?", userID)
if status != "" {
query = query.Where("status = ?", status)
}
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindByUserIDAndStatusWithTimeRange 根据用户ID、状态和时间范围查找发票申请列表
func (r *GormInvoiceApplicationRepository) FindByUserIDAndStatusWithTimeRange(ctx context.Context, userID string, status entities.ApplicationStatus, startTime, endTime *time.Time, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).Where("user_id = ?", userID)
// 添加状态筛选
if status != "" {
query = query.Where("status = ?", status)
}
// 添加时间范围筛选
if startTime != nil {
query = query.Where("created_at >= ?", startTime)
}
if endTime != nil {
query = query.Where("created_at <= ?", endTime)
}
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindByStatus 根据状态查找发票申请
func (r *GormInvoiceApplicationRepository) FindByStatus(ctx context.Context, status entities.ApplicationStatus) ([]*entities.InvoiceApplication, error) {
var applications []*entities.InvoiceApplication
err := r.db.WithContext(ctx).
Where("status = ?", status).
Order("created_at DESC").
Find(&applications).Error
return applications, err
}
// GetUserInvoiceInfo 获取用户发票信息
// GetUserTotalInvoicedAmount 获取用户已开票总金额
func (r *GormInvoiceApplicationRepository) GetUserTotalInvoicedAmount(ctx context.Context, userID string) (string, error) {
var total string
err := r.db.WithContext(ctx).
Model(&entities.InvoiceApplication{}).
Select("COALESCE(SUM(CAST(amount AS DECIMAL(10,2))), '0')").
Where("user_id = ? AND status = ?", userID, entities.ApplicationStatusCompleted).
Scan(&total).Error
return total, err
}
// GetUserTotalAppliedAmount 获取用户申请开票总金额
func (r *GormInvoiceApplicationRepository) GetUserTotalAppliedAmount(ctx context.Context, userID string) (string, error) {
var total string
err := r.db.WithContext(ctx).
Model(&entities.InvoiceApplication{}).
Select("COALESCE(SUM(CAST(amount AS DECIMAL(10,2))), '0')").
Where("user_id = ?", userID).
Scan(&total).Error
return total, err
}
// FindByUserIDAndInvoiceType 根据用户ID和发票类型查找申请
func (r *GormInvoiceApplicationRepository) FindByUserIDAndInvoiceType(ctx context.Context, userID string, invoiceType value_objects.InvoiceType, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).Where("user_id = ? AND invoice_type = ?", userID, invoiceType)
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindByDateRange 根据日期范围查找申请
func (r *GormInvoiceApplicationRepository) FindByDateRange(ctx context.Context, startDate, endDate string, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{})
if startDate != "" {
query = query.Where("DATE(created_at) >= ?", startDate)
}
if endDate != "" {
query = query.Where("DATE(created_at) <= ?", endDate)
}
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// SearchApplications 搜索发票申请
func (r *GormInvoiceApplicationRepository) SearchApplications(ctx context.Context, keyword string, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).
Where("company_name LIKE ? OR email LIKE ? OR tax_number LIKE ?",
fmt.Sprintf("%%%s%%", keyword),
fmt.Sprintf("%%%s%%", keyword),
fmt.Sprintf("%%%s%%", keyword))
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindByStatusWithTimeRange 根据状态和时间范围查找发票申请
func (r *GormInvoiceApplicationRepository) FindByStatusWithTimeRange(ctx context.Context, status entities.ApplicationStatus, startTime, endTime *time.Time, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{}).Where("status = ?", status)
// 添加时间范围筛选
if startTime != nil {
query = query.Where("created_at >= ?", startTime)
}
if endTime != nil {
query = query.Where("created_at <= ?", endTime)
}
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}
// FindAllWithTimeRange 根据时间范围查找所有发票申请
func (r *GormInvoiceApplicationRepository) FindAllWithTimeRange(ctx context.Context, startTime, endTime *time.Time, page, pageSize int) ([]*entities.InvoiceApplication, int64, error) {
var applications []*entities.InvoiceApplication
var total int64
query := r.db.WithContext(ctx).Model(&entities.InvoiceApplication{})
// 添加时间范围筛选
if startTime != nil {
query = query.Where("created_at >= ?", startTime)
}
if endTime != nil {
query = query.Where("created_at <= ?", endTime)
}
// 获取总数
err := query.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 获取分页数据
offset := (page - 1) * pageSize
err = query.Order("created_at DESC").
Offset(offset).
Limit(pageSize).
Find(&applications).Error
return applications, total, err
}

View File

@@ -0,0 +1,74 @@
package repositories
import (
"context"
"hyapi-server/internal/domains/finance/entities"
"hyapi-server/internal/domains/finance/repositories"
"gorm.io/gorm"
)
// GormUserInvoiceInfoRepository 用户开票信息仓储的GORM实现
type GormUserInvoiceInfoRepository struct {
db *gorm.DB
}
// NewGormUserInvoiceInfoRepository 创建用户开票信息仓储
func NewGormUserInvoiceInfoRepository(db *gorm.DB) repositories.UserInvoiceInfoRepository {
return &GormUserInvoiceInfoRepository{
db: db,
}
}
// Create 创建用户开票信息
func (r *GormUserInvoiceInfoRepository) Create(ctx context.Context, info *entities.UserInvoiceInfo) error {
return r.db.WithContext(ctx).Create(info).Error
}
// Update 更新用户开票信息
func (r *GormUserInvoiceInfoRepository) Update(ctx context.Context, info *entities.UserInvoiceInfo) error {
return r.db.WithContext(ctx).Save(info).Error
}
// Save 保存用户开票信息(创建或更新)
func (r *GormUserInvoiceInfoRepository) Save(ctx context.Context, info *entities.UserInvoiceInfo) error {
return r.db.WithContext(ctx).Save(info).Error
}
// FindByUserID 根据用户ID查找开票信息
func (r *GormUserInvoiceInfoRepository) FindByUserID(ctx context.Context, userID string) (*entities.UserInvoiceInfo, error) {
var info entities.UserInvoiceInfo
err := r.db.WithContext(ctx).Where("user_id = ?", userID).First(&info).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &info, nil
}
// FindByID 根据ID查找开票信息
func (r *GormUserInvoiceInfoRepository) FindByID(ctx context.Context, id string) (*entities.UserInvoiceInfo, error) {
var info entities.UserInvoiceInfo
err := r.db.WithContext(ctx).Where("id = ?", id).First(&info).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &info, nil
}
// Delete 删除用户开票信息
func (r *GormUserInvoiceInfoRepository) Delete(ctx context.Context, userID string) error {
return r.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.UserInvoiceInfo{}).Error
}
// Exists 检查用户开票信息是否存在
func (r *GormUserInvoiceInfoRepository) Exists(ctx context.Context, userID string) (bool, error) {
var count int64
err := r.db.WithContext(ctx).Model(&entities.UserInvoiceInfo{}).Where("user_id = ?", userID).Count(&count).Error
return count > 0, err
}