package service import ( "electricity_bill_calc/cache" "electricity_bill_calc/config" "electricity_bill_calc/exceptions" "electricity_bill_calc/global" "electricity_bill_calc/model" "fmt" "strconv" "time" "github.com/fufuok/utils" "github.com/samber/lo" "xorm.io/builder" "xorm.io/xorm" ) type _ChargeService struct{} var ChargeService _ChargeService func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithIgnoreSettle bool) error { tx := global.DBConn.NewSession() defer tx.Close() if err := tx.Begin(); err != nil { return err } charge.Seq = 0 _, err := tx.Insert(charge) if err != nil { tx.Rollback() return err } if extendWithIgnoreSettle { err := c.updateUserExpiration(tx, charge.UserId, charge.ChargeTo) if err != nil { return err } } err = tx.Commit() if err != nil { tx.Rollback() return err } cache.AbolishRelation("charge") return nil } func (c _ChargeService) SettleCharge(seq int64, uid string) error { var record *model.UserCharge has, err := global.DBConn.Where(builder.Eq{"seq": seq, "user_id": uid}).NoAutoCondition().Get(record) if err != nil { return nil } if !has { 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}) if err != nil { tx.Rollback() return err } err = c.updateUserExpiration(tx, uid, record.ChargeTo) if err != nil { return err } err = tx.Commit() if err != nil { tx.Rollback() return err } cache.AbolishRelation("charge") 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 { 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}) if err != nil { tx.Rollback() return err } if rows == 0 { tx.Rollback() return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") } lastValidExpriation, err := c.lastValidChargeTo(uid) if err != nil { tx.Rollback() return exceptions.NewNotFoundError("未找到最后合法的计费时间。") } err = c.updateUserExpiration(tx, uid, lastValidExpriation) if err != nil { return err } err = tx.Commit() if err != nil { tx.Rollback() return err } cache.AbolishRelation("charge") 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 { 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}) if err != nil { tx.Rollback() return err } if rows == 0 { tx.Rollback() return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") } err = tx.Commit() if err != nil { tx.Rollback() return err } tx = global.DBConn.NewSession() defer tx.Close() if err := tx.Begin(); err != nil { return err } lastValidExpriation, err := c.lastValidChargeTo(uid) if err != nil { return exceptions.NewNotFoundError("未找到最后合法的计费时间。") } err = c.updateUserExpiration(tx, uid, lastValidExpriation) if err != nil { return err } err = tx.Commit() if err != nil { tx.Rollback() return err } cache.AbolishRelation("user") cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) cache.AbolishRelation("charge") 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}) if err != nil { tx.Rollback() } cache.AbolishRelation("user") 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() condition = make([]string, 0) ) condition = append(condition, strconv.Itoa(page)) if len(keyword) != 0 { cond = cond.And(builder.Like{"d.name", keyword}.Or(builder.Like{"d.abbr", keyword})) condition = append(condition, keyword) } if len(beginDate) != 0 { beginTime, err := time.ParseInLocation("2006-01-02", beginDate, time.Local) beginTime = utils.BeginOfDay(beginTime) if err != nil { return make([]model.ChargeWithName, 0), 0, err } cond = cond.And(builder.Gte{"c.created_at": beginTime}) condition = append(condition, strconv.FormatInt(beginTime.Unix(), 10)) } if len(endDate) != 0 { endTime, err := time.ParseInLocation("2006-01-02", endDate, time.Local) endTime = utils.EndOfDay(endTime) if err != nil { return make([]model.ChargeWithName, 0), 0, err } cond = cond.And(builder.Lte{"c.created_at": endTime}) condition = append(condition, strconv.FormatInt(endTime.Unix(), 10)) } startItem := (page - 1) * config.ServiceSettings.ItemsPageSize var ( total int64 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...) } 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 } func (_ChargeService) lastValidChargeTo(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) if err != nil { return veryBlankTime, nil } mappedRecords := lo.Map(records, func(elem string, index int) time.Time { t, _ := time.Parse(time.RFC3339, elem) return utils.BeginOfDay(t) }) lastValid := lo.Reduce(mappedRecords, func(acc, elem time.Time, index int) time.Time { if elem.After(acc) { return elem } else { return acc } }, veryBlankTime) return lastValid, nil }