From 85f4d04a7faad8b999c81deca37c352603a51a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Mon, 5 Jun 2023 21:53:05 +0800 Subject: [PATCH] =?UTF-8?q?refactor(types):=E5=B0=86=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=B1=BB=E5=9E=8B=E6=8F=90=E5=8F=96=E5=88=B0?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E7=9A=84=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=8C=85=E4=B8=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/charge.go | 5 +- logger/logger.go | 17 ++++-- main.go | 3 +- model/charge.go | 5 +- model/types.go | 100 ----------------------------------- model/user.go | 21 ++++---- repository/charge.go | 9 ++-- service/charge.go | 4 +- types/date.go | 123 +++++++++++++++++++++++++++++++++++++++++++ types/datetime.go | 118 +++++++++++++++++++++++++++++++++++++++++ vo/user.go | 6 ++- 11 files changed, 285 insertions(+), 126 deletions(-) delete mode 100644 model/types.go create mode 100644 types/date.go create mode 100644 types/datetime.go diff --git a/controller/charge.go b/controller/charge.go index 0a7d932..2425588 100644 --- a/controller/charge.go +++ b/controller/charge.go @@ -6,6 +6,7 @@ import ( "electricity_bill_calc/repository" "electricity_bill_calc/response" "electricity_bill_calc/service" + "electricity_bill_calc/types" "net/http" "github.com/gofiber/fiber/v2" @@ -26,12 +27,12 @@ func searchCharges(c *fiber.Ctx) error { result := response.NewResult(c) keyword := c.Query("keyword", "") page := c.QueryInt("page", 1) - beginTime, err := model.ParseDate(c.Query("begin")) + beginTime, err := types.ParseDate(c.Query("begin")) if err != nil { chargeLog.Error("无法解析查询起始时间。", zap.Error(err)) return result.Error(http.StatusInternalServerError, err.Error()) } - endTime, err := model.ParseDate(c.Query("end")) + endTime, err := types.ParseDate(c.Query("end")) if err != nil { chargeLog.Error("无法解析查询结束时间。", zap.Error(err)) return result.Error(http.StatusInternalServerError, err.Error()) diff --git a/logger/logger.go b/logger/logger.go index d5a9877..bf872da 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,7 +1,7 @@ package logger import ( - "electricity_bill_calc/model" + "electricity_bill_calc/types" "os" "github.com/shopspring/decimal" @@ -137,6 +137,17 @@ func DecimalField(key string, val *decimal.Decimal) zap.Field { return zap.String(key, val.String()) } -func DateField(key string, val *model.Date) zap.Field { - return zap.String(key, val.Format("2006-01-02")) +func NullDecimalField(key string, val *decimal.NullDecimal) zap.Field { + if val.Valid { + return DecimalField(key, &val.Decimal) + } + return zap.String(key, "null") +} + +func DateField(key string, val *types.Date) zap.Field { + return val.Log(key) +} + +func DateTimeField(key string, val *types.DateTime) zap.Field { + return val.Log(key) } diff --git a/main.go b/main.go index 3363cc3..ebe27c6 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "electricity_bill_calc/repository" "electricity_bill_calc/router" "electricity_bill_calc/service" + "electricity_bill_calc/types" "fmt" "time" @@ -54,7 +55,7 @@ func intializeSingularity() error { return nil } singularityId := "000" - singularityExpires, err := model.ParseDate("2099-12-31") + singularityExpires, err := types.ParseDate("2099-12-31") if err != nil { l.Error("奇点用户账号过期时间解析失败。", zap.Error(err)) return fmt.Errorf("奇点用户账号过期时间解析失败: %w", err) diff --git a/model/charge.go b/model/charge.go index d3d6599..27bdbbd 100644 --- a/model/charge.go +++ b/model/charge.go @@ -1,6 +1,7 @@ package model import ( + "electricity_bill_calc/types" "time" "github.com/shopspring/decimal" @@ -13,7 +14,7 @@ type UserChargeDetail struct { Fee *decimal.Decimal `json:"fee"` Discount *decimal.Decimal `json:"discount"` Amount *decimal.Decimal `json:"amount"` - ChargeTo Date `json:"charge_to"` + ChargeTo types.Date `json:"charge_to"` Settled bool `json:"settled"` SettledAt *time.Time `json:"settled_at"` Cancelled bool `json:"cancelled"` @@ -26,5 +27,5 @@ type ChargeRecordCreationForm struct { Fee *decimal.Decimal `json:"fee"` Discount *decimal.Decimal `json:"discount"` Amount *decimal.Decimal `json:"amount"` - ChargeTo Date `json:"chargeTo"` + ChargeTo types.Date `json:"chargeTo"` } diff --git a/model/types.go b/model/types.go deleted file mode 100644 index 7a74be8..0000000 --- a/model/types.go +++ /dev/null @@ -1,100 +0,0 @@ -package model - -import ( - "database/sql" - "database/sql/driver" - "encoding/json" - "fmt" - "time" - - et "electricity_bill_calc/tools/time" -) - -type Date struct { - time.Time -} - -func NewDate(t time.Time) Date { - t = t.In(et.Loc) - return Date{ - Time: time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, et.Loc), - } -} - -func SpecificDate(year int, month time.Month, date int) Date { - return NewDate(et.Time(year, month, date, 0, 0, 0, 0)) -} - -func NewEmptyDate() Date { - return Date{ - Time: time.Time{}.In(et.Loc), - } -} - -func ParseDate(t string) (Date, error) { - d, err := time.ParseInLocation("2006-01-02", t, et.Loc) - if err != nil { - return NewEmptyDate(), fmt.Errorf("unable to parse given time, %w", err) - } - return Date{ - Time: d, - }, nil -} - -func (d Date) IsEmpty() bool { - return d.Time.IsZero() -} - -func (d Date) Format(fmt string) string { - return d.Time.Format(fmt) -} - -func (d Date) ToString() string { - return d.Time.Format("2006-01-02") -} - -var _ driver.Valuer = (*Date)(nil) - -func (d Date) Value() (driver.Value, error) { - return d.In(et.Loc).Format("2006-01-02"), nil -} - -var _ sql.Scanner = (*Date)(nil) - -// Scan scans the time parsing it if necessary using timeFormat. -func (d *Date) Scan(src interface{}) (err error) { - switch src := src.(type) { - case time.Time: - *d = NewDate(src) - return nil - case string: - d.Time, err = time.ParseInLocation("2006-01-02", src, et.Loc) - return err - case []byte: - d.Time, err = time.ParseInLocation("2006-01-02", string(src), et.Loc) - return err - case nil: - d.Time = time.Time{} - return nil - default: - return fmt.Errorf("unsupported data type: %T", src) - } -} - -var _ json.Marshaler = (*Date)(nil) - -func (d Date) MarshalJSON() ([]byte, error) { - return json.Marshal(d.Time.Format("2006-01-02")) -} - -var _ json.Unmarshaler = (*Date)(nil) - -func (d *Date) UnmarshalJSON(raw []byte) error { - var s string - err := json.Unmarshal(raw, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal value, %w", err) - } - d.Time, err = time.ParseInLocation("2006-01-02", s, et.Loc) - return err -} diff --git a/model/user.go b/model/user.go index d79edd2..672205e 100644 --- a/model/user.go +++ b/model/user.go @@ -1,6 +1,7 @@ package model import ( + "electricity_bill_calc/types" "time" "github.com/shopspring/decimal" @@ -13,14 +14,14 @@ const ( ) type ManagementAccountCreationForm struct { - Id *string `json:"id"` - Username string `json:"username"` - Name string `json:"name"` - Contact *string `json:"contact"` - Phone *string `json:"phone"` - Type int16 `json:"type"` - Enabled bool `json:"enabled"` - Expires Date `json:"expires"` + Id *string `json:"id"` + Username string `json:"username"` + Name string `json:"name"` + Contact *string `json:"contact"` + Phone *string `json:"phone"` + Type int16 `json:"type"` + Enabled bool `json:"enabled"` + Expires types.Date `json:"expires"` } func (m ManagementAccountCreationForm) IntoUser() *User { @@ -83,7 +84,7 @@ type UserDetail struct { Contact *string `json:"contact"` Phone *string `json:"phone"` UnitServiceFee decimal.Decimal `db:"unit_service_fee" json:"unitServiceFee"` - ServiceExpiration Date `json:"serviceExpiration"` + ServiceExpiration types.Date `json:"serviceExpiration"` CreatedAt time.Time `json:"createdAt"` CreatedBy *string `json:"createdBy"` LastModifiedAt time.Time `json:"lastModifiedAt"` @@ -105,7 +106,7 @@ type UserWithDetail struct { Contact *string `json:"contact"` Phone *string `json:"phone"` UnitServiceFee decimal.Decimal `db:"unit_service_fee" json:"unitServiceFee"` - ServiceExpiration Date `json:"serviceExpiration"` + ServiceExpiration types.Date `json:"serviceExpiration"` CreatedAt time.Time `json:"createdAt"` CreatedBy *string `json:"createdBy"` LastModifiedAt time.Time `json:"lastModifiedAt"` diff --git a/repository/charge.go b/repository/charge.go index d3706cb..1908913 100644 --- a/repository/charge.go +++ b/repository/charge.go @@ -9,6 +9,7 @@ import ( "electricity_bill_calc/model" "electricity_bill_calc/tools" "electricity_bill_calc/tools/time" + "electricity_bill_calc/types" "fmt" st "time" @@ -110,7 +111,7 @@ func (cr _ChargeRepository) FindCharges(page uint, beginTime, endTime *st.Time, } // 在用户充值记录中创建一条新的记录 -func (cr _ChargeRepository) CreateChargeRecord(tx pgx.Tx, ctx context.Context, uid string, fee, discount, amount *decimal.Decimal, chargeTo model.Date) (bool, error) { +func (cr _ChargeRepository) CreateChargeRecord(tx pgx.Tx, ctx context.Context, uid string, fee, discount, amount *decimal.Decimal, chargeTo types.Date) (bool, error) { createQuery, createArgs, _ := cr.ds. Insert(goqu.T("user_charge")). Cols("user_id", "fee", "discount", "amount", "charge_to", "created_at"). @@ -142,7 +143,7 @@ func (cr _ChargeRepository) CancelCharge(tx pgx.Tx, ctx context.Context, uid str } // 检索用户最近有效的服务期限 -func (cr _ChargeRepository) LatestValidChargeTo(tx pgx.Tx, ctx context.Context, uid string) (*model.Date, error) { +func (cr _ChargeRepository) LatestValidChargeTo(tx pgx.Tx, ctx context.Context, uid string) (*types.Date, error) { searchSql, searchArgs, _ := cr.ds. From(goqu.T("user_charge")). Select("charge_to"). @@ -154,7 +155,7 @@ func (cr _ChargeRepository) LatestValidChargeTo(tx pgx.Tx, ctx context.Context, ). Prepared(true).ToSQL() - var chargeTo []*model.Date + var chargeTo []*types.Date if err := pgxscan.Select(ctx, tx, &chargeTo, searchSql, searchArgs...); err != nil { cr.log.Error("检索用户有效服务期限列表失败。", zap.Error(err)) return nil, err @@ -162,6 +163,6 @@ func (cr _ChargeRepository) LatestValidChargeTo(tx pgx.Tx, ctx context.Context, if len(chargeTo) == 0 { return nil, fmt.Errorf("无法找到用户最近的有效服务期限。") } - lastCharge := lo.MaxBy(chargeTo, func(a, b *model.Date) bool { return a.Time.After(b.Time) }) + lastCharge := lo.MaxBy(chargeTo, func(a, b *types.Date) bool { return a.Time.After(b.Time) }) return lastCharge, nil } diff --git a/service/charge.go b/service/charge.go index fc962fc..4135a8a 100644 --- a/service/charge.go +++ b/service/charge.go @@ -4,8 +4,8 @@ import ( "electricity_bill_calc/cache" "electricity_bill_calc/global" "electricity_bill_calc/logger" - "electricity_bill_calc/model" "electricity_bill_calc/repository" + "electricity_bill_calc/types" "fmt" "github.com/shopspring/decimal" @@ -21,7 +21,7 @@ var ChargeService = &_ChargeService{ } // 创建一条新的用户充值记录,同时更新用户的服务期限 -func (cs _ChargeService) RecordUserCharge(uid string, fee, discount, amount *decimal.Decimal, chargeTo model.Date, extendExpriationIgnoringSettle bool) (bool, error) { +func (cs _ChargeService) RecordUserCharge(uid string, fee, discount, amount *decimal.Decimal, chargeTo types.Date, extendExpriationIgnoringSettle bool) (bool, error) { cs.log.Info( "创建一条新的用户充值记录。", zap.String("uid", uid), diff --git a/types/date.go b/types/date.go new file mode 100644 index 0000000..e9c8e8d --- /dev/null +++ b/types/date.go @@ -0,0 +1,123 @@ +package types + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "fmt" + "time" + + "go.uber.org/zap" +) + +type Date struct { + time.Time +} + +func NewDate(year int, month time.Month, day int) Date { + return Date{ + Time: time.Date(year, month, day, 0, 0, 0, 0, loc), + } +} + +func NewEmptyDate() Date { + return Date{ + Time: time.Time{}.In(loc), + } +} + +func NowDate() Date { + return Now().Date() +} + +func ParseDate(t string) (Date, error) { + d, err := time.ParseInLocation("2006-01-02", t, loc) + if err != nil { + return NewEmptyDate(), fmt.Errorf("无法解析给定的日期, %w", err) + } + return Date{ + Time: d, + }, nil +} + +var _ driver.Valuer = (*Date)(nil) + +func (dt Date) Value() (driver.Value, error) { + return dt.In(loc).Format("2006-01-02"), nil +} + +var _ sql.Scanner = (*Date)(nil) + +func (d *Date) Scan(src interface{}) (err error) { + switch src := src.(type) { + case time.Time: + d.Time = src + case string: + t, err := time.ParseInLocation("2006-01-02", src, loc) + if err != nil { + return err + } + *d = Date{Time: t} + case []byte: + d.Time, err = time.ParseInLocation("2006-01-02", string(src), loc) + return err + case nil: + d = nil + default: + return fmt.Errorf("该数据类型不支持解析到日期: %T", src) + } + return nil +} + +var _ json.Marshaler = (*Date)(nil) + +func (d Date) MarshalJSON() ([]byte, error) { + return json.Marshal(d.Format("2006-01-02")) +} + +var _ json.Unmarshaler = (*Date)(nil) + +func (d *Date) UnmarshalJSON(data []byte) error { + var str string + if err := json.Unmarshal(data, &str); err != nil { + return fmt.Errorf("不能解析指定的日期时间值: %w", err) + } + t, err := time.ParseInLocation("2006-01-02", str, loc) + d.Time = t + return err +} + +func (d Date) DifferenceInMonth(d2 *Date) int { + var differYear, differMonth int + differYear = d.Year() - d2.Year() + differMonth = int(d.Month() - d2.Month()) + return differYear*12 + differMonth +} + +func (d Date) IsNextMonth(d2 *Date) bool { + return d.DifferenceInMonth(d2) == 1 +} + +func (d Date) ToBeginningOfDate() DateTime { + return FromTime(time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, loc)) +} + +func (d Date) ToEndingOfDate() DateTime { + return FromTime(time.Date(d.Year(), d.Month(), d.Day(), 23, 59, 59, 999999, loc)) +} + +func (d Date) IsEmpty() bool { + return d.Time.IsZero() +} + +func (d Date) ToString() string { + return d.Time.Format("2006-01-02") +} + +func (d Date) ToDateTime() DateTime { + return FromTime(d.Time) +} + +func (d Date) Log(fieldName string) zap.Field { + return zap.String(fieldName, d.ToString()) +} diff --git a/types/datetime.go b/types/datetime.go new file mode 100644 index 0000000..74bbd5a --- /dev/null +++ b/types/datetime.go @@ -0,0 +1,118 @@ +package types + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "fmt" + "time" + + "go.uber.org/zap" +) + +var loc *time.Location = time.FixedZone("+0800", 8*60*60) + +type DateTime struct { + time.Time +} + +func Now() DateTime { + return DateTime{ + Time: time.Now().In(loc), + } +} + +func Timestamp() int64 { + startline := time.Date(2022, 2, 22, 22, 22, 22, 0, loc).Unix() + return Now().Unix() - startline +} + +func FromTime(t time.Time) DateTime { + return DateTime{ + Time: t, + } +} + +var _ driver.Valuer = (*DateTime)(nil) + +func (dt DateTime) Value() (driver.Value, error) { + return dt.In(loc).Format("2006-01-02 15:04:05"), nil +} + +var _ sql.Scanner = (*DateTime)(nil) + +func (dt *DateTime) Scan(src interface{}) (err error) { + switch src := src.(type) { + case time.Time: + dt.Time = src + case string: + t, err := time.ParseInLocation("2006-01-02 15:04:05", src, loc) + if err != nil { + return err + } + *dt = DateTime{Time: t} + case []byte: + dt.Time, err = time.ParseInLocation("2006-01-02 15:04:05", string(src), loc) + return err + case nil: + dt = nil + default: + return fmt.Errorf("该数据类型不支持解析到日期时间: %T", src) + } + return nil +} + +var _ json.Marshaler = (*DateTime)(nil) + +func (dt DateTime) MarshalJSON() ([]byte, error) { + return json.Marshal(dt.Format("2006-01-02 15:04:05")) +} + +var _ json.Unmarshaler = (*DateTime)(nil) + +func (dt *DateTime) UnmarshalJSON(data []byte) error { + var str string + if err := json.Unmarshal(data, &str); err != nil { + return fmt.Errorf("不能解析指定的日期时间值: %w", err) + } + t, err := time.ParseInLocation("2006-01-02 15:04:05", str, loc) + dt.Time = t + return err +} + +func (dt DateTime) DifferenceInMonth(d DateTime) int { + var differYear, differMonth int + differYear = dt.Year() - d.Year() + differMonth = int(dt.Month() - d.Month()) + return differYear*12 + differMonth +} + +func (dt DateTime) IsNextMonth(target DateTime) bool { + return dt.DifferenceInMonth(target) == 1 +} + +func (dt *DateTime) ToBeginningOfDate() { + dt.Time = time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, loc) +} + +func (dt *DateTime) ToEndingOfDate() { + dt.Time = time.Date(dt.Year(), dt.Month(), dt.Day(), 23, 59, 59, 999999, loc) +} + +func (dt DateTime) Date() Date { + return Date{ + Time: time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, loc), + } +} + +func (dt DateTime) IsEmpty() bool { + return dt.Time.IsZero() +} + +func (dt DateTime) ToString() string { + return dt.Time.Format("2006-01-02 15:04:05") +} + +func (dt DateTime) Log(fieldName string) zap.Field { + return zap.String(fieldName, dt.ToString()) +} diff --git a/vo/user.go b/vo/user.go index b09051e..8c7d7e4 100644 --- a/vo/user.go +++ b/vo/user.go @@ -3,6 +3,7 @@ package vo import ( "electricity_bill_calc/model" "electricity_bill_calc/tools/time" + "electricity_bill_calc/types" st "time" "github.com/shopspring/decimal" @@ -29,6 +30,7 @@ func (u MGTAndOPSAccountCreationForm) IntoUser() *model.User { func (u MGTAndOPSAccountCreationForm) IntoUserDetail() *model.UserDetail { return &model.UserDetail{ + Id: "", Name: &u.Name, Abbr: nil, Region: nil, @@ -36,7 +38,7 @@ func (u MGTAndOPSAccountCreationForm) IntoUserDetail() *model.UserDetail { Contact: u.Contact, Phone: u.Phone, UnitServiceFee: decimal.Zero, - ServiceExpiration: model.SpecificDate(2099, st.December, 31), + ServiceExpiration: types.NewDate(2099, st.December, 31), CreatedAt: time.Now(), CreatedBy: nil, LastModifiedAt: time.Now(), @@ -80,7 +82,7 @@ func (u EnterpriseAccountCreationForm) IntoUserDetail() (*model.UserDetail, erro Contact: u.Contact, Phone: u.Phone, UnitServiceFee: unitServiceFee, - ServiceExpiration: model.SpecificDate(2000, st.January, 1), + ServiceExpiration: types.NewDate(2000, st.January, 1), CreatedAt: time.Now(), CreatedBy: nil, LastModifiedAt: time.Now(),