Files
junhong_cmp_fiber/internal/handler/admin/order.go
邹进 eb9f12d160
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m34s
feat: 后台订单按平台、代理商、在线支付拆分列表与导出
- 新增 GET /orders/platform、/orders/agent、/orders/online,类别由路径固定,参数、响应、权限同原订单列表
- 新增导出场景 order_platform、order_agent、order_online,筛选与表头同 order
- 类别按钱从哪来划分:客户端下单为在线支付;后台代理钱包扣款(含平台代扣)为代理商;其余后台订单为平台
- 列表与导出共用 internal/query/ordercategory 的类别条件
- 企业账号访问订单列表、详情、购买预检改为无权限,修复其可看到全部订单的问题
- 原订单列表与 order 导出场景保留不变

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:53:24 +09:00

201 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package admin
import (
"strconv"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
orderService "github.com/break/junhong_cmp_fiber/internal/service/order"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/break/junhong_cmp_fiber/pkg/response"
"github.com/break/junhong_cmp_fiber/pkg/utils"
)
// OrderHandler 后台订单处理器
type OrderHandler struct {
service *orderService.Service
validator *validator.Validate
}
// NewOrderHandler 创建后台订单处理器
func NewOrderHandler(service *orderService.Service, validator *validator.Validate) *OrderHandler {
return &OrderHandler{service: service, validator: validator}
}
// Create 创建后台订单
// POST /api/admin/orders
func (h *OrderHandler) Create(c *fiber.Ctx) error {
var req dto.CreateAdminOrderRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
// 验证请求参数(payment_method 必须为 wallet 或 offline)
if err := h.validator.Struct(&req); err != nil {
return errors.New(errors.CodeInvalidParam)
}
ctx := c.UserContext()
userType := middleware.GetUserTypeFromContext(ctx)
shopID := middleware.GetShopIDFromContext(ctx)
// 线下支付仅限平台用户
if req.PaymentMethod == model.PaymentMethodOffline {
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
return errors.New(errors.CodeForbidden, "只有平台可以使用线下支付")
}
} else if req.PaymentMethod == model.PaymentMethodWallet {
if userType != constants.UserTypeAgent && userType != constants.UserTypePlatform && userType != constants.UserTypeSuperAdmin {
return errors.New(errors.CodeForbidden, "无权创建订单")
}
} else {
// 防御性分支:DTO 验证已限制,此处兜底
return errors.New(errors.CodeInvalidParam, "后台仅支持钱包支付和线下支付")
}
buyerType := ""
buyerID := uint(0)
if userType == constants.UserTypeAgent {
buyerType = model.BuyerTypeAgent
buyerID = shopID
}
order, err := h.service.CreateAdminOrder(ctx, &req, buyerType, buyerID)
if err != nil {
return err
}
return response.Success(c, order)
}
func (h *OrderHandler) Get(c *fiber.Ctx) error {
if err := rejectEnterpriseOrderAccess(c); err != nil {
return err
}
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
}
order, err := h.service.GetDetail(c.UserContext(), uint(id))
if err != nil {
return err
}
return response.Success(c, order)
}
// List 获取订单列表(不限类别)。
func (h *OrderHandler) List(c *fiber.Ctx) error {
return h.list(c, "")
}
// ListPlatform 获取平台订单列表:后台下单且不走代理钱包。
func (h *OrderHandler) ListPlatform(c *fiber.Ctx) error {
return h.list(c, constants.OrderCategoryPlatform)
}
// ListAgent 获取代理商订单列表:后台下单且从代理钱包扣款。
func (h *OrderHandler) ListAgent(c *fiber.Ctx) error {
return h.list(c, constants.OrderCategoryAgent)
}
// ListOnline 获取在线支付订单列表:客户端下单。
func (h *OrderHandler) ListOnline(c *fiber.Ctx) error {
return h.list(c, constants.OrderCategoryOnline)
}
// list 是订单列表的唯一实现:参数解析、时间范围与代理买家条件只有一份,
// 按类别拆分的接口只额外固定订单类别,权限与数据范围与原列表一致。
func (h *OrderHandler) list(c *fiber.Ctx, category string) error {
if err := rejectEnterpriseOrderAccess(c); err != nil {
return err
}
var req dto.OrderListRequest
if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
startTime, endTime, err := utils.ParseTimeRange(req.StartTime, req.EndTime)
if err != nil {
return err
}
ctx := c.UserContext()
userType := middleware.GetUserTypeFromContext(ctx)
shopID := middleware.GetShopIDFromContext(ctx)
var buyerType string
var buyerID uint
if userType == constants.UserTypeAgent {
buyerType = model.BuyerTypeAgent
buyerID = shopID
} else {
buyerType = ""
buyerID = 0
}
orders, err := h.service.List(ctx, &req, startTime, endTime, buyerType, buyerID, category)
if err != nil {
return err
}
return response.Success(c, orders)
}
func (h *OrderHandler) Cancel(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
}
ctx := c.UserContext()
userType := middleware.GetUserTypeFromContext(ctx)
shopID := middleware.GetShopIDFromContext(ctx)
if userType != constants.UserTypeAgent {
return errors.New(errors.CodeForbidden, "只有代理账号可以取消订单")
}
if err := h.service.Cancel(ctx, uint(id), model.BuyerTypeAgent, shopID); err != nil {
return err
}
return response.Success(c, nil)
}
// PurchaseCheck 代购订单预检接口
// 路由: POST /api/admin/orders/purchase-check
// 参数: order_type(订单类型), resource_id(资源ID), package_ids(套餐ID列表)
// 响应: 套餐总价、强充要求、实际支付金额等预检结果
func (h *OrderHandler) PurchaseCheck(c *fiber.Ctx) error {
if err := rejectEnterpriseOrderAccess(c); err != nil {
return err
}
var req dto.PurchaseCheckRequest
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
result, err := h.service.GetPurchaseCheck(c.UserContext(), &req)
if err != nil {
return err
}
return response.Success(c, result)
}
// rejectEnterpriseOrderAccess 拒绝企业账号访问后台订单接口:后台对企业账号不加订单数据范围,
// 放行会看到全部订单。企业账号的下单与取消已由各自接口拒绝。
func rejectEnterpriseOrderAccess(c *fiber.Ctx) error {
if middleware.GetUserTypeFromContext(c.UserContext()) == constants.UserTypeEnterprise {
return errors.New(errors.CodeForbidden, "企业账号无权访问订单")
}
return nil
}