package services import ( "context" "database/sql" "encoding/json" "fmt" bill2 "platform/web/domains/bill" resource2 "platform/web/domains/resource" trade2 "platform/web/domains/trade" g "platform/web/globals" "platform/web/globals/orm" m "platform/web/models" q "platform/web/queries" "strings" "time" "github.com/gofiber/fiber/v2" ) var Resource = &resourceService{} type resourceService struct{} func (s *resourceService) PrepareResource(ctx context.Context, data *CreateResourceData, uid int32, method trade2.Method) (*TransactionPrepareResult, error) { amount := data.GetPrice() // 保存到数据库 var result *TransactionPrepareResult err := q.Q.Transaction(func(q *q.Query) error { var err error // 生成交易订单 result, err = Transaction.PrepareTransaction(ctx, q, uid, &TransactionPrepareData{ Subject: "购买套餐 - " + data.GetName(), Amount: amount, ExpireAt: time.Now().Add(30 * time.Minute), Type: trade2.TypeRecharge, Method: method, }) if err != nil { return err } // 保存请求缓存 reqStr, err := json.Marshal(&CreateResourceCache{ CreateResourceData: *data, Uid: uid, TradeId: result.Trade.ID, BillId: result.Bill.ID, }) if err != nil { return err } err = g.Redis.Set(ctx, result.TradeNo, reqStr, 30*time.Minute).Err() if err != nil { return err } return nil }) if err != nil { return nil, err } return result, nil } func (s *resourceService) CompleteResource(ctx context.Context, tradeNo string, rs *TransactionVerifyResult) error { // 获取请求缓存 reqStr, err := g.Redis.Get(ctx, tradeNo).Result() if err != nil { return err } cache := new(CreateResourceCache) if err := json.Unmarshal([]byte(reqStr), cache); err != nil { return err } // 保存交易信息 err = q.Q.Transaction(func(q *q.Query) error { // 保存套餐 resource, err := createResource(q, &cache.CreateResourceData, cache.Uid) if err != nil { return err } // 更新账单 _, err = q.Bill.Debug(). Select(q.Bill.ResourceID). Updates(&m.Bill{ ID: cache.BillId, ResourceID: resource.ID, }) if err != nil { return err } // 完成交易 _, err = Transaction.CompleteTransaction(ctx, q, &TransactionCompleteData{ TradeNo: tradeNo, TransactionVerifyResult: *rs, }) if err != nil { return err } // 删除缓存 err = g.Redis.Del(ctx, tradeNo).Err() if err != nil { return err } return nil }) if err != nil { return err } return nil } func (s *resourceService) CreateResource(data *CreateResourceData, uid int32) error { // 保存交易信息 err := q.Q.Transaction(func(q *q.Query) error { amount := data.GetPrice() // 检查用户 user, err := q.User. Where(q.User.ID.Eq(uid)). Take() if err != nil { return err } // 检查余额 if user.Balance < amount { return fiber.NewError(fiber.StatusBadRequest, "余额不足") } // 保存套餐 resource, err := createResource(q, data, uid) if err != nil { return err } // 生成账单 bill := m.Bill{ UserID: uid, ResourceID: resource.ID, BillNo: ID.GenReadable("bil"), Info: "购买套餐 - " + data.GetName(), Type: int32(bill2.TypeConsume), Amount: data.GetPrice(), } err = q.Bill. Omit(q.Bill.TradeID, q.Bill.RefundID). Create(&bill) if err != nil { return err } // 更新用户余额 _, err = q.User. Where(q.User.ID.Eq(uid)). UpdateSimple(q.User.Balance.Sub(amount)) if err != nil { return err } return nil }, &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) if err != nil { return err } return nil } type CreateResourceData struct { Type int32 `json:"type" validate:"required"` Live int32 `json:"live" validate:"required"` Expire int32 `json:"expire" validate:"required"` Quota int32 `json:"quota" validate:"required"` DailyLimit int32 `json:"daily_limit" validate:"required"` name string price float64 } func (data *CreateResourceData) GetName() string { if data.name == "" { sb := strings.Builder{} sb.WriteString("短效动态") switch data.Type { case 1: sb.WriteString("包时 ") case 2: sb.WriteString("包量 ") } sb.WriteString(fmt.Sprintf("%d 分钟", data.Live/60)) data.name = sb.String() } return data.name } func (data *CreateResourceData) GetPrice() float64 { if data.price == 0 { var count int switch data.Type { case 1: count = int(data.DailyLimit) case 2: count = int(data.Quota) } seconds := int(data.Live) if seconds == 180 { seconds = 150 } times := int(data.Expire) if data.Type == 2 { times = 1 } data.price = float64(count*seconds*times) / 30000 } return data.price } type CreateResourceCache struct { CreateResourceData `json:"data"` Uid int32 `json:"uid"` TradeId int32 `json:"trade_id"` BillId int32 `json:"bill_id"` } func createResource(q *q.Query, data *CreateResourceData, uid int32) (*m.Resource, error) { // 创建套餐 resource := m.Resource{ UserID: uid, ResourceNo: ID.GenReadable("res"), Active: true, Type: int32(resource2.TypeShort), Short: &m.ResourceShort{ Type: data.Type, Live: data.Live, Quota: data.Quota, Expire: orm.LocalDateTime(time.Now().Add(time.Duration(data.Expire) * 24 * time.Hour)), DailyLimit: data.DailyLimit, }, } err := q.Resource.Create(&resource) if err != nil { return nil, err } return &resource, nil } func (s *resourceService) CancelResource(ctx context.Context, tradeNo string, at time.Time, method trade2.Method) error { // 删除请求缓存 _, err := g.Redis.Del(ctx, tradeNo).Result() if err != nil { return err } // 取消交易 err = Transaction.RevokeTransaction(ctx, tradeNo, method) if err != nil { return err } // 更新订单状态 err = Transaction.FinishTransaction(ctx, q.Q, tradeNo, at) if err != nil { return err } return nil }