feat: complete charge
This commit is contained in:
@@ -9,18 +9,22 @@ import (
|
||||
"backend/database/models/qvyun/public/table"
|
||||
"backend/pkg/consts"
|
||||
"backend/pkg/db"
|
||||
"backend/pkg/errorx"
|
||||
"backend/pkg/pg"
|
||||
"backend/providers/jwt"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"github.com/go-jet/jet/v2/qrm"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
hashids "github.com/speps/go-hashids/v2"
|
||||
)
|
||||
|
||||
// @provider:except
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
log *logrus.Entry `inject:"false"`
|
||||
db *sql.DB
|
||||
hashIds *hashids.HashID
|
||||
log *logrus.Entry `inject:"false"`
|
||||
}
|
||||
|
||||
func (svc *Service) Prepare() error {
|
||||
@@ -255,3 +259,123 @@ func (svc *Service) SetTenantExpireAtBySlug(ctx context.Context, slug string, ex
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) GenerateChargeCode(ctx context.Context, tenantID, chargeAmount int64) (string, error) {
|
||||
log := svc.log.WithField("method", "GenerateChargeCode")
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
code, err := svc.hashIds.EncodeInt64([]int64{tenantID, chargeAmount, timestamp})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to encode charge code")
|
||||
}
|
||||
log.Infof("generate charge code: %s", code)
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// Charge
|
||||
func (svc *Service) Charge(ctx context.Context, claim *jwt.Claims, code string) error {
|
||||
log := svc.log.WithField("method", "Charge")
|
||||
raw, err := svc.hashIds.DecodeInt64WithError(code)
|
||||
if err != nil {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
|
||||
if len(raw) != 3 {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
|
||||
tenantId, chargeAmount, timestamp := raw[0], raw[1], raw[2]
|
||||
if tenantId != claim.TenantID {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
generatedAt := time.Unix(timestamp, 0)
|
||||
log.Infof("charge code %s generated at: %s", code, generatedAt)
|
||||
|
||||
if chargeAmount <= 0 {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
|
||||
t := table.UserBalanceHistories
|
||||
st := t.SELECT(COUNT(t.ID).AS("cnt")).WHERE(t.Code.EQ(String(code)))
|
||||
log.Debug(st.DebugSql())
|
||||
|
||||
var result struct {
|
||||
Cnt int64
|
||||
}
|
||||
if err := st.QueryContext(ctx, db.FromContext(ctx, svc.db), &result); err != nil {
|
||||
return errors.Wrap(err, "failed to query charge code")
|
||||
}
|
||||
|
||||
if result.Cnt > 0 {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
|
||||
has, err := svc.TenantHasUser(ctx, claim.UserID, tenantId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to check user-tenant relation")
|
||||
}
|
||||
|
||||
if !has {
|
||||
return errorx.InvalidChargeCode
|
||||
}
|
||||
|
||||
log.Infof("charge tenant: %d, user: %d, amount: %d", claim.TenantID, claim.UserID, chargeAmount)
|
||||
|
||||
tx, err := svc.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to begin transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// update user balance in users_tenants
|
||||
tbl := table.UsersTenants
|
||||
stmt := tbl.
|
||||
UPDATE().
|
||||
SET(
|
||||
tbl.Balance.SET(
|
||||
tbl.Balance.ADD(Int64(chargeAmount)),
|
||||
),
|
||||
).
|
||||
WHERE(
|
||||
tbl.UserID.EQ(Int64(claim.UserID)).AND(
|
||||
tbl.TenantID.EQ(Int64(claim.TenantID)),
|
||||
),
|
||||
)
|
||||
log.Debug(stmt.DebugSql())
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, db.FromContext(ctx, svc.db)); err != nil {
|
||||
return errors.Wrap(err, "failed to charge user balance")
|
||||
}
|
||||
|
||||
// insert charge record
|
||||
chargeTbl := table.UserBalanceHistories
|
||||
chargeStmt := chargeTbl.
|
||||
INSERT(
|
||||
chargeTbl.UserID,
|
||||
chargeTbl.TenantID,
|
||||
chargeTbl.Balance,
|
||||
chargeTbl.Target,
|
||||
chargeTbl.Type,
|
||||
chargeTbl.Code,
|
||||
).
|
||||
VALUES(
|
||||
Int64(claim.UserID),
|
||||
Int64(claim.TenantID),
|
||||
Int64(chargeAmount),
|
||||
Json(pg.BalanceTarget{}.MustValue()),
|
||||
String(pg.BalanceTypeCharge.String()),
|
||||
String(code),
|
||||
)
|
||||
log.Debug(chargeStmt.DebugSql())
|
||||
|
||||
if _, err := chargeStmt.ExecContext(ctx, db.FromContext(ctx, svc.db)); err != nil {
|
||||
return errors.Wrap(err, "failed to insert charge record")
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, "failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user