完善套餐与账单接口 & 完善支付数据保存,记录实付价格并关联优惠券
This commit is contained in:
@@ -47,10 +47,24 @@ func PageBillByAdmin(c *fiber.Ctx) error {
|
||||
time := u.DateHead(*req.CreatedAtEnd)
|
||||
do = do.Where(q.Bill.CreatedAt.Lte(time))
|
||||
}
|
||||
if req.ProductCode != nil {
|
||||
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
|
||||
}
|
||||
if req.SkuCode != nil {
|
||||
do = do.Where(q.Bill.
|
||||
Where(q.ResourceShort.As("Resource__Short").Code.Eq(*req.SkuCode)).
|
||||
Or(q.ResourceLong.As("Resource__Long").Code.Eq(*req.SkuCode)))
|
||||
}
|
||||
|
||||
// 查询用户列表
|
||||
list, total, err := q.Bill.Debug().
|
||||
Joins(q.Bill.User, q.Bill.Resource, q.Bill.Trade).
|
||||
Joins(
|
||||
q.Bill.User,
|
||||
q.Bill.Resource,
|
||||
q.Bill.Trade,
|
||||
q.Bill.Resource.Short,
|
||||
q.Bill.Resource.Long,
|
||||
).
|
||||
Select(
|
||||
q.Bill.ALL,
|
||||
q.User.As("User").Phone.As("User__phone"),
|
||||
@@ -82,6 +96,8 @@ type PageBillByAdminReq struct {
|
||||
BillNo *string `json:"bill_no,omitempty"`
|
||||
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||
ProductCode *string `json:"product_code,omitempty"`
|
||||
SkuCode *string `json:"sku_code,omitempty"`
|
||||
}
|
||||
|
||||
// ListBill 获取账单列表
|
||||
|
||||
@@ -91,6 +91,29 @@ func DeleteProduct(c *fiber.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AllProductSkuByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req AllProductSkuByAdminReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list, err := s.ProductSku.All(req.Code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(list)
|
||||
}
|
||||
|
||||
type AllProductSkuByAdminReq struct {
|
||||
Code string `json:"product_code"`
|
||||
}
|
||||
|
||||
func PageProductSkuByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuRead)
|
||||
if err != nil {
|
||||
|
||||
@@ -70,7 +70,7 @@ func PageResourceShort(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
resource, err := q.Resource.Where(do).
|
||||
Joins(q.Resource.Short, q.ResourceShort.Sku).
|
||||
Joins(q.Resource.Short).
|
||||
Order(q.Resource.CreatedAt.Desc()).
|
||||
Offset(req.GetOffset()).
|
||||
Limit(req.GetLimit()).
|
||||
@@ -240,9 +240,28 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
||||
time := u.DateTail(*req.CreatedAtEnd)
|
||||
do = do.Where(q.Resource.CreatedAt.Lte(time))
|
||||
}
|
||||
if req.Expired != nil {
|
||||
if *req.Expired {
|
||||
do = do.Where(q.Resource.Where(
|
||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
||||
q.ResourceShort.As("Short").ExpireAt.Lte(time.Now()),
|
||||
).Or(
|
||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
||||
q.ResourceShort.As("Short").Quota.LteCol(q.ResourceShort.As("Short").Used),
|
||||
))
|
||||
} else {
|
||||
do = do.Where(q.Resource.Where(
|
||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
||||
q.ResourceShort.As("Short").ExpireAt.Gt(time.Now()),
|
||||
).Or(
|
||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
||||
q.ResourceShort.As("Short").Quota.GtCol(q.ResourceShort.As("Short").Used),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
list, total, err := q.Resource.Debug().
|
||||
Joins(q.Resource.User, q.Resource.Short).
|
||||
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
|
||||
Select(
|
||||
q.Resource.ALL,
|
||||
q.User.As("User").Phone.As("User__phone"),
|
||||
@@ -254,9 +273,14 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
||||
q.ResourceShort.As("Short").Daily.As("Short__daily"),
|
||||
q.ResourceShort.As("Short").LastAt.As("Short__last_at"),
|
||||
q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"),
|
||||
q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"),
|
||||
).
|
||||
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do).
|
||||
Order(q.Resource.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
List: list,
|
||||
@@ -274,9 +298,10 @@ type PageResourceShortByAdminReq struct {
|
||||
Mode *int `json:"mode" form:"mode"`
|
||||
CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"`
|
||||
CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"`
|
||||
Expired *bool `json:"expired" form:"expired"`
|
||||
}
|
||||
|
||||
// PageResourceLongByAdmin 分页查询全部短效套餐
|
||||
// PageResourceLongByAdmin 分页查询全部长效套餐
|
||||
func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin()
|
||||
if err != nil {
|
||||
@@ -307,9 +332,28 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||
if req.CreatedAtEnd != nil {
|
||||
do = do.Where(q.Resource.CreatedAt.Lte(*req.CreatedAtEnd))
|
||||
}
|
||||
if req.Expired != nil {
|
||||
if *req.Expired {
|
||||
do = do.Where(q.Resource.Where(
|
||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
||||
q.ResourceLong.As("Long").ExpireAt.Lte(time.Now()),
|
||||
).Or(
|
||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
||||
q.ResourceLong.As("Long").Quota.LteCol(q.ResourceLong.As("Long").Used),
|
||||
))
|
||||
} else {
|
||||
do = do.Where(q.Resource.Where(
|
||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
||||
q.ResourceLong.As("Long").ExpireAt.Gt(time.Now()),
|
||||
).Or(
|
||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
||||
q.ResourceLong.As("Long").Quota.GtCol(q.ResourceLong.As("Long").Used),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
list, total, err := q.Resource.
|
||||
Joins(q.Resource.User, q.Resource.Long).
|
||||
list, total, err := q.Resource.Debug().
|
||||
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
|
||||
Select(
|
||||
q.Resource.ALL,
|
||||
q.User.As("User").Phone.As("User__phone"),
|
||||
@@ -321,9 +365,14 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||
q.ResourceLong.As("Long").Daily.As("Long__daily"),
|
||||
q.ResourceLong.As("Long").LastAt.As("Long__last_at"),
|
||||
q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"),
|
||||
q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"),
|
||||
).
|
||||
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do).
|
||||
Order(q.Resource.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
List: list,
|
||||
@@ -341,6 +390,7 @@ type PageResourceLongByAdminReq struct {
|
||||
Mode *int `json:"mode" form:"mode"`
|
||||
CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"`
|
||||
CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"`
|
||||
Expired *bool `json:"expired" form:"expired"`
|
||||
}
|
||||
|
||||
// AllActiveResource 所有可用套餐
|
||||
@@ -402,6 +452,24 @@ func AllActiveResource(c *fiber.Ctx) error {
|
||||
type AllResourceReq struct {
|
||||
}
|
||||
|
||||
func UpdateResourceByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req s.UpdateResourceData
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Resource.Update(&req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(nil)
|
||||
}
|
||||
|
||||
// StatisticResourceFree 统计每日可用
|
||||
func StatisticResourceFree(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
@@ -602,26 +670,28 @@ func ResourcePrice(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 获取套餐价格
|
||||
sku, err := s.Resource.GetSku(req.CreateResourceData)
|
||||
sku, err := s.Resource.GetSku(req.CreateResourceData.Code())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
before, after, err := s.Resource.GetPrice(sku, req.Count(), nil)
|
||||
_, amount, discounted, couponApplied, err := s.Resource.GetPrice(sku, req.Count(), nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算折扣
|
||||
return c.JSON(ResourcePriceResp{
|
||||
Price: before.StringFixed(2),
|
||||
Discounted: float32(sku.Discount.Discount) / 100,
|
||||
DiscountedPrice: after.StringFixed(2),
|
||||
Discount: float32(sku.Discount.Discount) / 100,
|
||||
Price: amount.StringFixed(2),
|
||||
Discounted: discounted.StringFixed(2),
|
||||
CouponApplied: couponApplied.StringFixed(2),
|
||||
})
|
||||
}
|
||||
|
||||
type ResourcePriceResp struct {
|
||||
Price string `json:"price"`
|
||||
Discounted float32 `json:"discounted"`
|
||||
DiscountedPrice string `json:"discounted_price"`
|
||||
Price string `json:"price"`
|
||||
Discount float32 `json:"discounted"`
|
||||
Discounted string `json:"discounted_price"`
|
||||
CouponApplied string `json:"coupon_applied"`
|
||||
}
|
||||
|
||||
@@ -109,53 +109,38 @@ func TradeCreate(c *fiber.Ctx) error {
|
||||
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var product s.ProductInfo
|
||||
switch req.Type {
|
||||
case m.TradeTypePurchase:
|
||||
if req.Resource == nil {
|
||||
return core.NewBizErr("购买信息不能为空")
|
||||
}
|
||||
product, err = s.NewCreateResourceByTradeData(req.Resource)
|
||||
if err != nil {
|
||||
return core.NewServErr("处理购买产品信息失败", err)
|
||||
}
|
||||
case m.TradeTypeRecharge:
|
||||
if req.Recharge == nil {
|
||||
return core.NewBizErr("充值信息不能为空")
|
||||
}
|
||||
product = req.Recharge
|
||||
}
|
||||
|
||||
// 创建交易
|
||||
result, err := s.Trade.CreateTrade(authCtx.User.ID, time.Now(), &req.CreateTradeData, product)
|
||||
// 处理订单
|
||||
uid := authCtx.User.ID
|
||||
result, err := s.Trade.Create(uid, req.CreateTradeData, req.Resource)
|
||||
if err != nil {
|
||||
slog.Error("创建交易失败", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "创建交易失败"})
|
||||
return core.NewServErr("处理购买产品信息失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(&TradeCreateResp{
|
||||
PayUrl: result.PaymentUrl,
|
||||
TradeNo: result.TradeNo,
|
||||
})
|
||||
return c.JSON(result)
|
||||
}
|
||||
|
||||
type TradeCreateReq struct {
|
||||
s.CreateTradeData
|
||||
Type m.TradeType `json:"type" validate:"required"`
|
||||
Resource *s.CreateResourceData `json:"resource,omitempty"`
|
||||
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
|
||||
}
|
||||
|
||||
type TradeCreateResp struct {
|
||||
PayUrl string `json:"pay_url"`
|
||||
TradeNo string `json:"trade_no"`
|
||||
*s.CreateTradeData
|
||||
Type m.TradeType `json:"type" validate:"required"`
|
||||
Resource *s.CreateResourceData `json:"resource,omitempty"`
|
||||
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
|
||||
}
|
||||
|
||||
// 完成订单
|
||||
func TradeComplete(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitUser()
|
||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -167,7 +152,7 @@ func TradeComplete(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 检查订单状态
|
||||
err = s.Trade.CompleteTrade(&req.ModifyTradeData)
|
||||
err = s.Trade.CompleteTrade(authCtx.User, &req.TradeRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,7 +161,7 @@ func TradeComplete(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
type TradeCompleteReq struct {
|
||||
s.ModifyTradeData
|
||||
s.TradeRef
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
@@ -194,7 +179,7 @@ func TradeCancel(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 取消交易
|
||||
err = s.Trade.CancelTrade(&req.ModifyTradeData, time.Now())
|
||||
err = s.Trade.CancelTrade(&req.TradeRef)
|
||||
if err != nil {
|
||||
slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"})
|
||||
@@ -204,7 +189,7 @@ func TradeCancel(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
type TradeCancelReq struct {
|
||||
s.ModifyTradeData
|
||||
s.TradeRef
|
||||
}
|
||||
|
||||
// 检查订单
|
||||
@@ -225,7 +210,7 @@ func TradeCheck(c *fiber.Ctx) error {
|
||||
interval := 5
|
||||
for range expire / interval {
|
||||
// 检查订单状态
|
||||
result, err := s.Trade.CheckTrade(&req.ModifyTradeData)
|
||||
result, err := s.Trade.CheckTrade(&req.TradeRef)
|
||||
if err != nil {
|
||||
slog.Error("检查订单状态失败", "trade_no", req.TradeNo, "error", err)
|
||||
return
|
||||
@@ -256,5 +241,5 @@ func TradeCheck(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
type TradeCheckReq struct {
|
||||
s.ModifyTradeData
|
||||
s.TradeRef
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user