修复账单与交易接口问题 & 管理员权限的身份限制
This commit is contained in:
@@ -169,6 +169,9 @@ func adminScopes(admin *m.Admin) ([]string, error) {
|
||||
|
||||
scopeNames := make([]string, 0, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
if scope.Name == "" {
|
||||
continue
|
||||
}
|
||||
scopeNames = append(scopeNames, scope.Name)
|
||||
}
|
||||
return scopeNames, nil
|
||||
|
||||
@@ -356,6 +356,11 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 非锁定管理员,不允许为空权限
|
||||
if !admin.Lock && (len(scopes) == 0) {
|
||||
return nil, ErrAuthorizeInvalidScope // 没有配置权限
|
||||
}
|
||||
|
||||
// 更新管理员登录时间
|
||||
admin.LastLogin = u.P(time.Now())
|
||||
admin.LastLoginIP = ip
|
||||
|
||||
@@ -66,6 +66,7 @@ const (
|
||||
ScopeTradeRead = string("trade:read") // 读取交易列表
|
||||
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表
|
||||
ScopeTradeWrite = string("trade:write") // 写入交易
|
||||
ScopeTradeWriteComplete = string("trade:write:complete") // 完成交易
|
||||
|
||||
ScopeBill = string("bill") // 账单
|
||||
ScopeBillRead = string("bill:read") // 读取账单列表
|
||||
|
||||
@@ -173,6 +173,8 @@ type PageTradeOfUserByAdminReq struct {
|
||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
// 创建订单
|
||||
func TradeCreate(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
@@ -219,6 +221,8 @@ type TradeCreateReq struct {
|
||||
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
// 完成订单
|
||||
func TradeComplete(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
@@ -228,13 +232,13 @@ func TradeComplete(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(TradeCompleteReq)
|
||||
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||
var req s.TradeRef
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查订单状态
|
||||
err = s.Trade.CompleteTrade(authCtx.User, &req.TradeRef)
|
||||
err = s.Trade.CompleteTrade(authCtx.User, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -242,10 +246,40 @@ func TradeComplete(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
||||
type TradeCompleteReq struct {
|
||||
// 管理员完成订单
|
||||
func TradeCompleteByAdmin(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWriteComplete)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
var req struct {
|
||||
s.TradeRef
|
||||
UserID int32 `json:"user_id" validate:"required"`
|
||||
}
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
user, err := s.User.Get(q.Q, req.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 完成订单
|
||||
err = s.Trade.CompleteTrade(user, &req.TradeRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
// 取消订单
|
||||
func TradeCancel(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
@@ -274,6 +308,8 @@ type TradeCancelReq struct {
|
||||
s.TradeRef
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
// 检查订单
|
||||
func TradeCheck(c *fiber.Ctx) error {
|
||||
// 检查权限:sse 接口暂时不检查权限
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/web/auth"
|
||||
"platform/web/core"
|
||||
g "platform/web/globals"
|
||||
@@ -76,6 +77,12 @@ func PageUserByAdmin(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
|
||||
if user.IDNo != nil && len(*user.IDNo) == 18 {
|
||||
var str = *user.IDNo
|
||||
*user.IDNo = str[:6] + "****" + str[len(str)-2:]
|
||||
}
|
||||
|
||||
if user.Admin != nil {
|
||||
user.Admin = &m.Admin{
|
||||
Name: user.Admin.Name,
|
||||
@@ -306,6 +313,9 @@ func UpdateUser(c *fiber.Ctx) error {
|
||||
ContactQQ: &req.ContactQQ,
|
||||
ContactWechat: &req.ContactWechat,
|
||||
})
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return core.NewBizErr("用户名或邮箱已被占用")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/pkg/env"
|
||||
"platform/pkg/u"
|
||||
"platform/web/auth"
|
||||
@@ -92,7 +93,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
|
||||
|
||||
ip, err := secureAddr(req.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
return core.NewBizErr("IP 地址无效", err)
|
||||
}
|
||||
|
||||
// 创建白名单
|
||||
@@ -132,7 +133,7 @@ func UpdateWhitelist(c *fiber.Ctx) error {
|
||||
|
||||
ip, err := secureAddr(req.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
return core.NewBizErr("IP 地址无效", err)
|
||||
}
|
||||
|
||||
// 更新白名单
|
||||
@@ -201,7 +202,7 @@ func secureAddr(str string) (*orm.Inet, error) {
|
||||
return nil, err
|
||||
}
|
||||
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用")
|
||||
return nil, errors.New("IP 地址不可用")
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@ func adminRouter(api fiber.Router) {
|
||||
var trade = api.Group("/trade")
|
||||
trade.Post("/page", handlers.PageTradeByAdmin)
|
||||
trade.Post("/page/of-user", handlers.PageTradeOfUserByAdmin)
|
||||
trade.Post("/complete", handlers.TradeCompleteByAdmin)
|
||||
|
||||
// bill 账单
|
||||
var bill = api.Group("/bill")
|
||||
|
||||
@@ -9,8 +9,8 @@ var Bill = &billService{}
|
||||
|
||||
type billService struct{}
|
||||
|
||||
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) error {
|
||||
return q.Bill.Create(&m.Bill{
|
||||
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) (*m.Bill, error) {
|
||||
bill := &m.Bill{
|
||||
UserID: uid,
|
||||
BillNo: ID.GenReadable("bil"),
|
||||
TradeID: &tradeId,
|
||||
@@ -18,11 +18,18 @@ func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *T
|
||||
Info: &detail.Subject,
|
||||
Amount: detail.Amount,
|
||||
Actual: detail.Actual,
|
||||
})
|
||||
}
|
||||
|
||||
err := q.Bill.Create(bill)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bill, nil
|
||||
}
|
||||
|
||||
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) error {
|
||||
return q.Bill.Create(&m.Bill{
|
||||
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) (*m.Bill, error) {
|
||||
bill := &m.Bill{
|
||||
UserID: uid,
|
||||
BillNo: ID.GenReadable("bil"),
|
||||
ResourceID: &resourceId,
|
||||
@@ -32,5 +39,12 @@ func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, trade
|
||||
Info: &detail.Subject,
|
||||
Amount: detail.Amount,
|
||||
Actual: detail.Actual,
|
||||
})
|
||||
}
|
||||
|
||||
err := q.Bill.Create(bill)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bill, nil
|
||||
}
|
||||
|
||||
@@ -28,11 +28,6 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
|
||||
|
||||
return q.Q.Transaction(func(q *q.Query) error {
|
||||
|
||||
// 更新用户余额
|
||||
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
|
||||
return core.NewServErr("更新用户余额失败", err)
|
||||
}
|
||||
|
||||
// 保存套餐
|
||||
resource, err := s.Create(q, user.ID, now, data)
|
||||
if err != nil {
|
||||
@@ -40,11 +35,16 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
|
||||
}
|
||||
|
||||
// 生成账单
|
||||
err = Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
|
||||
bill, err := Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
|
||||
if err != nil {
|
||||
return core.NewServErr("生成账单失败", err)
|
||||
}
|
||||
|
||||
// 更新用户余额
|
||||
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil, &bill.ID); err != nil {
|
||||
return core.NewServErr("更新用户余额失败", err)
|
||||
}
|
||||
|
||||
// 核销优惠券
|
||||
if detail.CouponUserId != nil {
|
||||
err = Coupon.UseCoupon(q, *detail.CouponUserId)
|
||||
|
||||
@@ -302,17 +302,18 @@ 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, "充值余额", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成账单
|
||||
err = Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
|
||||
bill, err := Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
|
||||
if err != nil {
|
||||
return core.NewServErr("生成账单失败", err)
|
||||
}
|
||||
|
||||
// 更新用户余额
|
||||
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil, &bill.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case m.TradeTypePurchase:
|
||||
data, ok := detail.Product.(*CreateResourceData)
|
||||
if !ok {
|
||||
@@ -326,7 +327,7 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
||||
}
|
||||
|
||||
// 生成账单
|
||||
err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
|
||||
_, err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
|
||||
if err != nil {
|
||||
return core.NewServErr("生成账单失败", err)
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Deci
|
||||
}
|
||||
|
||||
return q.Q.Transaction(func(q *q.Query) error {
|
||||
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
|
||||
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
|
||||
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32, billId *int32) error {
|
||||
balance := user.Balance.Add(amount)
|
||||
if balance.IsNegative() {
|
||||
return core.NewServErr("用户余额不足")
|
||||
@@ -66,6 +66,7 @@ func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Dec
|
||||
err = q.BalanceActivity.Create(&m.BalanceActivity{
|
||||
UserID: user.ID,
|
||||
AdminID: adminId,
|
||||
BillID: billId,
|
||||
Amount: amount.StringFixed(2),
|
||||
BalancePrev: user.Balance.StringFixed(2),
|
||||
BalanceCurr: balance.StringFixed(2),
|
||||
|
||||
Reference in New Issue
Block a user