forked from free-lancers/electricity_bill_calc_service
		
	feat(charge):基本完成所有用户服务延期记录部分的功能,待测。
This commit is contained in:
		| @@ -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("指定用户服务延期记录状态已经更新。") | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -17,6 +17,7 @@ func Router() *gin.Engine { | ||||
|  | ||||
| 	controller.InitializeUserController(router) | ||||
| 	controller.InitializeRegionController(router) | ||||
| 	controller.InitializeChargesController(router) | ||||
|  | ||||
| 	return router | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user