新增单行文本编码器与结构化 GORM 日志,统一错误记录与请求日志策略,收紧日志文件权限并修复按天切分与压缩,支付回调参数脱敏,生产强制阿里云短信,RequestID 校验防注入,日志文案中文化。
210 lines
5.2 KiB
Go
210 lines
5.2 KiB
Go
package pickup
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"hfb_sys/backend/internal/auditlog"
|
|
"hfb_sys/backend/internal/middleware"
|
|
"hfb_sys/backend/pkg/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type Handler struct {
|
|
service *Service
|
|
}
|
|
|
|
func NewHandler(service *Service) *Handler {
|
|
return &Handler{service: service}
|
|
}
|
|
|
|
// Create 创建提号订单(管理员)
|
|
func (h *Handler) Create(c *gin.Context) {
|
|
adminID, ok := currentAdminID(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "缺少管理员上下文")
|
|
return
|
|
}
|
|
var req CreateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "参数格式错误")
|
|
return
|
|
}
|
|
item, err := h.service.Create(c.Request.Context(), req, adminID, auditMeta(c))
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, item)
|
|
}
|
|
|
|
// Complete 完成提号结算(管理员)
|
|
func (h *Handler) Complete(c *gin.Context) {
|
|
adminID, ok := currentAdminID(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "缺少管理员上下文")
|
|
return
|
|
}
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req CompleteRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "结算金额不正确")
|
|
return
|
|
}
|
|
item, err := h.service.Complete(c.Request.Context(), id, req, adminID, auditMeta(c))
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, item)
|
|
}
|
|
|
|
// UpdateProfit 修改提号中的线下利润(管理员)
|
|
func (h *Handler) UpdateProfit(c *gin.Context) {
|
|
adminID, ok := currentAdminID(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "缺少管理员上下文")
|
|
return
|
|
}
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req UpdateProfitRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "利润金额不正确")
|
|
return
|
|
}
|
|
item, err := h.service.UpdateProfit(c.Request.Context(), id, req, adminID, auditMeta(c))
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, item)
|
|
}
|
|
|
|
// Cancel 取消提号(管理员)
|
|
func (h *Handler) Cancel(c *gin.Context) {
|
|
adminID, ok := currentAdminID(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "缺少管理员上下文")
|
|
return
|
|
}
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req CancelRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "取消原因不能为空")
|
|
return
|
|
}
|
|
if err := h.service.Cancel(c.Request.Context(), id, req.Reason, adminID, auditMeta(c)); err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, gin.H{"cancelled": true})
|
|
}
|
|
|
|
// List 提号订单列表(管理员)
|
|
func (h *Handler) List(c *gin.Context) {
|
|
query := parseAdminQuery(c)
|
|
result, err := h.service.ListAdmin(c.Request.Context(), query)
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, result)
|
|
}
|
|
|
|
// ShopOptions 返回后台提号已使用的店铺名称。
|
|
func (h *Handler) ShopOptions(c *gin.Context) {
|
|
items, err := h.service.ListShopNames(c.Request.Context(), c.Query("platform"))
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, items)
|
|
}
|
|
|
|
// Detail 提号订单详情(管理员)
|
|
func (h *Handler) Detail(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
item, err := h.service.FindByID(c.Request.Context(), id)
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, item)
|
|
}
|
|
|
|
// AvailableListings 可提号的 listing 搜索(管理员)
|
|
func (h *Handler) AvailableListings(c *gin.Context) {
|
|
page, pageSize := parsePagination(c)
|
|
result, err := h.service.ListAvailableListings(c.Request.Context(), AvailableListingQuery{
|
|
Keyword: strings.TrimSpace(c.Query("keyword")),
|
|
SourceType: strings.TrimSpace(c.Query("source_type")),
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
})
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, result)
|
|
}
|
|
|
|
// ListForSeller 卖家自己的提号记录
|
|
func (h *Handler) ListForSeller(c *gin.Context) {
|
|
userID, ok := currentUserID(c)
|
|
if !ok {
|
|
response.Unauthorized(c, "缺少用户上下文")
|
|
return
|
|
}
|
|
query := parseSellerQuery(c)
|
|
result, err := h.service.ListForSeller(c.Request.Context(), userID, query)
|
|
if err != nil {
|
|
writePickupError(c, err)
|
|
return
|
|
}
|
|
response.OK(c, result)
|
|
}
|
|
|
|
func writePickupError(c *gin.Context, err error) {
|
|
response.RecordError(c, err)
|
|
switch {
|
|
case errors.Is(err, ErrDependencyUnavailable):
|
|
response.ServiceUnavailable(c, "数据库未连接")
|
|
case errors.Is(err, ErrListingUnavailable):
|
|
response.BadRequest(c, "账号不可用或已在交易中")
|
|
case errors.Is(err, ErrDuplicatePickup):
|
|
response.BadRequest(c, "该账号已有进行中的提号订单")
|
|
case errors.Is(err, ErrPickupNotFound):
|
|
response.Error(c, http.StatusNotFound, "pickup_not_found", "提号订单不存在")
|
|
case errors.Is(err, ErrPickupNotPickingUp):
|
|
response.BadRequest(c, "提号订单状态不允许该操作")
|
|
case errors.Is(err, ErrInvalidAmount):
|
|
response.BadRequest(c, "结算金额不正确")
|
|
case errors.Is(err, ErrInvalidProfit):
|
|
response.BadRequest(c, "利润金额不正确")
|
|
default:
|
|
response.Error(c, http.StatusInternalServerError, "pickup_error", err.Error())
|
|
}
|
|
}
|
|
|
|
func auditMeta(c *gin.Context) auditlog.Meta {
|
|
return auditlog.Meta{
|
|
IP: c.ClientIP(),
|
|
UserAgent: c.GetHeader("User-Agent"),
|
|
RequestID: middleware.GetRequestID(c),
|
|
}
|
|
}
|