From f033691aa47caa6b7e6062c247e80862b3f1adf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Mon, 15 Aug 2022 15:40:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(charge):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=89=80=E6=9C=89=E7=94=A8=E6=88=B7=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=BB=B6=E6=9C=9F=E8=AE=B0=E5=BD=95=E9=83=A8=E5=88=86=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=BE=85=E6=B5=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/charge.go | 59 +++++++++++++++++- model/user_charges.go | 23 ++++--- repository/charge.go | 18 ++++++ router/router.go | 1 + service/charge.go | 142 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 232 insertions(+), 11 deletions(-) diff --git a/controller/charge.go b/controller/charge.go index 3603310..a5666c8 100644 --- a/controller/charge.go +++ b/controller/charge.go @@ -1,13 +1,17 @@ package controller import ( + "electricity_bill_calc/model" "electricity_bill_calc/repository" "electricity_bill_calc/response" "electricity_bill_calc/security" + "electricity_bill_calc/service" "net/http" "strconv" + "time" "github.com/gin-gonic/gin" + "github.com/shopspring/decimal" ) type _ChargesController struct { @@ -22,7 +26,8 @@ func InitializeChargesController(router *gin.Engine) { } ChargesController.Router.GET("/charges", security.OPSAuthorize, listAllCharges) - + ChargesController.Router.POST("/charge", security.OPSAuthorize, recordNewCharge) + ChargesController.Router.PUT("/charge/:uid/:seq", security.OPSAuthorize, modifyChargeState) } func listAllCharges(c *gin.Context) { @@ -46,3 +51,55 @@ func listAllCharges(c *gin.Context) { gin.H{"records": charges}, ) } + +type _NewChargeFormData struct { + UserId string `json:"userId" form:"userId"` + Fee decimal.NullDecimal `json:"fee" form:"fee"` + Discount decimal.NullDecimal `json:"discount" form:"discount"` + Amount decimal.NullDecimal `json:"amount" form:"amount"` + ChargeTo time.Time `json:"chargeTo" form:"chargeTo" time_format:"simple_date" time_location:"shanghai"` +} + +func recordNewCharge(c *gin.Context) { + result := response.NewResult(c) + formData := new(_NewChargeFormData) + c.BindJSON(formData) + currentTime := time.Now() + newRecord := &model.UserCharge{ + UserId: formData.UserId, + Fee: formData.Fee, + Discount: formData.Discount, + Amount: formData.Amount, + Settled: true, + SettledAt: ¤tTime, + ChargeTo: formData.ChargeTo, + } + err := service.ChargeService.CreateChargeRecord(newRecord, true) + if err != nil { + result.Error(http.StatusInternalServerError, err.Error()) + return + } + result.Success("指定用户的服务已延期。") +} + +type _StateChangeFormData struct { + Cancelled bool `json:"cancelled"` +} + +func modifyChargeState(c *gin.Context) { + result := response.NewResult(c) + formData := new(_StateChangeFormData) + c.BindJSON(formData) + requestUserID := c.Param("uid") + requestChargeSeq, err := strconv.Atoi(c.Param("seq")) + if err != nil { + result.Error(http.StatusNotAcceptable, "参数[记录流水号]解析错误。") + return + } + err = service.ChargeService.CancelCharge(int64(requestChargeSeq), requestUserID) + if err != nil { + result.Error(http.StatusInternalServerError, err.Error()) + return + } + result.Success("指定用户服务延期记录状态已经更新。") +} diff --git a/model/user_charges.go b/model/user_charges.go index 3f8ab12..fba29b9 100644 --- a/model/user_charges.go +++ b/model/user_charges.go @@ -7,16 +7,19 @@ import ( ) type UserCharge struct { - Created `xorm:"extends"` - Seq int64 `xorm:"bigint pk not null " json:"seq"` - UserId string `xorm:"varchar(120) not null" json:"userId"` - Fee decimal.Decimal `xorm:"numeric(12,2) not null" json:"fee"` - Discount decimal.Decimal `xorm:"numeric(5,4) not null" json:"discount"` - Amount decimal.Decimal `xorm:"numeric(12,2) not null" json:"amount"` - Settled bool `xorm:"bool not null default false" json:"settled"` - SettledAt *time.Time `xorm:"timestampz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"` - Refunded bool `xorm:"bool not null default false" json:"refunded"` - RefundedAt *time.Time `xorm:"timestampz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"` + Created `xorm:"extends"` + Seq int64 `xorm:"bigint pk not null autoincr" json:"seq"` + UserId string `xorm:"varchar(120) not null" json:"userId"` + Fee decimal.NullDecimal `xorm:"numeric(12,2)" json:"fee"` + Discount decimal.NullDecimal `xorm:"numeric(5,4)" json:"discount"` + Amount decimal.NullDecimal `xorm:"numeric(12,2)" json:"amount"` + ChargeTo time.Time `xorm:"date not null" json:"chargeTo" time_format:"simple_date" time_location:"shanghai"` + Settled bool `xorm:"bool not null default false" json:"settled"` + SettledAt *time.Time `xorm:"timestampz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"` + Cancelled bool `xorm:"bool not null default false" json:"cancelled"` + CancelledAt *time.Time `xorm:"timestampz" json:"cancelledAt" time_format:"simple_datetime" time_location:"shanghai"` + Refunded bool `xorm:"bool not null default false" json:"refunded"` + RefundedAt *time.Time `xorm:"timestampz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"` } func (UserCharge) TableName() string { diff --git a/repository/charge.go b/repository/charge.go index cdcfa6d..54b8aa8 100644 --- a/repository/charge.go +++ b/repository/charge.go @@ -4,6 +4,7 @@ import ( "electricity_bill_calc/config" "electricity_bill_calc/global" "electricity_bill_calc/model" + "electricity_bill_calc/utils" "time" "xorm.io/builder" @@ -50,3 +51,20 @@ func (_ChargeRepository) ListPagedChargeRecord(keyword, beginDate, endDate strin Find(&charges) return charges, total, err } + +func (_ChargeRepository) LastValidChargeTo(uid string) (time.Time, error) { + veryBlankTime, _ := time.Parse("2006-01-02 15:04:05", "0001-01-01 00:00:00") + var records = make([]time.Time, 0) + err := global.DBConn.Where(builder.Eq{"settled": true, "cancelled": false, "refunded": false}).Cols("charged_to").Find(&records) + if err != nil { + return veryBlankTime, nil + } + lastValid := utils.Reduce(records, veryBlankTime, func(acc, elem time.Time) time.Time { + if elem.After(acc) { + return elem + } else { + return acc + } + }) + return lastValid, nil +} diff --git a/router/router.go b/router/router.go index c8dfb6a..7cf804a 100644 --- a/router/router.go +++ b/router/router.go @@ -17,6 +17,7 @@ func Router() *gin.Engine { controller.InitializeUserController(router) controller.InitializeRegionController(router) + controller.InitializeChargesController(router) return router } diff --git a/service/charge.go b/service/charge.go index 31ee260..1bca1d9 100644 --- a/service/charge.go +++ b/service/charge.go @@ -1,5 +1,147 @@ package service +import ( + "electricity_bill_calc/exceptions" + "electricity_bill_calc/global" + "electricity_bill_calc/model" + "electricity_bill_calc/repository" + "time" + + "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 + } + _, 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 + } + 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 + } + 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 := repository.ChargeRepo.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 + } + 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("未找到匹配指定条件的计费记录。") + } + lastValidExpriation, err := repository.ChargeRepo.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 + } + 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() + } + return err +}