From c6c14233644021f2af4b690bbbc5fcd434f69179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Fri, 16 Sep 2022 08:51:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor(charge):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E7=94=A8=E6=88=B7=E8=AE=A1=E8=B4=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=9A=84=E8=BF=81=E7=A7=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/charge.go | 195 ++++++++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 83 deletions(-) diff --git a/service/charge.go b/service/charge.go index 8c31265..33f8346 100644 --- a/service/charge.go +++ b/service/charge.go @@ -1,10 +1,13 @@ package service import ( + "context" + "database/sql" "electricity_bill_calc/cache" "electricity_bill_calc/config" "electricity_bill_calc/exceptions" "electricity_bill_calc/global" + "electricity_bill_calc/logger" "electricity_bill_calc/model" "fmt" "strconv" @@ -12,28 +15,32 @@ import ( "github.com/fufuok/utils" "github.com/samber/lo" - "xorm.io/builder" - "xorm.io/xorm" + "github.com/uptrace/bun" + "go.uber.org/zap" ) -type _ChargeService struct{} +type _ChargeService struct { + l *zap.Logger +} -var ChargeService _ChargeService +var ChargeService = _ChargeService{ + l: logger.Named("Service", "Charge"), +} func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithIgnoreSettle bool) error { - tx := global.DBConn.NewSession() - defer tx.Close() - if err := tx.Begin(); err != nil { + ctx, cancel := global.TimeoutContext() + defer cancel() + tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { return err } - charge.Seq = 0 - _, err := tx.Insert(charge) + _, err = tx.NewInsert().Model(charge).Exec(ctx) if err != nil { tx.Rollback() return err } if extendWithIgnoreSettle { - err := c.updateUserExpiration(tx, charge.UserId, charge.ChargeTo) + err := c.updateUserExpiration(&tx, ctx, charge.UserId, charge.ChargeTo) if err != nil { return err } @@ -48,26 +55,35 @@ func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithI } func (c _ChargeService) SettleCharge(seq int64, uid string) error { + ctx, cancel := global.TimeoutContext() + defer cancel() + tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { + return err + } var record *model.UserCharge - has, err := global.DBConn.Where(builder.Eq{"seq": seq, "user_id": uid}).NoAutoCondition().Get(record) + err = tx.NewSelect().Model(&record). + Where("seq = ?", seq). + Where("user_id = ?", uid). + Scan(ctx) if err != nil { return nil } - if !has { + if record == nil { return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") } - tx := global.DBConn.NewSession() - defer tx.Close() - if err := tx.Begin(); err != nil { - return err - } currentTime := time.Now() - _, err = tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("settled", "settled_at").Update(&model.UserCharge{Settled: true, SettledAt: ¤tTime}) + _, err = tx.NewUpdate().Model((*model.UserCharge)(nil)). + Where("seq = ?", seq). + Where("user_id = ?", uid). + Set("settled = ?", true). + Set("settled_at = ?", currentTime). + Exec(ctx) if err != nil { tx.Rollback() return err } - err = c.updateUserExpiration(tx, uid, record.ChargeTo) + err = c.updateUserExpiration(&tx, ctx, uid, record.ChargeTo) if err != nil { return err } @@ -76,33 +92,38 @@ func (c _ChargeService) SettleCharge(seq int64, uid string) error { tx.Rollback() return err } - cache.AbolishRelation("charge") - cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq)) + cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) return nil } func (c _ChargeService) RefundCharge(seq int64, uid string) error { - tx := global.DBConn.NewSession() - defer tx.Close() - if err := tx.Begin(); err != nil { + ctx, cancel := global.TimeoutContext() + defer cancel() + tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { return err } currentTime := time.Now() - rows, err := tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("refunded", "refunded_at").Update(&model.UserCharge{Refunded: true, RefundedAt: ¤tTime}) + res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)). + Where("seq = ?", seq). + Where("user_id = ?", uid). + Set("refunded = ?", true). + Set("refunded_at = ?", currentTime). + Exec(ctx) if err != nil { tx.Rollback() return err } - if rows == 0 { + if rows, _ := res.RowsAffected(); rows == 0 { tx.Rollback() return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") } - lastValidExpriation, err := c.lastValidChargeTo(uid) + lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid) if err != nil { tx.Rollback() return exceptions.NewNotFoundError("未找到最后合法的计费时间。") } - err = c.updateUserExpiration(tx, uid, lastValidExpriation) + err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation) if err != nil { return err } @@ -111,24 +132,29 @@ func (c _ChargeService) RefundCharge(seq int64, uid string) error { tx.Rollback() return err } - cache.AbolishRelation("charge") - cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq)) + cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) return nil } func (c _ChargeService) CancelCharge(seq int64, uid string) error { - tx := global.DBConn.NewSession() - defer tx.Close() - if err := tx.Begin(); err != nil { + ctx, cancel := global.TimeoutContext() + defer cancel() + tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { return err } currentTime := time.Now() - rows, err := tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("cancelled", "cancelled_at").Update(&model.UserCharge{Cancelled: true, CancelledAt: ¤tTime}) + res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)). + Where("seq = ?", seq). + Where("user_id = ?", uid). + Set("cancelled = ?", true). + Set("cancelled_at = ?", currentTime). + Exec(ctx) if err != nil { tx.Rollback() return err } - if rows == 0 { + if rows, _ := res.RowsAffected(); rows == 0 { tx.Rollback() return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") } @@ -137,16 +163,15 @@ func (c _ChargeService) CancelCharge(seq int64, uid string) error { tx.Rollback() return err } - tx = global.DBConn.NewSession() - defer tx.Close() - if err := tx.Begin(); err != nil { + tx, err = global.DB.BeginTx(ctx, &sql.TxOptions{}) + if err != nil { return err } - lastValidExpriation, err := c.lastValidChargeTo(uid) + lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid) if err != nil { return exceptions.NewNotFoundError("未找到最后合法的计费时间。") } - err = c.updateUserExpiration(tx, uid, lastValidExpriation) + err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation) if err != nil { return err } @@ -156,30 +181,38 @@ func (c _ChargeService) CancelCharge(seq int64, uid string) error { return err } cache.AbolishRelation("user") - cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) + cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) cache.AbolishRelation("charge") - cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq)) + cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) return nil } -func (_ChargeService) updateUserExpiration(tx *xorm.Session, uid string, expiration time.Time) error { - _, err := tx.ID(uid).Cols("service_expiration").Update(&model.UserDetail{ServiceExpiration: expiration}) +func (_ChargeService) updateUserExpiration(tx *bun.Tx, ctx context.Context, uid string, expiration time.Time) error { + _, err := tx.NewUpdate().Model((*model.UserDetail)(nil)). + Set("service_expiration = ?", expiration). + Where("id = ?", uid). + Exec(ctx) if err != nil { tx.Rollback() } - cache.AbolishRelation("user") - cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) + cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) return err } func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, page int) ([]model.ChargeWithName, int64, error) { var ( - cond = builder.NewCond() + cond = global.DB.NewSelect() condition = make([]string, 0) + charges = make([]model.UserCharge, 0) ) + cond = cond.Model(&charges).Relation("Detail") condition = append(condition, strconv.Itoa(page)) if len(keyword) != 0 { - cond = cond.And(builder.Like{"d.name", keyword}.Or(builder.Like{"d.abbr", keyword})) + keywordCond := "%" + keyword + "%" + cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { + return q.Where("d.mame like ?", keywordCond). + WhereOr("d.abbr like ?", keywordCond) + }) condition = append(condition, keyword) } if len(beginDate) != 0 { @@ -188,7 +221,7 @@ func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, if err != nil { return make([]model.ChargeWithName, 0), 0, err } - cond = cond.And(builder.Gte{"c.created_at": beginTime}) + cond = cond.Where("c.created_at >= ?", beginTime) condition = append(condition, strconv.FormatInt(beginTime.Unix(), 10)) } if len(endDate) != 0 { @@ -197,51 +230,47 @@ func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, if err != nil { return make([]model.ChargeWithName, 0), 0, err } - cond = cond.And(builder.Lte{"c.created_at": endTime}) + cond = cond.Where("c.created_at <= ?", endTime) condition = append(condition, strconv.FormatInt(endTime.Unix(), 10)) } + + if cachedTotal, err := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 && err == nil { + if cachedCharges, _ := cache.RetreiveSearch[[]model.ChargeWithName]("charge_with_name", condition...); cachedCharges != nil { + return *cachedCharges, cachedTotal, nil + } + } + startItem := (page - 1) * config.ServiceSettings.ItemsPageSize var ( - total int64 + total int err error ) - if cachedTotal, err := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 && err == nil { - total = cachedTotal - } else { - total, err = global.DBConn. - Alias("d"). - Join("INNER", []string{"user_charge", "c"}, "c.user_id=d.id"). - Where(cond). - NoAutoCondition(). - Count(&model.ChargeWithName{}) - if err != nil { - return nil, -1, err - } - cache.CacheCount([]string{"charge"}, "charge_with_name", total, condition...) + ctx, cancel := global.TimeoutContext() + defer cancel() + total, err = cond.Limit(config.ServiceSettings.ItemsPageSize).Offset(startItem).ScanAndCount(ctx) + + relations := []string{"charge"} + chargesWithName := make([]model.ChargeWithName, 0) + for _, c := range charges { + chargesWithName = append(chargesWithName, model.ChargeWithName{ + UserCharge: c, + UserDetail: *c.Detail, + }) + relations = append(relations, fmt.Sprintf("charge:%s:%d", c.UserId, c.Seq)) } - charges := make([]model.ChargeWithName, 0) - if cachedCharges, _ := cache.RetreiveSearch[[]model.ChargeWithName]("charge_with_name", condition...); cachedCharges != nil { - return *cachedCharges, total, nil - } - err = global.DBConn. - Alias("d"). - Join("INNER", []string{"user_charge", "c"}, "c.user_id=d.id"). - Where(cond). - Limit(config.ServiceSettings.ItemsPageSize, startItem). - NoAutoCondition(). - Find(&charges) - cache.CacheSearch(charges, []string{"charge"}, "charge_with_name", condition...) - return charges, total, err + + cache.CacheCount(relations, "charge_with_name", int64(total), condition...) + cache.CacheSearch(charges, relations, "charge_with_name", condition...) + return chargesWithName, int64(total), err } -func (_ChargeService) lastValidChargeTo(uid string) (time.Time, error) { +func (_ChargeService) lastValidChargeTo(tx *bun.Tx, ctx *context.Context, uid string) (time.Time, error) { veryBlankTime, _ := time.Parse("2006-01-02 15:04:05", "0001-01-01 00:00:00") var records []string - err := global.DBConn. - Table(&model.UserCharge{}). - Where(builder.Eq{"settled": true, "cancelled": false, "refunded": false, "user_id": uid}). - Cols("charge_to"). - Find(&records) + err := tx.NewSelect().Table("user_charge"). + Where("settled = ? and cancelled = ? and refunded = ? and user_id = ?", true, false, false, uid). + Column("charge_to"). + Scan(*ctx, &records) if err != nil { return veryBlankTime, nil }