From 234e811324e1159f37422ae1db84e50340eddf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Mon, 19 Jun 2023 14:46:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(topup):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E5=95=86=E6=88=B7=E5=85=85=E5=80=BC=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E5=BA=93=E6=93=8D=E4=BD=9C=E9=83=A8?= =?UTF-8?q?=E5=88=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/top_up.go | 22 +++++ repository/top_up.go | 186 +++++++++++++++++++++++++++++++++++++++++++ vo/top_up.go | 9 +++ 3 files changed, 217 insertions(+) create mode 100644 model/top_up.go create mode 100644 repository/top_up.go create mode 100644 vo/top_up.go diff --git a/model/top_up.go b/model/top_up.go new file mode 100644 index 0000000..64f2b35 --- /dev/null +++ b/model/top_up.go @@ -0,0 +1,22 @@ +package model + +import ( + "electricity_bill_calc/types" + + "github.com/shopspring/decimal" +) + +type TopUp struct { + TopUpCode string `json:"topUpCode" db:"top_up_code"` + Park string `json:"parkId" db:"park_id"` + Tenement string `json:"tenementId" db:"tenement_id"` + TenementName string `json:"tenementName" db:"tenement_name"` + Meter string `json:"meterId" db:"meter_id"` + MeterAddress *string `json:"meterAddress" db:"meter_address"` + ToppedUpAt types.DateTime `json:"toppedUpAt" db:"topped_up_at"` + Amount decimal.Decimal `json:"amount" db:"amount"` + PaymentType int16 `json:"paymentType" db:"payment_type"` + SuccessfulSynchronized bool `json:"successfulSynchronized" db:"successful_synchronized"` + SynchronizedAt *types.DateTime `json:"synchronizedAt" db:"synchronized_at"` + CancelledAt *types.DateTime `json:"cancelledAt" db:"cancelled_at"` +} diff --git a/repository/top_up.go b/repository/top_up.go new file mode 100644 index 0000000..83b87cf --- /dev/null +++ b/repository/top_up.go @@ -0,0 +1,186 @@ +package repository + +import ( + "electricity_bill_calc/cache" + "electricity_bill_calc/config" + "electricity_bill_calc/global" + "electricity_bill_calc/logger" + "electricity_bill_calc/model" + "electricity_bill_calc/tools/serial" + "electricity_bill_calc/types" + "electricity_bill_calc/vo" + "fmt" + + "github.com/doug-martin/goqu/v9" + _ "github.com/doug-martin/goqu/v9/dialect/postgres" + "github.com/georgysavva/scany/v2/pgxscan" + "go.uber.org/zap" +) + +type _TopUpRepository struct { + log *zap.Logger + ds goqu.DialectWrapper +} + +var TopUpRepository = _TopUpRepository{ + log: logger.Named("Repository", "TopUp"), + ds: goqu.Dialect("postgres"), +} + +// 检索符合条件的商户充值记录 +func (tur _TopUpRepository) ListTopUps(pid string, startDate, endDate *types.Date, keyword *string, page uint) ([]*model.TopUp, int64, error) { + tur.log.Info("查询符合条件的商户充值记录", zap.String("Park", pid), logger.DateFieldp("StartDate", startDate), logger.DateFieldp("EndDate", endDate), zap.Stringp("keyword", keyword), zap.Uint("page", page)) + cacheConditions := []string{ + pid, + cache.NullableStringKey(keyword), + cache.NullableConditionKey(startDate), + cache.NullableConditionKey(endDate), + fmt.Sprintf("%d", page), + } + if topUps, total, err := cache.RetrievePagedSearch[[]*model.TopUp]("top_ups", cacheConditions...); err == nil && topUps != nil && len(*topUps) > 0 { + tur.log.Info("从缓存中获取到商户充值记录", zap.Int("Count", len(*topUps)), zap.Int64("Total", total)) + return *topUps, total, nil + } + ctx, cancel := global.TimeoutContext() + defer cancel() + + topUpQuery := tur.ds. + From(goqu.T("tenement_top_ups").As("t")). + LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))). + LeftJoin(goqu.T("meter").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))). + Select("t.*", goqu.I("te.full_name").As("tenement_name"), goqu.I("m.address").As("meter_address")). + Where(goqu.I("t.park_id").Eq(pid)) + countQuery := tur.ds. + From(goqu.T("tenement_top_ups").As("t")). + LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))). + LeftJoin(goqu.T("meter").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))). + Select(goqu.COUNT("t.*")). + Where(goqu.I("t.park_id").Eq(pid)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + topUpQuery = topUpQuery.Where(goqu.Or( + goqu.I("te.full_name").ILike(pattern), + goqu.I("te.short_name").ILike(pattern), + goqu.I("te.abbr").ILike(pattern), + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("te.full_name").ILike(pattern), + goqu.I("te.short_name").ILike(pattern), + goqu.I("te.abbr").ILike(pattern), + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + } + + if startDate != nil { + topUpQuery = topUpQuery.Where(goqu.I("t.topped_up_at").Gte(startDate.ToBeginningOfDate())) + countQuery = countQuery.Where(goqu.I("t.topped_up_at").Gte(startDate.ToBeginningOfDate())) + } + + if endDate != nil { + topUpQuery = topUpQuery.Where(goqu.I("t.topped_up_at").Lte(endDate.ToEndingOfDate())) + countQuery = countQuery.Where(goqu.I("t.topped_up_at").Lte(endDate.ToEndingOfDate())) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + topUpQuery = topUpQuery.Order(goqu.I("t.topped_up_at").Desc()).Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + topUpSql, topUpArgs, _ := topUpQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + + var ( + topUps []*model.TopUp = make([]*model.TopUp, 0) + total int64 = 0 + ) + if err := pgxscan.Select(ctx, global.DB, &topUps, topUpSql, topUpArgs...); err != nil { + tur.log.Error("查询商户充值记录失败", zap.Error(err)) + return topUps, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil { + tur.log.Error("查询商户充值记录总数失败", zap.Error(err)) + return topUps, 0, err + } + cache.CachePagedSearch(topUps, total, []string{fmt.Sprintf("top_up:%s", pid), fmt.Sprintf("tenement:%s", pid), fmt.Sprintf("meter:%s", pid)}, "top_ups", cacheConditions...) + return topUps, total, nil +} + +// 取得一个充值记录的详细信息 +func (tur _TopUpRepository) GetTopUp(pid, topUpCode string) (*model.TopUp, error) { + tur.log.Info("查询充值记录", zap.String("Park", pid), zap.String("TopUpCode", topUpCode)) + if topUp, err := cache.RetrieveEntity[model.TopUp]("top_up", topUpCode); err == nil && topUp != nil { + tur.log.Info("从缓存中获取到充值记录") + return topUp, err + } + ctx, cancel := global.TimeoutContext() + defer cancel() + + topUpSql, topUpArgs, _ := tur.ds. + From(goqu.T("tenement_top_ups").As("t")). + LeftJoin(goqu.T("tenement").As("te"), goqu.On(goqu.I("te.id").Eq(goqu.I("t.tenement_id")), goqu.I("te.park_id").Eq(goqu.I("t.park_id")))). + LeftJoin(goqu.T("meter").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("t.meter_id")), goqu.I("m.park_id").Eq(goqu.I("t.park_id")))). + Select("t.*", goqu.I("te.full_name").As("tenement_name"), goqu.I("m.address").As("meter_address")). + Where(goqu.I("t.park_id").Eq(pid), goqu.I("t.top_up_code").Eq(topUpCode)). + Prepared(true).ToSQL() + + var topUp model.TopUp + if err := pgxscan.Get(ctx, global.DB, &topUp, topUpSql, topUpArgs...); err != nil { + tur.log.Error("查询充值记录失败", zap.Error(err)) + return nil, err + } + cache.CacheEntity(&topUp, []string{fmt.Sprintf("top_up:%s", pid), fmt.Sprintf("tenement:%s", pid), fmt.Sprintf("meter:%s", pid)}, "top_up", topUpCode) + return &topUp, nil +} + +// 创建一条新的充值记录 +func (tur _TopUpRepository) CreateTopUp(pid string, form *vo.TopUpCreationForm) error { + tur.log.Info("创建一条新的充值记录", zap.String("Park", pid), zap.String("Tenement", form.Tenement), zap.String("Meter", form.Meter)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + serial.StringSerialRequestChan <- 1 + topUpCode := serial.Prefix("U", <-serial.StringSerialResponseChan) + topUpTime := types.Now() + topUpSql, topUpArgs, _ := tur.ds. + Insert("tenement_top_ups"). + Cols("top_up_code", "park_id", "tenement_id", "meter_id", "topped_up_at", "amount", "payment_type"). + Vals(goqu.Vals{ + topUpCode, + pid, + form.Tenement, + form.Meter, + topUpTime, + form.Amount, + model.PAYMENT_CASH, + }). + Prepared(true).ToSQL() + + if _, err := global.DB.Exec(ctx, topUpSql, topUpArgs...); err != nil { + tur.log.Error("创建充值记录失败", zap.Error(err)) + return err + } + cache.AbolishRelation(fmt.Sprintf("top_ups:%s", pid)) + return nil +} + +// 删除一条充值记录 +func (tur _TopUpRepository) DeleteTopUp(pid, topUpCode string) error { + tur.log.Info("删除一条充值记录", zap.String("Park", pid), zap.String("TopUpCode", topUpCode)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + topUpSql, topUpArgs, _ := tur.ds. + Update("tenement_top_ups"). + Set(goqu.Record{"cancelled_at": types.Now()}). + Where(goqu.I("park_id").Eq(pid), goqu.I("top_up_code").Eq(topUpCode)). + Prepared(true).ToSQL() + + if _, err := global.DB.Exec(ctx, topUpSql, topUpArgs...); err != nil { + tur.log.Error("删除充值记录失败", zap.Error(err)) + return err + } + cache.AbolishRelation(fmt.Sprintf("top_ups:%s", pid)) + return nil +} diff --git a/vo/top_up.go b/vo/top_up.go new file mode 100644 index 0000000..fb7f01f --- /dev/null +++ b/vo/top_up.go @@ -0,0 +1,9 @@ +package vo + +import "github.com/shopspring/decimal" + +type TopUpCreationForm struct { + Tenement string `json:"tenement"` + Meter string `json:"meter"` + Amount decimal.Decimal `json:"amount"` +}