恢复余额功能 & 管理员修改余额功能

This commit is contained in:
2026-03-30 14:59:35 +08:00
parent 22cb2d50d3
commit 4481c581e9
27 changed files with 1246 additions and 204 deletions

View File

@@ -122,7 +122,7 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
// 更新管理员基本信息
if len(simples) > 0 {
_, err := tx.Admin.
Where(tx.Admin.ID.Eq(update.Id)).
Where(tx.Admin.ID.Eq(update.Id), tx.Admin.Username.Neq("admin")).
UpdateSimple(simples...)
if err != nil {
return err
@@ -154,6 +154,6 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
}
func (s *adminService) RemoveAdmin(id int32) error {
_, err := q.Admin.Where(q.Admin.ID.Eq(id)).UpdateColumn(q.Admin.DeletedAt, time.Now())
_, err := q.Admin.Where(q.Admin.ID.Eq(id), q.Admin.Username.Neq("admin")).UpdateColumn(q.Admin.DeletedAt, time.Now())
return err
}

View File

@@ -18,11 +18,11 @@ var Coupon = &couponService{}
type couponService struct{}
func (s *couponService) All() (result []*m.Coupon, err error) {
return q.Coupon.Find()
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).Find()
}
func (s *couponService) Page(req *core.PageReq) (result []*m.Coupon, count int64, err error) {
return q.Coupon.FindByPage(req.GetOffset(), req.GetLimit())
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *couponService) Create(data CreateCouponData) error {

View File

@@ -15,5 +15,5 @@ func (r *permissionService) ListPermissions() (result []*m.Permission, err error
}
func (p *permissionService) PagePermissions(req core.PageReq) (result []*m.Permission, count int64, err error) {
return q.Permission.FindByPage(req.GetOffset(), req.GetLimit())
return q.Permission.Order(q.Permission.Sort).FindByPage(req.GetOffset(), req.GetLimit())
}

View File

@@ -20,7 +20,9 @@ func (s *productService) GetPrice(code string) {
// 获取所有产品
func (s *productService) AllProducts() ([]*m.Product, error) {
return q.Product.Find()
return q.Product.
Order(q.Product.Sort.Asc(), q.Product.CreatedAt.Desc()).
Find()
}
// 新增产品

View File

@@ -14,11 +14,11 @@ var ProductDiscount = &productDiscountService{}
type productDiscountService struct{}
func (s *productDiscountService) All() (result []*m.ProductDiscount, err error) {
return q.ProductDiscount.Find()
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).Find()
}
func (s *productDiscountService) Page(req *core.PageReq) (result []*m.ProductDiscount, count int64, err error) {
return q.ProductDiscount.FindByPage(req.GetOffset(), req.GetLimit())
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *productDiscountService) Create(data CreateProductDiscountData) (err error) {

View File

@@ -20,6 +20,7 @@ func (s *productSkuService) All(product_code string) (result []*m.ProductSku, er
Joins(q.ProductSku.Product).
Where(q.Product.As("Product").Code.Eq(product_code)).
Select(q.ProductSku.ALL).
Order(q.ProductSku.CreatedAt.Desc()).
Find()
}
@@ -31,6 +32,7 @@ func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []
return q.ProductSku.
Joins(q.ProductSku.Discount).
Where(do...).
Order(q.ProductSku.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
}

View File

@@ -26,21 +26,10 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
return core.NewServErr("获取产品支付信息失败", err)
}
newBalance := user.Balance.Sub(detail.Actual)
if newBalance.IsNegative() {
return ErrBalanceNotEnough
}
return q.Q.Transaction(func(q *q.Query) error {
// 更新用户余额
_, err = q.User.
Where(
q.User.ID.Eq(user.ID),
q.User.Balance.Eq(user.Balance),
).
UpdateSimple(q.User.Balance.Value(newBalance))
if err != nil {
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
return core.NewServErr("更新用户余额失败", err)
}
@@ -273,13 +262,21 @@ func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error)
return nil, err
}
var discountId *int32 = nil
if discount != nil {
discountId = &discount.ID
}
var couponId *int32 = nil
if coupon != nil {
couponId = &coupon.ID
}
return &TradeDetail{
data,
m.TradeTypePurchase,
sku.Name,
amount, actual,
&discount.ID, discount,
&coupon.ID, coupon,
discountId, discount,
couponId, coupon,
}, nil
}

View File

@@ -1,8 +1,9 @@
package services
import (
"bytes"
"context"
"encoding/json"
"encoding/gob"
"errors"
"fmt"
"io"
@@ -26,13 +27,18 @@ import (
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
)
func init() {
gob.Register(&CreateResourceData{})
gob.Register(&UpdateBalanceData{})
}
var Trade = &tradeService{}
type tradeService struct {
}
// 创建交易
func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productData *CreateResourceData) (*CreateTradeResult, error) {
func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productData ProductData) (*CreateTradeResult, error) {
if user == nil {
return nil, core.NewBizErr("用户未登录")
}
@@ -196,15 +202,12 @@ func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productD
}
// 缓存产品数据
serialized, err := json.Marshal(detail)
if err != nil {
return nil, core.NewServErr("序列化产品信息失败", err)
}
w := bytes.Buffer{}
gob.NewEncoder(&w).Encode(detail)
err = g.Redis.Set(
context.Background(),
tradeProductKey(tradeNo),
serialized,
w.Bytes(),
expireIn,
).Err()
if err != nil {
@@ -267,14 +270,15 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
case m.TradeStatusPending:
}
// 恢复购买信息
detailStr, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Result()
// 恢复购买信息;如果反序列化失败,检查开头 init 函数中是否注册了对应的 struct 类型
detailBytes, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Bytes()
if err != nil {
return core.NewServErr("恢复购买信息失败", err)
}
var detail TradeDetail
if err := json.Unmarshal([]byte(detailStr), &detail); err != nil {
r := bytes.NewReader(detailBytes)
if err := gob.NewDecoder(r).Decode(&detail); err != nil {
return core.NewServErr("解析购买信息失败", err)
}
@@ -299,7 +303,7 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
switch trade.Type {
case m.TradeTypeRecharge:
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual); err != nil {
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil); err != nil {
return err
}
@@ -605,12 +609,12 @@ type OnTradeCompletedData struct {
*TradeSuccessResult
}
type ProductInfo interface {
type ProductData interface {
TradeDetail(user *m.User) (*TradeDetail, error)
}
type TradeDetail struct {
Product ProductInfo `json:"product"`
Product ProductData `json:"product"`
Type m.TradeType `json:"type"`
Subject string `json:"subject"`
Amount decimal.Decimal `json:"amount"`
@@ -621,11 +625,6 @@ type TradeDetail struct {
Coupon *m.Coupon `json:"-"` // 不应缓存
}
type CompleteEvent interface {
Check(t m.TradeType) (ProductInfo, bool)
OnTradeComplete(info ProductInfo, trade *m.Trade) error
}
type TradeErr string
func (e TradeErr) Error() string {

View File

@@ -28,12 +28,28 @@ func (s *userService) Get(q *q.Query, uid int32) (*m.User, error) {
return user, nil
}
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal) error {
func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Decimal, adminId *int32) error {
if user == nil {
return core.NewServErr("用户不存在")
}
amount := newBalance.Sub(user.Balance)
if amount.IsZero() {
return nil
}
return q.Q.Transaction(func(q *q.Query) error {
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
})
}
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
balance := user.Balance.Add(amount)
if balance.IsNegative() {
return core.NewServErr("用户余额不足")
}
// 更新余额
_, err := q.User.
Where(
q.User.ID.Eq(user.ID),
@@ -46,6 +62,19 @@ func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Dec
return core.NewServErr("更新用户余额失败", err)
}
// 新增动账记录
err = q.BalanceActivity.Create(&m.BalanceActivity{
UserID: user.ID,
AdminID: adminId,
Amount: amount.StringFixed(2),
BalancePrev: user.Balance.StringFixed(2),
BalanceCurr: balance.StringFixed(2),
Remark: &remark,
})
if err != nil {
return core.NewServErr("新增动账记录失败", err)
}
return nil
}