refactor(types):将日期时间类型提取到公共的类型定义包中。

This commit is contained in:
徐涛 2023-06-05 21:53:05 +08:00
parent c22e7e7dc0
commit 85f4d04a7f
11 changed files with 285 additions and 126 deletions

View File

@ -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())

View File

@ -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)
}

View File

@ -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)

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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"`

View File

@ -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
}

View File

@ -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),

123
types/date.go Normal file
View File

@ -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())
}

118
types/datetime.go Normal file
View File

@ -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())
}

View File

@ -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(),