electricity_bill_calc_service/service/charge.go

263 lines
7.4 KiB
Go

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: &currentTime})
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: &currentTime})
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: &currentTime})
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("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, _ := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 {
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) {
if cachedValid, _ := cache.RetreiveSearch[time.Time]("last_valid_charge", uid); cachedValid != nil {
return *cachedValid, nil
}
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)
cache.CacheSearch(lastValid, []string{"charge"}, "last_valid_charge", uid)
return lastValid, nil
}