All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m34s
- 新增 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>
201 lines
6.1 KiB
Go
201 lines
6.1 KiB
Go
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
|
||
}
|