Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 40s
feat(pay): 添加支付API基础结构 feat(miniapp): 创建支付测试小程序页面与配置 feat(wechatpay): 配置微信支付参数与证书 fix(guild): 修复成员列表查询条件 docs: 更新代码规范文档与需求文档 style: 统一前后端枚举显示与注释格式 refactor(admin): 重构用户奖励发放接口参数处理 test(title): 添加称号效果参数验证测试
319 lines
14 KiB
Go
319 lines
14 KiB
Go
package admin
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"bindbox-game/internal/code"
|
|
"bindbox-game/internal/pkg/core"
|
|
"bindbox-game/internal/pkg/validation"
|
|
"bindbox-game/internal/repository/mysql/model"
|
|
)
|
|
|
|
type listPayOrdersRequest struct {
|
|
Page int `form:"page"`
|
|
PageSize int `form:"page_size"`
|
|
Status *int32 `form:"status"`
|
|
SourceType *int32 `form:"source_type"`
|
|
UserID *int64 `form:"user_id"`
|
|
OrderNo string `form:"order_no"`
|
|
IsConsumed *int32 `form:"is_consumed"`
|
|
StartDate string `form:"start_date"`
|
|
EndDate string `form:"end_date"`
|
|
PayStart string `form:"pay_start"`
|
|
PayEnd string `form:"pay_end"`
|
|
}
|
|
|
|
type listPayOrdersResponse struct {
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
Total int64 `json:"total"`
|
|
List []*model.Orders `json:"list"`
|
|
}
|
|
|
|
func (h *handler) ListPayOrders() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
req := new(listPayOrdersRequest)
|
|
rsp := new(listPayOrdersResponse)
|
|
if err := ctx.ShouldBindForm(req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
if req.Page <= 0 {
|
|
req.Page = 1
|
|
}
|
|
if req.PageSize <= 0 {
|
|
req.PageSize = 20
|
|
}
|
|
if req.PageSize > 100 {
|
|
req.PageSize = 100
|
|
}
|
|
q := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB()
|
|
if req.Status != nil {
|
|
q = q.Where(h.readDB.Orders.Status.Eq(*req.Status))
|
|
}
|
|
if req.SourceType != nil {
|
|
q = q.Where(h.readDB.Orders.SourceType.Eq(*req.SourceType))
|
|
}
|
|
if req.UserID != nil {
|
|
q = q.Where(h.readDB.Orders.UserID.Eq(*req.UserID))
|
|
}
|
|
if req.OrderNo != "" {
|
|
q = q.Where(h.readDB.Orders.OrderNo.Eq(req.OrderNo))
|
|
}
|
|
if req.IsConsumed != nil {
|
|
q = q.Where(h.readDB.Orders.IsConsumed.Eq(*req.IsConsumed))
|
|
}
|
|
if req.StartDate != "" {
|
|
if t, err := time.Parse("2006-01-02", req.StartDate); err == nil {
|
|
q = q.Where(h.readDB.Orders.CreatedAt.Gte(t))
|
|
}
|
|
}
|
|
if req.EndDate != "" {
|
|
if t, err := time.Parse("2006-01-02", req.EndDate); err == nil {
|
|
t = t.Add(24 * time.Hour).Add(-time.Second)
|
|
q = q.Where(h.readDB.Orders.CreatedAt.Lte(t))
|
|
}
|
|
}
|
|
if req.PayStart != "" {
|
|
if t, err := time.Parse(time.RFC3339, req.PayStart); err == nil {
|
|
q = q.Where(h.readDB.Orders.PaidAt.Gte(t))
|
|
}
|
|
}
|
|
if req.PayEnd != "" {
|
|
if t, err := time.Parse(time.RFC3339, req.PayEnd); err == nil {
|
|
q = q.Where(h.readDB.Orders.PaidAt.Lte(t))
|
|
}
|
|
}
|
|
total, err := q.Count()
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21001, err.Error()))
|
|
return
|
|
}
|
|
rows, err := q.Order(h.readDB.Orders.ID.Desc()).Offset((req.Page-1)*req.PageSize).Limit(req.PageSize).Find()
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21002, err.Error()))
|
|
return
|
|
}
|
|
rsp.Page = req.Page
|
|
rsp.PageSize = req.PageSize
|
|
rsp.Total = total
|
|
rsp.List = rows
|
|
ctx.Payload(rsp)
|
|
}
|
|
}
|
|
|
|
type getPayOrderResponse struct {
|
|
Order *model.Orders `json:"order"`
|
|
Items []*model.OrderItems `json:"items"`
|
|
Shipments []*model.ShippingRecords `json:"shipments"`
|
|
Ledgers []*model.UserPointsLedger `json:"ledgers"`
|
|
User *model.Users `json:"user"`
|
|
Activity *struct{
|
|
ActivityID int64 `json:"activity_id"`
|
|
ActivityName string `json:"activity_name"`
|
|
IssueID int64 `json:"issue_id"`
|
|
IssueNumber string `json:"issue_number"`
|
|
IsWinner int32 `json:"is_winner"`
|
|
Level int32 `json:"level"`
|
|
RewardID int64 `json:"reward_id"`
|
|
RewardName string `json:"reward_name"`
|
|
ProductID int64 `json:"product_id"`
|
|
} `json:"activity"`
|
|
Payment *struct{
|
|
Status int32 `json:"status"`
|
|
ActualAmount int64 `json:"actual_amount"`
|
|
PaidAt string `json:"paid_at"`
|
|
PayPreorderID int64 `json:"pay_preorder_id"`
|
|
TransactionID string `json:"transaction_id"`
|
|
} `json:"payment"`
|
|
Refunds []*struct{
|
|
RefundNo string `json:"refund_no"`
|
|
Amount int64 `json:"amount"`
|
|
Status string `json:"status"`
|
|
Channel string `json:"channel"`
|
|
Reason string `json:"reason"`
|
|
CreatedAt string `json:"created_at"`
|
|
} `json:"refunds"`
|
|
RefundableAmount int64 `json:"refundable_amount"`
|
|
}
|
|
|
|
func (h *handler) GetPayOrderDetail() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
rsp := new(getPayOrderResponse)
|
|
orderNo := ctx.Param("order_no")
|
|
if orderNo == "" {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号"))
|
|
return
|
|
}
|
|
order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First()
|
|
if err != nil || order == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21003, "订单不存在"))
|
|
return
|
|
}
|
|
items, _ := h.readDB.OrderItems.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderItems.OrderID.Eq(order.ID)).Find()
|
|
shipments, _ := h.readDB.ShippingRecords.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ShippingRecords.OrderID.Eq(order.ID)).Find()
|
|
ledgers, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(orderNo)).Find()
|
|
user, _ := h.readDB.Users.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Users.ID.Eq(order.UserID)).First()
|
|
var activityID int64
|
|
var activityName string
|
|
var issueID int64
|
|
var issueNumber string
|
|
var isWinner int32
|
|
var level int32
|
|
var rewardID int64
|
|
var rewardName string
|
|
var rewardProductID int64
|
|
if order.SourceType == 2 {
|
|
drawLog, _ := h.readDB.ActivityDrawLogs.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityDrawLogs.OrderID.Eq(order.ID)).First()
|
|
if drawLog != nil {
|
|
isWinner = drawLog.IsWinner
|
|
level = drawLog.Level
|
|
rewardID = drawLog.RewardID
|
|
issueID = drawLog.IssueID
|
|
issue, _ := h.readDB.ActivityIssues.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityIssues.ID.Eq(issueID)).First()
|
|
if issue != nil {
|
|
issueNumber = issue.IssueNumber
|
|
activityID = issue.ActivityID
|
|
activity, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Activities.ID.Eq(activityID)).First()
|
|
if activity != nil {
|
|
activityName = activity.Name
|
|
}
|
|
}
|
|
if rewardID > 0 {
|
|
reward, _ := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.ID.Eq(rewardID)).First()
|
|
if reward != nil {
|
|
rewardName = reward.Name
|
|
rewardProductID = reward.ProductID
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rsp.Activity = &struct{ActivityID int64 `json:"activity_id"`; ActivityName string `json:"activity_name"`; IssueID int64 `json:"issue_id"`; IssueNumber string `json:"issue_number"`; IsWinner int32 `json:"is_winner"`; Level int32 `json:"level"`; RewardID int64 `json:"reward_id"`; RewardName string `json:"reward_name"`; ProductID int64 `json:"product_id"`}{
|
|
ActivityID: activityID,
|
|
ActivityName: activityName,
|
|
IssueID: issueID,
|
|
IssueNumber: issueNumber,
|
|
IsWinner: isWinner,
|
|
Level: level,
|
|
RewardID: rewardID,
|
|
RewardName: rewardName,
|
|
ProductID: rewardProductID,
|
|
}
|
|
if user != nil { rsp.User = user }
|
|
rsp.Payment = &struct{Status int32 `json:"status"`; ActualAmount int64 `json:"actual_amount"`; PaidAt string `json:"paid_at"`; PayPreorderID int64 `json:"pay_preorder_id"`; TransactionID string `json:"transaction_id"`}{
|
|
Status: order.Status,
|
|
ActualAmount: order.ActualAmount,
|
|
PaidAt: order.PaidAt.Format("2006-01-02 15:04:05"),
|
|
PayPreorderID: order.PayPreorderID,
|
|
TransactionID: "",
|
|
}
|
|
tx, _ := h.readDB.PaymentTransactions.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentTransactions.OrderNo.Eq(order.OrderNo)).Order(h.readDB.PaymentTransactions.ID.Desc()).First()
|
|
if tx != nil {
|
|
rsp.Payment.PaidAt = tx.SuccessTime.Format("2006-01-02 15:04:05")
|
|
rsp.Payment.TransactionID = tx.TransactionID
|
|
}
|
|
var refundedSum int64
|
|
var refunds []*struct{ RefundNo string `json:"refund_no"`; Amount int64 `json:"amount"`; Status string `json:"status"`; Channel string `json:"channel"`; Reason string `json:"reason"`; CreatedAt string `json:"created_at"` }
|
|
prList, _ := h.readDB.PaymentRefunds.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.PaymentRefunds.OrderNo.Eq(order.OrderNo)).Order(h.readDB.PaymentRefunds.ID.Desc()).Find()
|
|
for _, r := range prList {
|
|
refundedSum += r.AmountRefund
|
|
refunds = append(refunds, &struct{ RefundNo string `json:"refund_no"`; Amount int64 `json:"amount"`; Status string `json:"status"`; Channel string `json:"channel"`; Reason string `json:"reason"`; CreatedAt string `json:"created_at"` }{ RefundNo: r.RefundNo, Amount: r.AmountRefund, Status: r.Status, Channel: r.Channel, Reason: r.Reason, CreatedAt: r.CreatedAt.Format("2006-01-02 15:04:05") })
|
|
}
|
|
var refundable int64 = order.ActualAmount - refundedSum
|
|
if refundable < 0 { refundable = 0 }
|
|
rsp.Refunds = refunds
|
|
rsp.RefundableAmount = refundable
|
|
rsp.Order = order
|
|
rsp.Items = items
|
|
rsp.Shipments = shipments
|
|
rsp.Ledgers = ledgers
|
|
ctx.Payload(rsp)
|
|
}
|
|
}
|
|
|
|
type updateOrderRemarkRequest struct {
|
|
Remark string `json:"remark"`
|
|
}
|
|
|
|
type simpleResponse struct {
|
|
Success bool `json:"success"`
|
|
}
|
|
|
|
func (h *handler) UpdateOrderRemark() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
req := new(updateOrderRemarkRequest)
|
|
rsp := new(simpleResponse)
|
|
if err := ctx.ShouldBindJSON(req); err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
|
|
return
|
|
}
|
|
orderNo := ctx.Param("order_no")
|
|
if orderNo == "" {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号"))
|
|
return
|
|
}
|
|
order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First()
|
|
if err != nil || order == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21004, "订单不存在"))
|
|
return
|
|
}
|
|
_, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{
|
|
h.writeDB.Orders.Remark.ColumnName().String(): req.Remark,
|
|
h.writeDB.Orders.UpdatedAt.ColumnName().String(): time.Now(),
|
|
})
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21005, err.Error()))
|
|
return
|
|
}
|
|
rsp.Success = true
|
|
ctx.Payload(rsp)
|
|
}
|
|
}
|
|
|
|
func (h *handler) CancelOrder() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
orderNo := ctx.Param("order_no")
|
|
if orderNo == "" {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号"))
|
|
return
|
|
}
|
|
order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First()
|
|
if err != nil || order == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21006, "订单不存在"))
|
|
return
|
|
}
|
|
_, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID), h.readDB.Orders.Status.Eq(1)).Updates(map[string]any{
|
|
h.readDB.Orders.Status.ColumnName().String(): 3,
|
|
h.readDB.Orders.CancelledAt.ColumnName().String(): time.Now(),
|
|
})
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21007, err.Error()))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleResponse{Success: true})
|
|
}
|
|
}
|
|
|
|
func (h *handler) ConsumeOrder() core.HandlerFunc {
|
|
return func(ctx core.Context) {
|
|
orderNo := ctx.Param("order_no")
|
|
if orderNo == "" {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "缺少订单号"))
|
|
return
|
|
}
|
|
order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Orders.OrderNo.Eq(orderNo)).First()
|
|
if err != nil || order == nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21008, "订单不存在"))
|
|
return
|
|
}
|
|
_, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID), h.readDB.Orders.Status.Eq(2)).Updates(map[string]any{
|
|
h.readDB.Orders.IsConsumed.ColumnName().String(): 1,
|
|
})
|
|
if err != nil {
|
|
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21009, err.Error()))
|
|
return
|
|
}
|
|
ctx.Payload(&simpleResponse{Success: true})
|
|
}
|
|
} |