refactor(handler): 按业务域拆分 merchant.go(847 → 最大 182 行)
纯文件级移动,无行为变更: - merchant.go 结构体、构造函数与跨域共用辅助(mapKeys/pageParams) - merchant_product.go 商品目录与分配 - merchant_order.go 订单、人工/测试下单与发货链接 - merchant_wallet.go 钱包、积分流水与管理员调账 - merchant_api_client.go API 客户端与回调订阅 - merchant_member.go 成员与角色 - merchant_platform.go 平台侧租户管理 校验:与 HEAD 的声明行 diff 为空,MerchantHandler 方法数 41 → 41。
This commit is contained in:
@@ -3,11 +3,8 @@ package handler
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/model"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
@@ -55,791 +52,6 @@ func mapKeys(values map[string]struct{}) []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListProducts(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.merchantSvc.ListMerchantProductsWithOptions(middleware.GetMerchantID(c), page, size, service.ListMerchantProductsOptions{
|
||||
Status: strings.TrimSpace(c.Query("status")),
|
||||
Keyword: strings.TrimSpace(c.Query("keyword")),
|
||||
})
|
||||
if err != nil {
|
||||
if err.Error() == "无效的商品状态" {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type merchantProductStatusReq struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
|
||||
// UpdateProductStatus PATCH /api/merchant/products/:id/status
|
||||
// 商户仅能上架/下架商品,价格、成本等配置由平台管理员维护。
|
||||
// UpdateProduct PATCH /api/merchant/products/:id — 编辑商品(显示名称、售价、成本、库存、状态)。
|
||||
// 严格风控门禁:仅平台超级管理员(model.RoleAdmin)拥有修改商品价格、成本、库存与名称的权限,普通分销商严禁修改。
|
||||
func (h *MerchantHandler) UpdateProduct(c *gin.Context) {
|
||||
if middleware.GetRole(c) != model.RoleAdmin {
|
||||
response.Forbidden(c, "权限不足:仅平台超级管理员可编辑商品价格、成本与库存")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
var req merchantProductUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(middleware.GetMerchantID(c), uint(id), service.UpdateMerchantProductInput{
|
||||
DisplayName: req.DisplayName,
|
||||
PriceAmount: req.PriceAmount,
|
||||
CostAmount: req.CostAmount,
|
||||
Stock: req.Stock,
|
||||
Status: req.Status,
|
||||
FulfillmentConfig: req.FulfillmentConfig,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateProductStatus(c *gin.Context) {
|
||||
var req merchantProductStatusReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:status 必填")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(middleware.GetMerchantID(c), uint(id), service.UpdateMerchantProductInput{
|
||||
Status: &req.Status,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// DeleteProduct DELETE /api/merchant/products/:id — 仅已下架可删(软删除)。
|
||||
func (h *MerchantHandler) DeleteProduct(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.DeleteMerchantProduct(middleware.GetMerchantID(c), uint(id), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListOrders(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
orderSource := strings.TrimSpace(c.Query("order_source"))
|
||||
list, total, err := h.fulfillmentSvc.ListOrders(middleware.GetMerchantID(c), page, size, c.Query("order_status"), orderSource, c.Query("keyword"))
|
||||
if err != nil {
|
||||
if err.Error() == "无效的订单来源" || err.Error() == "无效的订单状态" {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
// ListManualOrderProducts returns active products for order creation without granting product-management access.
|
||||
func (h *MerchantHandler) ListManualOrderProducts(c *gin.Context) {
|
||||
list, _, err := h.merchantSvc.ListMerchantProductsForClient(middleware.GetMerchantID(c), 1, 100)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
type merchantManualOrderReq struct {
|
||||
ClientOrderNo string `json:"client_order_no" binding:"required,max=96"`
|
||||
SKU string `json:"sku" binding:"required,max=96"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
BuyerReference string `json:"buyer_reference" binding:"max=128"`
|
||||
GameAccount string `json:"game_account" binding:"max=128"`
|
||||
Note string `json:"note" binding:"max=512"`
|
||||
}
|
||||
|
||||
// CreateManualOrder 在商户后台创建真实订单;扣款、库存和回调与开放 API 下单保持一致。
|
||||
func (h *MerchantHandler) CreateManualOrder(c *gin.Context) {
|
||||
var req merchantManualOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:商户单号与商品必填")
|
||||
return
|
||||
}
|
||||
data := map[string]string{}
|
||||
if gameAccount := strings.TrimSpace(req.GameAccount); gameAccount != "" {
|
||||
data["game_account"] = gameAccount
|
||||
}
|
||||
if note := strings.TrimSpace(req.Note); note != "" {
|
||||
data["manual_note"] = note
|
||||
}
|
||||
result, err := h.fulfillmentSvc.CreateManualOrder(service.CreateManualOrderInput{
|
||||
MerchantID: middleware.GetMerchantID(c),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
ClientOrderNo: req.ClientOrderNo,
|
||||
SKU: req.SKU,
|
||||
Quantity: req.Quantity,
|
||||
BuyerReference: req.BuyerReference,
|
||||
RequestData: data,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
canShip, cannotShipReason := service.CanFulfill(result.Order)
|
||||
response.OK(c, gin.H{
|
||||
"order": result.Order,
|
||||
"idempotent": result.Idempotent,
|
||||
"can_ship": canShip,
|
||||
"cannot_ship_reason": cannotShipReason,
|
||||
})
|
||||
}
|
||||
|
||||
type merchantTestOrderReq struct {
|
||||
SKU string `json:"sku" binding:"required"`
|
||||
BuyerReference string `json:"buyer_reference"`
|
||||
Note string `json:"note"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateTestOrder(c *gin.Context) {
|
||||
var req merchantTestOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:sku 必填")
|
||||
return
|
||||
}
|
||||
order, err := h.fulfillmentSvc.CreateTestOrder(service.CreateTestOrderInput{
|
||||
MerchantID: middleware.GetMerchantID(c),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
SKU: req.SKU,
|
||||
BuyerReference: req.BuyerReference,
|
||||
Note: req.Note,
|
||||
OrderStatus: req.OrderStatus,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
canShip, cannotShipReason := service.CanFulfill(order)
|
||||
response.OK(c, gin.H{
|
||||
"order": order,
|
||||
"can_ship": canShip,
|
||||
"cannot_ship_reason": cannotShipReason,
|
||||
})
|
||||
}
|
||||
|
||||
type merchantCancelOrderReq struct {
|
||||
Reason string `json:"reason" binding:"required,max=512"`
|
||||
}
|
||||
|
||||
// CancelOrder 商户后台取消订单:全额退还积分并回补库存,取消后订单不可再发货。
|
||||
func (h *MerchantHandler) CancelOrder(c *gin.Context) {
|
||||
var req merchantCancelOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:取消原因必填且最长 512 字")
|
||||
return
|
||||
}
|
||||
order, err := h.fulfillmentSvc.CancelOrderByUser(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no"), req.Reason)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{
|
||||
"order": order,
|
||||
"refund_amount": order.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) GetDeliveryLink(c *gin.Context) {
|
||||
link, err := h.deliverySvc.GetOrCreateDeliveryLink(middleware.GetMerchantID(c), c.Param("order_no"), requestBaseURL(c))
|
||||
if err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, link)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RevokeDeliveryLink(c *gin.Context) {
|
||||
if err := h.deliverySvc.RevokeDeliveryLink(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no")); err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RestoreDeliveryLink(c *gin.Context) {
|
||||
link, err := h.deliverySvc.RestoreDeliveryLink(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no"), requestBaseURL(c))
|
||||
if err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, link)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) GetWallet(c *gin.Context) {
|
||||
wallet, err := h.fulfillmentSvc.GetWallet(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListWalletLedger(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
referenceNo := c.Query("reference_no")
|
||||
entryType := c.Query("type")
|
||||
list, total, err := h.fulfillmentSvc.ListWalletLedger(middleware.GetMerchantID(c), page, size, referenceNo, entryType)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
// AdminGetWallet 供平台管理员查看指定商户的钱包。
|
||||
func (h *MerchantHandler) AdminGetWallet(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
wallet, err := h.fulfillmentSvc.GetWallet(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
|
||||
// AdminListWalletLedger 供平台管理员查看指定商户的积分流水。
|
||||
func (h *MerchantHandler) AdminListWalletLedger(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.fulfillmentSvc.ListWalletLedger(uint(id), page, size, c.Query("reference_no"), c.Query("type"))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type walletAdjustReq struct {
|
||||
Amount int64 `json:"amount" binding:"required"`
|
||||
IdempotencyKey string `json:"idempotency_key" binding:"required"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
// AdminAdjustWallet 供平台管理员手动调整指定商户的积分,商户侧无任何调账入口。
|
||||
func (h *MerchantHandler) AdminAdjustWallet(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
var req walletAdjustReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:amount 与 idempotency_key 必填")
|
||||
return
|
||||
}
|
||||
wallet, err := h.fulfillmentSvc.AdjustWallet(service.WalletAdjustInput{
|
||||
MerchantID: uint(id),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
Amount: req.Amount,
|
||||
IdempotencyKey: req.IdempotencyKey,
|
||||
Note: req.Note,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.rechargeSvc.CheckLowBalanceAndNotify(uint(id))
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListAPIClients(c *gin.Context) {
|
||||
clients, err := h.merchantSvc.ListAPIClients(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, clients)
|
||||
}
|
||||
|
||||
type apiClientReq struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Scopes string `json:"scopes" binding:"required"`
|
||||
SignatureVersion string `json:"signature_version"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateAPIClient(c *gin.Context) {
|
||||
var req apiClientReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:name 与 scopes 必填")
|
||||
return
|
||||
}
|
||||
var expiresAt *time.Time
|
||||
if req.ExpiresAt != "" {
|
||||
value, err := time.Parse(time.RFC3339, req.ExpiresAt)
|
||||
if err != nil {
|
||||
response.BadRequest(c, "expires_at 必须是 RFC3339 时间")
|
||||
return
|
||||
}
|
||||
expiresAt = &value
|
||||
}
|
||||
credential, err := h.merchantSvc.CreateAPIClient(middleware.GetMerchantID(c), service.CreateAPIClientInput{
|
||||
Name: req.Name,
|
||||
Scopes: req.Scopes,
|
||||
SignatureVersion: req.SignatureVersion,
|
||||
ExpiresAt: expiresAt,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, credential)
|
||||
}
|
||||
|
||||
type statusReq struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateAPIClientStatus(c *gin.Context) {
|
||||
var req statusReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.UpdateAPIClientStatus(middleware.GetMerchantID(c), uint(id), req.Status, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// DeleteAPIClient DELETE /api/merchant/api-clients/:id
|
||||
func (h *MerchantHandler) DeleteAPIClient(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.DeleteAPIClient(middleware.GetMerchantID(c), uint(id), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListCallbacks(c *gin.Context) {
|
||||
subscription, err := h.callbackSvc.GetSubscription(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, subscription)
|
||||
}
|
||||
|
||||
type callbackReq struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url" binding:"required"`
|
||||
Events string `json:"events" binding:"required"`
|
||||
Status string `json:"status"`
|
||||
RotateSecret bool `json:"rotate_secret"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateCallback(c *gin.Context) {
|
||||
var req callbackReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:url、events 必填")
|
||||
return
|
||||
}
|
||||
credential, err := h.callbackSvc.CreateSubscription(middleware.GetMerchantID(c), service.CreateCallbackInput{
|
||||
Name: req.Name,
|
||||
URL: req.URL,
|
||||
Events: req.Events,
|
||||
Status: req.Status,
|
||||
RotateSecret: req.RotateSecret,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, credential)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListMembers(c *gin.Context) {
|
||||
members, err := h.merchantSvc.ListMembers(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, members)
|
||||
}
|
||||
|
||||
type addMemberReq struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Nickname string `json:"nickname"`
|
||||
Role string `json:"role" binding:"required"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) AddCurrentMerchantMember(c *gin.Context) {
|
||||
var req addMemberReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色必填")
|
||||
return
|
||||
}
|
||||
member, err := h.merchantSvc.AddMember(middleware.GetMerchantID(c), service.AddMemberInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Nickname: req.Nickname,
|
||||
Role: req.Role,
|
||||
IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
type updateMemberReq struct {
|
||||
Role string `json:"role" binding:"required"`
|
||||
Status int `json:"status"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateCurrentMerchantMember(c *gin.Context) {
|
||||
memberID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req updateMemberReq
|
||||
if memberID == 0 || c.ShouldBindJSON(&req) != nil {
|
||||
response.BadRequest(c, "参数错误:角色必填")
|
||||
return
|
||||
}
|
||||
member, err := h.merchantSvc.UpdateMember(middleware.GetMerchantID(c), uint(memberID), service.UpdateMemberInput{
|
||||
Role: req.Role, Status: req.Status, IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RemoveCurrentMerchantMember(c *gin.Context) {
|
||||
memberID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if memberID == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.RemoveMember(middleware.GetMerchantID(c), uint(memberID), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListRoles(c *gin.Context) {
|
||||
roles, err := h.merchantSvc.ListRoles(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, roles)
|
||||
}
|
||||
|
||||
type createMerchantRoleReq struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateRole(c *gin.Context) {
|
||||
var req createMerchantRoleReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色编码和名称必填")
|
||||
return
|
||||
}
|
||||
role, err := h.merchantSvc.CreateRole(middleware.GetMerchantID(c), service.MerchantRoleInput{
|
||||
Code: req.Code, Name: req.Name, Permissions: req.Permissions,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateRole(c *gin.Context) {
|
||||
roleID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req createMerchantRoleReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色名称必填")
|
||||
return
|
||||
}
|
||||
role, err := h.merchantSvc.UpdateRole(middleware.GetMerchantID(c), uint(roleID), service.MerchantRoleInput{
|
||||
Name: req.Name, Permissions: req.Permissions,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListPlatformMerchants(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.merchantSvc.ListMerchants(page, size)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
// ListProductCatalog 返回自营商户的全部可售商品,作为平台默认商品目录供分配。
|
||||
func (h *MerchantHandler) ListProductCatalog(c *gin.Context) {
|
||||
list, err := h.merchantSvc.ListProductCatalog()
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
// ListMerchantProductsByAdmin 供平台管理员查看指定商户的可售商品。
|
||||
func (h *MerchantHandler) ListMerchantProductsByAdmin(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
list, err := h.merchantSvc.ListMerchantProductsByAdmin(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
type merchantProductUpdateReq struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
PriceAmount *int64 `json:"price_amount"`
|
||||
CostAmount *int64 `json:"cost_amount"`
|
||||
Stock *int64 `json:"stock"`
|
||||
Status *string `json:"status"`
|
||||
FulfillmentConfig *string `json:"fulfillment_config"`
|
||||
}
|
||||
|
||||
// AdminUpdateMerchantProduct 供平台管理员编辑指定商户的商品(价格、成本、库存、状态等)。
|
||||
func (h *MerchantHandler) AdminUpdateMerchantProduct(c *gin.Context) {
|
||||
merchantID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
productID, _ := strconv.ParseUint(c.Param("pid"), 10, 64)
|
||||
if merchantID == 0 || productID == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
var req merchantProductUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(uint(merchantID), uint(productID), service.UpdateMerchantProductInput{
|
||||
DisplayName: req.DisplayName,
|
||||
PriceAmount: req.PriceAmount,
|
||||
CostAmount: req.CostAmount,
|
||||
Stock: req.Stock,
|
||||
Status: req.Status,
|
||||
FulfillmentConfig: req.FulfillmentConfig,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
type assignProductsReq struct {
|
||||
CatalogIDs []uint `json:"catalog_ids"`
|
||||
}
|
||||
|
||||
// AssignProducts 按商品目录 ID 批量同步商户的可售商品。
|
||||
func (h *MerchantHandler) AssignProducts(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req assignProductsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
count, err := h.merchantSvc.AssignProducts(uint(id), service.AssignProductsInput{
|
||||
CatalogIDs: req.CatalogIDs,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"assigned": count})
|
||||
}
|
||||
|
||||
type createMerchantReq struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
ContactName string `json:"contact_name"`
|
||||
ContactInfo string `json:"contact_info"`
|
||||
OwnerUserID uint `json:"owner_user_id"`
|
||||
OwnerUsername string `json:"owner_username"`
|
||||
OwnerPassword string `json:"owner_password"`
|
||||
OwnerNickname string `json:"owner_nickname"`
|
||||
Features string `json:"features"`
|
||||
FeeType string `json:"fee_type"`
|
||||
FeeRateBP int64 `json:"fee_rate_bp"`
|
||||
FeeFixedAmount int64 `json:"fee_fixed_amount"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateMerchant(c *gin.Context) {
|
||||
var req createMerchantReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:code、name 必填")
|
||||
return
|
||||
}
|
||||
merchant, err := h.merchantSvc.CreateMerchant(service.CreateMerchantInput{
|
||||
Code: req.Code,
|
||||
Name: req.Name,
|
||||
ContactName: req.ContactName,
|
||||
ContactInfo: req.ContactInfo,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
OwnerUsername: req.OwnerUsername,
|
||||
OwnerPassword: req.OwnerPassword,
|
||||
OwnerNickname: req.OwnerNickname,
|
||||
Features: req.Features,
|
||||
FeeType: req.FeeType,
|
||||
FeeRateBP: req.FeeRateBP,
|
||||
FeeFixedAmount: req.FeeFixedAmount,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, merchant)
|
||||
}
|
||||
|
||||
type updateMerchantSettingsReq struct {
|
||||
Name *string `json:"name"`
|
||||
Status *string `json:"status"`
|
||||
ContactName *string `json:"contact_name"`
|
||||
ContactInfo *string `json:"contact_info"`
|
||||
Features *string `json:"features"`
|
||||
FeeType *string `json:"fee_type"`
|
||||
FeeRateBP *int64 `json:"fee_rate_bp"`
|
||||
FeeFixedAmount *int64 `json:"fee_fixed_amount"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateMerchantSettings(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req updateMerchantSettingsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantSettings(uint(id), service.UpdateMerchantSettingsInput{
|
||||
Name: req.Name,
|
||||
Status: req.Status,
|
||||
ContactName: req.ContactName,
|
||||
ContactInfo: req.ContactInfo,
|
||||
Features: req.Features,
|
||||
FeeType: req.FeeType,
|
||||
FeeRateBP: req.FeeRateBP,
|
||||
FeeFixedAmount: req.FeeFixedAmount,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) AddPlatformMerchantMember(c *gin.Context) {
|
||||
var req addMemberReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:user_id 与 role 必填")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
member, err := h.merchantSvc.AddMember(uint(id), service.AddMemberInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Nickname: req.Nickname,
|
||||
Role: req.Role,
|
||||
IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
// AdminGetMerchantAccount 平台管理员查看指定商户的负责人登录账号。
|
||||
func (h *MerchantHandler) AdminGetMerchantAccount(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
accounts, err := h.merchantSvc.ListOwnerAccounts(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, accounts)
|
||||
}
|
||||
|
||||
type adminUpdateMerchantAccountReq struct {
|
||||
UserID uint `json:"user_id" binding:"required"`
|
||||
Username string `json:"username" binding:"required,min=3,max=64"`
|
||||
Nickname string `json:"nickname" binding:"max=64"`
|
||||
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
|
||||
// AdminUpdateMerchantAccount 平台管理员维护指定商户的负责人登录账号:
|
||||
// 修改用户名/昵称、重置密码、启用停用;密码留空表示不重置。
|
||||
func (h *MerchantHandler) AdminUpdateMerchantAccount(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req adminUpdateMerchantAccountReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:用户名必填(3-64 位),密码至少 6 位")
|
||||
return
|
||||
}
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
account, err := h.merchantSvc.UpdateMerchantOwnerAccount(uint(id), service.UpdateMerchantOwnerAccountInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Nickname: req.Nickname,
|
||||
Password: req.Password,
|
||||
Status: req.Status,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, account)
|
||||
}
|
||||
|
||||
func pageParams(c *gin.Context) (int, int) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) ListAPIClients(c *gin.Context) {
|
||||
clients, err := h.merchantSvc.ListAPIClients(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, clients)
|
||||
}
|
||||
|
||||
type apiClientReq struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Scopes string `json:"scopes" binding:"required"`
|
||||
SignatureVersion string `json:"signature_version"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateAPIClient(c *gin.Context) {
|
||||
var req apiClientReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:name 与 scopes 必填")
|
||||
return
|
||||
}
|
||||
var expiresAt *time.Time
|
||||
if req.ExpiresAt != "" {
|
||||
value, err := time.Parse(time.RFC3339, req.ExpiresAt)
|
||||
if err != nil {
|
||||
response.BadRequest(c, "expires_at 必须是 RFC3339 时间")
|
||||
return
|
||||
}
|
||||
expiresAt = &value
|
||||
}
|
||||
credential, err := h.merchantSvc.CreateAPIClient(middleware.GetMerchantID(c), service.CreateAPIClientInput{
|
||||
Name: req.Name,
|
||||
Scopes: req.Scopes,
|
||||
SignatureVersion: req.SignatureVersion,
|
||||
ExpiresAt: expiresAt,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, credential)
|
||||
}
|
||||
|
||||
type statusReq struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateAPIClientStatus(c *gin.Context) {
|
||||
var req statusReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.UpdateAPIClientStatus(middleware.GetMerchantID(c), uint(id), req.Status, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// DeleteAPIClient DELETE /api/merchant/api-clients/:id
|
||||
func (h *MerchantHandler) DeleteAPIClient(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.DeleteAPIClient(middleware.GetMerchantID(c), uint(id), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListCallbacks(c *gin.Context) {
|
||||
subscription, err := h.callbackSvc.GetSubscription(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, subscription)
|
||||
}
|
||||
|
||||
type callbackReq struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url" binding:"required"`
|
||||
Events string `json:"events" binding:"required"`
|
||||
Status string `json:"status"`
|
||||
RotateSecret bool `json:"rotate_secret"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateCallback(c *gin.Context) {
|
||||
var req callbackReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:url、events 必填")
|
||||
return
|
||||
}
|
||||
credential, err := h.callbackSvc.CreateSubscription(middleware.GetMerchantID(c), service.CreateCallbackInput{
|
||||
Name: req.Name,
|
||||
URL: req.URL,
|
||||
Events: req.Events,
|
||||
Status: req.Status,
|
||||
RotateSecret: req.RotateSecret,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, credential)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) ListMembers(c *gin.Context) {
|
||||
members, err := h.merchantSvc.ListMembers(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, members)
|
||||
}
|
||||
|
||||
type addMemberReq struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Nickname string `json:"nickname"`
|
||||
Role string `json:"role" binding:"required"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) AddCurrentMerchantMember(c *gin.Context) {
|
||||
var req addMemberReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色必填")
|
||||
return
|
||||
}
|
||||
member, err := h.merchantSvc.AddMember(middleware.GetMerchantID(c), service.AddMemberInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Nickname: req.Nickname,
|
||||
Role: req.Role,
|
||||
IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
type updateMemberReq struct {
|
||||
Role string `json:"role" binding:"required"`
|
||||
Status int `json:"status"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateCurrentMerchantMember(c *gin.Context) {
|
||||
memberID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req updateMemberReq
|
||||
if memberID == 0 || c.ShouldBindJSON(&req) != nil {
|
||||
response.BadRequest(c, "参数错误:角色必填")
|
||||
return
|
||||
}
|
||||
member, err := h.merchantSvc.UpdateMember(middleware.GetMerchantID(c), uint(memberID), service.UpdateMemberInput{
|
||||
Role: req.Role, Status: req.Status, IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RemoveCurrentMerchantMember(c *gin.Context) {
|
||||
memberID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if memberID == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.RemoveMember(middleware.GetMerchantID(c), uint(memberID), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListRoles(c *gin.Context) {
|
||||
roles, err := h.merchantSvc.ListRoles(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, roles)
|
||||
}
|
||||
|
||||
type createMerchantRoleReq struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateRole(c *gin.Context) {
|
||||
var req createMerchantRoleReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色编码和名称必填")
|
||||
return
|
||||
}
|
||||
role, err := h.merchantSvc.CreateRole(middleware.GetMerchantID(c), service.MerchantRoleInput{
|
||||
Code: req.Code, Name: req.Name, Permissions: req.Permissions,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateRole(c *gin.Context) {
|
||||
roleID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req createMerchantRoleReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:角色名称必填")
|
||||
return
|
||||
}
|
||||
role, err := h.merchantSvc.UpdateRole(middleware.GetMerchantID(c), uint(roleID), service.MerchantRoleInput{
|
||||
Name: req.Name, Permissions: req.Permissions,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, role)
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) ListOrders(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
orderSource := strings.TrimSpace(c.Query("order_source"))
|
||||
list, total, err := h.fulfillmentSvc.ListOrders(middleware.GetMerchantID(c), page, size, c.Query("order_status"), orderSource, c.Query("keyword"))
|
||||
if err != nil {
|
||||
if err.Error() == "无效的订单来源" || err.Error() == "无效的订单状态" {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type merchantManualOrderReq struct {
|
||||
ClientOrderNo string `json:"client_order_no" binding:"required,max=96"`
|
||||
SKU string `json:"sku" binding:"required,max=96"`
|
||||
Quantity int64 `json:"quantity"`
|
||||
BuyerReference string `json:"buyer_reference" binding:"max=128"`
|
||||
GameAccount string `json:"game_account" binding:"max=128"`
|
||||
Note string `json:"note" binding:"max=512"`
|
||||
}
|
||||
|
||||
// CreateManualOrder 在商户后台创建真实订单;扣款、库存和回调与开放 API 下单保持一致。
|
||||
func (h *MerchantHandler) CreateManualOrder(c *gin.Context) {
|
||||
var req merchantManualOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:商户单号与商品必填")
|
||||
return
|
||||
}
|
||||
data := map[string]string{}
|
||||
if gameAccount := strings.TrimSpace(req.GameAccount); gameAccount != "" {
|
||||
data["game_account"] = gameAccount
|
||||
}
|
||||
if note := strings.TrimSpace(req.Note); note != "" {
|
||||
data["manual_note"] = note
|
||||
}
|
||||
result, err := h.fulfillmentSvc.CreateManualOrder(service.CreateManualOrderInput{
|
||||
MerchantID: middleware.GetMerchantID(c),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
ClientOrderNo: req.ClientOrderNo,
|
||||
SKU: req.SKU,
|
||||
Quantity: req.Quantity,
|
||||
BuyerReference: req.BuyerReference,
|
||||
RequestData: data,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
canShip, cannotShipReason := service.CanFulfill(result.Order)
|
||||
response.OK(c, gin.H{
|
||||
"order": result.Order,
|
||||
"idempotent": result.Idempotent,
|
||||
"can_ship": canShip,
|
||||
"cannot_ship_reason": cannotShipReason,
|
||||
})
|
||||
}
|
||||
|
||||
type merchantTestOrderReq struct {
|
||||
SKU string `json:"sku" binding:"required"`
|
||||
BuyerReference string `json:"buyer_reference"`
|
||||
Note string `json:"note"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateTestOrder(c *gin.Context) {
|
||||
var req merchantTestOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:sku 必填")
|
||||
return
|
||||
}
|
||||
order, err := h.fulfillmentSvc.CreateTestOrder(service.CreateTestOrderInput{
|
||||
MerchantID: middleware.GetMerchantID(c),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
SKU: req.SKU,
|
||||
BuyerReference: req.BuyerReference,
|
||||
Note: req.Note,
|
||||
OrderStatus: req.OrderStatus,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
canShip, cannotShipReason := service.CanFulfill(order)
|
||||
response.OK(c, gin.H{
|
||||
"order": order,
|
||||
"can_ship": canShip,
|
||||
"cannot_ship_reason": cannotShipReason,
|
||||
})
|
||||
}
|
||||
|
||||
type merchantCancelOrderReq struct {
|
||||
Reason string `json:"reason" binding:"required,max=512"`
|
||||
}
|
||||
|
||||
// CancelOrder 商户后台取消订单:全额退还积分并回补库存,取消后订单不可再发货。
|
||||
func (h *MerchantHandler) CancelOrder(c *gin.Context) {
|
||||
var req merchantCancelOrderReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:取消原因必填且最长 512 字")
|
||||
return
|
||||
}
|
||||
order, err := h.fulfillmentSvc.CancelOrderByUser(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no"), req.Reason)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{
|
||||
"order": order,
|
||||
"refund_amount": order.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) GetDeliveryLink(c *gin.Context) {
|
||||
link, err := h.deliverySvc.GetOrCreateDeliveryLink(middleware.GetMerchantID(c), c.Param("order_no"), requestBaseURL(c))
|
||||
if err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, link)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RevokeDeliveryLink(c *gin.Context) {
|
||||
if err := h.deliverySvc.RevokeDeliveryLink(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no")); err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) RestoreDeliveryLink(c *gin.Context) {
|
||||
link, err := h.deliverySvc.RestoreDeliveryLink(middleware.GetMerchantID(c), middleware.GetUserID(c), c.Param("order_no"), requestBaseURL(c))
|
||||
if err != nil {
|
||||
writeDeliveryError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, link)
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) ListPlatformMerchants(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.merchantSvc.ListMerchants(page, size)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type createMerchantReq struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
ContactName string `json:"contact_name"`
|
||||
ContactInfo string `json:"contact_info"`
|
||||
OwnerUserID uint `json:"owner_user_id"`
|
||||
OwnerUsername string `json:"owner_username"`
|
||||
OwnerPassword string `json:"owner_password"`
|
||||
OwnerNickname string `json:"owner_nickname"`
|
||||
Features string `json:"features"`
|
||||
FeeType string `json:"fee_type"`
|
||||
FeeRateBP int64 `json:"fee_rate_bp"`
|
||||
FeeFixedAmount int64 `json:"fee_fixed_amount"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) CreateMerchant(c *gin.Context) {
|
||||
var req createMerchantReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:code、name 必填")
|
||||
return
|
||||
}
|
||||
merchant, err := h.merchantSvc.CreateMerchant(service.CreateMerchantInput{
|
||||
Code: req.Code,
|
||||
Name: req.Name,
|
||||
ContactName: req.ContactName,
|
||||
ContactInfo: req.ContactInfo,
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
OwnerUsername: req.OwnerUsername,
|
||||
OwnerPassword: req.OwnerPassword,
|
||||
OwnerNickname: req.OwnerNickname,
|
||||
Features: req.Features,
|
||||
FeeType: req.FeeType,
|
||||
FeeRateBP: req.FeeRateBP,
|
||||
FeeFixedAmount: req.FeeFixedAmount,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, merchant)
|
||||
}
|
||||
|
||||
type updateMerchantSettingsReq struct {
|
||||
Name *string `json:"name"`
|
||||
Status *string `json:"status"`
|
||||
ContactName *string `json:"contact_name"`
|
||||
ContactInfo *string `json:"contact_info"`
|
||||
Features *string `json:"features"`
|
||||
FeeType *string `json:"fee_type"`
|
||||
FeeRateBP *int64 `json:"fee_rate_bp"`
|
||||
FeeFixedAmount *int64 `json:"fee_fixed_amount"`
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateMerchantSettings(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req updateMerchantSettingsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantSettings(uint(id), service.UpdateMerchantSettingsInput{
|
||||
Name: req.Name,
|
||||
Status: req.Status,
|
||||
ContactName: req.ContactName,
|
||||
ContactInfo: req.ContactInfo,
|
||||
Features: req.Features,
|
||||
FeeType: req.FeeType,
|
||||
FeeRateBP: req.FeeRateBP,
|
||||
FeeFixedAmount: req.FeeFixedAmount,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) AddPlatformMerchantMember(c *gin.Context) {
|
||||
var req addMemberReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:user_id 与 role 必填")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
member, err := h.merchantSvc.AddMember(uint(id), service.AddMemberInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Nickname: req.Nickname,
|
||||
Role: req.Role,
|
||||
IsDefault: req.IsDefault,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, member)
|
||||
}
|
||||
|
||||
// AdminGetMerchantAccount 平台管理员查看指定商户的负责人登录账号。
|
||||
func (h *MerchantHandler) AdminGetMerchantAccount(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
accounts, err := h.merchantSvc.ListOwnerAccounts(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, accounts)
|
||||
}
|
||||
|
||||
type adminUpdateMerchantAccountReq struct {
|
||||
UserID uint `json:"user_id" binding:"required"`
|
||||
Username string `json:"username" binding:"required,min=3,max=64"`
|
||||
Nickname string `json:"nickname" binding:"max=64"`
|
||||
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
|
||||
// AdminUpdateMerchantAccount 平台管理员维护指定商户的负责人登录账号:
|
||||
// 修改用户名/昵称、重置密码、启用停用;密码留空表示不重置。
|
||||
func (h *MerchantHandler) AdminUpdateMerchantAccount(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req adminUpdateMerchantAccountReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:用户名必填(3-64 位),密码至少 6 位")
|
||||
return
|
||||
}
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
account, err := h.merchantSvc.UpdateMerchantOwnerAccount(uint(id), service.UpdateMerchantOwnerAccountInput{
|
||||
UserID: req.UserID,
|
||||
Username: req.Username,
|
||||
Nickname: req.Nickname,
|
||||
Password: req.Password,
|
||||
Status: req.Status,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, account)
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/model"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) ListProducts(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.merchantSvc.ListMerchantProductsWithOptions(middleware.GetMerchantID(c), page, size, service.ListMerchantProductsOptions{
|
||||
Status: strings.TrimSpace(c.Query("status")),
|
||||
Keyword: strings.TrimSpace(c.Query("keyword")),
|
||||
})
|
||||
if err != nil {
|
||||
if err.Error() == "无效的商品状态" {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type merchantProductStatusReq struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
|
||||
// UpdateProductStatus PATCH /api/merchant/products/:id/status
|
||||
// 商户仅能上架/下架商品,价格、成本等配置由平台管理员维护。
|
||||
// UpdateProduct PATCH /api/merchant/products/:id — 编辑商品(显示名称、售价、成本、库存、状态)。
|
||||
// 严格风控门禁:仅平台超级管理员(model.RoleAdmin)拥有修改商品价格、成本、库存与名称的权限,普通分销商严禁修改。
|
||||
func (h *MerchantHandler) UpdateProduct(c *gin.Context) {
|
||||
if middleware.GetRole(c) != model.RoleAdmin {
|
||||
response.Forbidden(c, "权限不足:仅平台超级管理员可编辑商品价格、成本与库存")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
var req merchantProductUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(middleware.GetMerchantID(c), uint(id), service.UpdateMerchantProductInput{
|
||||
DisplayName: req.DisplayName,
|
||||
PriceAmount: req.PriceAmount,
|
||||
CostAmount: req.CostAmount,
|
||||
Stock: req.Stock,
|
||||
Status: req.Status,
|
||||
FulfillmentConfig: req.FulfillmentConfig,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) UpdateProductStatus(c *gin.Context) {
|
||||
var req merchantProductStatusReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:status 必填")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(middleware.GetMerchantID(c), uint(id), service.UpdateMerchantProductInput{
|
||||
Status: &req.Status,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// DeleteProduct DELETE /api/merchant/products/:id — 仅已下架可删(软删除)。
|
||||
func (h *MerchantHandler) DeleteProduct(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.merchantSvc.DeleteMerchantProduct(middleware.GetMerchantID(c), uint(id), middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// ListManualOrderProducts returns active products for order creation without granting product-management access.
|
||||
func (h *MerchantHandler) ListManualOrderProducts(c *gin.Context) {
|
||||
list, _, err := h.merchantSvc.ListMerchantProductsForClient(middleware.GetMerchantID(c), 1, 100)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
// ListProductCatalog 返回自营商户的全部可售商品,作为平台默认商品目录供分配。
|
||||
func (h *MerchantHandler) ListProductCatalog(c *gin.Context) {
|
||||
list, err := h.merchantSvc.ListProductCatalog()
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
// ListMerchantProductsByAdmin 供平台管理员查看指定商户的可售商品。
|
||||
func (h *MerchantHandler) ListMerchantProductsByAdmin(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
list, err := h.merchantSvc.ListMerchantProductsByAdmin(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, list)
|
||||
}
|
||||
|
||||
type merchantProductUpdateReq struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
PriceAmount *int64 `json:"price_amount"`
|
||||
CostAmount *int64 `json:"cost_amount"`
|
||||
Stock *int64 `json:"stock"`
|
||||
Status *string `json:"status"`
|
||||
FulfillmentConfig *string `json:"fulfillment_config"`
|
||||
}
|
||||
|
||||
// AdminUpdateMerchantProduct 供平台管理员编辑指定商户的商品(价格、成本、库存、状态等)。
|
||||
func (h *MerchantHandler) AdminUpdateMerchantProduct(c *gin.Context) {
|
||||
merchantID, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
productID, _ := strconv.ParseUint(c.Param("pid"), 10, 64)
|
||||
if merchantID == 0 || productID == 0 {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
var req merchantProductUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.merchantSvc.UpdateMerchantProduct(uint(merchantID), uint(productID), service.UpdateMerchantProductInput{
|
||||
DisplayName: req.DisplayName,
|
||||
PriceAmount: req.PriceAmount,
|
||||
CostAmount: req.CostAmount,
|
||||
Stock: req.Stock,
|
||||
Status: req.Status,
|
||||
FulfillmentConfig: req.FulfillmentConfig,
|
||||
}, middleware.GetUserID(c)); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
type assignProductsReq struct {
|
||||
CatalogIDs []uint `json:"catalog_ids"`
|
||||
}
|
||||
|
||||
// AssignProducts 按商品目录 ID 批量同步商户的可售商品。
|
||||
func (h *MerchantHandler) AssignProducts(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req assignProductsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
count, err := h.merchantSvc.AssignProducts(uint(id), service.AssignProductsInput{
|
||||
CatalogIDs: req.CatalogIDs,
|
||||
}, middleware.GetUserID(c))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"assigned": count})
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"affiliate_dash/internal/middleware"
|
||||
"affiliate_dash/internal/pkg/response"
|
||||
"affiliate_dash/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (h *MerchantHandler) GetWallet(c *gin.Context) {
|
||||
wallet, err := h.fulfillmentSvc.GetWallet(middleware.GetMerchantID(c))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
|
||||
func (h *MerchantHandler) ListWalletLedger(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
referenceNo := c.Query("reference_no")
|
||||
entryType := c.Query("type")
|
||||
list, total, err := h.fulfillmentSvc.ListWalletLedger(middleware.GetMerchantID(c), page, size, referenceNo, entryType)
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
// AdminGetWallet 供平台管理员查看指定商户的钱包。
|
||||
func (h *MerchantHandler) AdminGetWallet(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
wallet, err := h.fulfillmentSvc.GetWallet(uint(id))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
|
||||
// AdminListWalletLedger 供平台管理员查看指定商户的积分流水。
|
||||
func (h *MerchantHandler) AdminListWalletLedger(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
page, size := pageParams(c)
|
||||
list, total, err := h.fulfillmentSvc.ListWalletLedger(uint(id), page, size, c.Query("reference_no"), c.Query("type"))
|
||||
if err != nil {
|
||||
response.ServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
type walletAdjustReq struct {
|
||||
Amount int64 `json:"amount" binding:"required"`
|
||||
IdempotencyKey string `json:"idempotency_key" binding:"required"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
// AdminAdjustWallet 供平台管理员手动调整指定商户的积分,商户侧无任何调账入口。
|
||||
func (h *MerchantHandler) AdminAdjustWallet(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if id == 0 {
|
||||
response.BadRequest(c, "商户 ID 无效")
|
||||
return
|
||||
}
|
||||
var req walletAdjustReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误:amount 与 idempotency_key 必填")
|
||||
return
|
||||
}
|
||||
wallet, err := h.fulfillmentSvc.AdjustWallet(service.WalletAdjustInput{
|
||||
MerchantID: uint(id),
|
||||
ActorUserID: middleware.GetUserID(c),
|
||||
Amount: req.Amount,
|
||||
IdempotencyKey: req.IdempotencyKey,
|
||||
Note: req.Note,
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.rechargeSvc.CheckLowBalanceAndNotify(uint(id))
|
||||
response.OK(c, wallet)
|
||||
}
|
||||
Reference in New Issue
Block a user