mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 18:26:16 +00:00
feat: add admin panel with multi-chain support and notification system
- Add full admin REST API: auth, API keys, chains, chain tokens, wallets, orders, RPC nodes, settings, dashboard stats, notifications - Add JWT-based admin authentication and API key auth middleware - Add multi-chain listeners: BSC, ETH, Polygon, Plasma, EVM WebSocket - Add RPC node health check job with automatic failover - Add Telegram notification channel for order events - Add installer for first-run database initialization - Add new DB models: admin_user, api_key, chain, chain_token, rpc_node, settings, notification_channel - Add order statistics aggregation (daily/monthly/by-chain) - Serve built admin SPA from embedded www/ assets - Add comprehensive test coverage for all new features
This commit is contained in:
@@ -2,16 +2,47 @@ package comm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/assimon/luuu/middleware"
|
||||
"github.com/assimon/luuu/model/mdb"
|
||||
"github.com/assimon/luuu/model/request"
|
||||
"github.com/assimon/luuu/model/service"
|
||||
"github.com/assimon/luuu/util/constant"
|
||||
"github.com/assimon/luuu/util/log"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// apiKeyFromContext returns the api_keys row stamped by CheckApiSign.
|
||||
// Returns nil when the middleware didn't run (should not happen on authed routes).
|
||||
func apiKeyFromContext(ctx echo.Context) *mdb.ApiKey {
|
||||
if v, ok := ctx.Get(middleware.ApiKeyRowKey).(*mdb.ApiKey); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTransaction 创建交易
|
||||
// @Summary Create transaction
|
||||
// @Description Create a payment transaction order. Accepts JSON body (application/json) or form-encoded body (application/x-www-form-urlencoded).
|
||||
// @Tags Payment
|
||||
// @Accept json
|
||||
// @Accept x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param request body request.CreateTransactionRequest false "Transaction payload (JSON)"
|
||||
// @Param order_id formData string false "Merchant order ID"
|
||||
// @Param currency formData string false "Fiat currency (e.g. cny)"
|
||||
// @Param token formData string false "Crypto token (e.g. usdt)"
|
||||
// @Param network formData string false "Network (e.g. tron)"
|
||||
// @Param amount formData number false "Amount"
|
||||
// @Param notify_url formData string false "Callback URL"
|
||||
// @Param signature formData string false "MD5 signature"
|
||||
// @Param redirect_url formData string false "Redirect URL"
|
||||
// @Param name formData string false "Order name"
|
||||
// @Param payment_type formData string false "Payment type"
|
||||
// @Success 200 {object} response.ApiResponse{data=response.CreateTransactionResponse}
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /payments/gmpay/v1/order/create-transaction [post]
|
||||
func (c *BaseCommController) CreateTransaction(ctx echo.Context) (err error) {
|
||||
req := new(request.CreateTransactionRequest)
|
||||
if err = ctx.Bind(req); err != nil {
|
||||
@@ -20,7 +51,7 @@ func (c *BaseCommController) CreateTransaction(ctx echo.Context) (err error) {
|
||||
if err = c.ValidateStruct(ctx, req); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
resp, err := service.CreateTransaction(req)
|
||||
resp, err := service.CreateTransaction(req, apiKeyFromContext(ctx))
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
@@ -28,6 +59,15 @@ func (c *BaseCommController) CreateTransaction(ctx echo.Context) (err error) {
|
||||
}
|
||||
|
||||
// SwitchNetwork 切换支付网络,创建或返回子订单
|
||||
// @Summary Switch payment network
|
||||
// @Description Switch to a different payment network, creating or returning a sub-order
|
||||
// @Tags Payment
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body request.SwitchNetworkRequest true "Switch network payload"
|
||||
// @Success 200 {object} response.ApiResponse
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /pay/switch-network [post]
|
||||
func (c *BaseCommController) SwitchNetwork(ctx echo.Context) (err error) {
|
||||
req := new(request.SwitchNetworkRequest)
|
||||
if err = ctx.Bind(req); err != nil {
|
||||
@@ -46,33 +86,60 @@ func (c *BaseCommController) SwitchNetwork(ctx echo.Context) (err error) {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
|
||||
fmt.Printf("switch network response: \n%s", string(jsonBytes))
|
||||
log.Sugar.Debugf("switch network response: \n%s", string(jsonBytes))
|
||||
|
||||
return c.SucJson(ctx, resp)
|
||||
}
|
||||
|
||||
// CreateTransactionAndRedirect creates a transaction and redirects to
|
||||
// the checkout counter. The route accepts BOTH GET (query string) and
|
||||
// POST (form) per the legacy EPAY protocol; swagger documents POST as
|
||||
// the canonical form — the GET variant is identical save the transport.
|
||||
// @Summary Create transaction and redirect (EPAY compat)
|
||||
// @Description Legacy EPAY-style endpoint. Accepts GET (querystring) and POST (form). On success, 302 redirects to /pay/checkout-counter/{trade_id}. Signature uses MD5 of sorted params + secret_key of the api_keys row matching the submitted pid.
|
||||
// @Tags Payment
|
||||
// @Accept x-www-form-urlencoded
|
||||
// @Produce html
|
||||
// @Param pid query integer false "API key PID (GET query)"
|
||||
// @Param money query number false "Amount (fiat, GET query)"
|
||||
// @Param out_trade_no query string false "Merchant order ID (GET query)"
|
||||
// @Param notify_url query string false "Callback URL (GET query)"
|
||||
// @Param return_url query string false "Redirect URL after payment (GET query)"
|
||||
// @Param name query string false "Order name (GET query)"
|
||||
// @Param type query string false "Payment type (e.g. alipay, GET query)"
|
||||
// @Param sign query string false "MD5 signature (GET query)"
|
||||
// @Param sign_type query string false "Signature type (MD5, GET query)"
|
||||
// @Param pid formData integer true "API key PID"
|
||||
// @Param money formData number true "Amount (fiat)"
|
||||
// @Param out_trade_no formData string true "Merchant order ID"
|
||||
// @Param notify_url formData string true "Callback URL"
|
||||
// @Param return_url formData string false "Redirect URL after payment"
|
||||
// @Param name formData string false "Order name"
|
||||
// @Param type formData string false "Payment type (e.g. alipay)"
|
||||
// @Param sign formData string true "MD5 signature"
|
||||
// @Param sign_type formData string false "Signature type (MD5)"
|
||||
// @Success 302 "Redirect to checkout counter"
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /payments/epay/v1/order/create-transaction/submit.php [post]
|
||||
// @Router /payments/epay/v1/order/create-transaction/submit.php [get]
|
||||
func (c *BaseCommController) CreateTransactionAndRedirect(ctx echo.Context) (err error) {
|
||||
req := new(request.CreateTransactionRequest)
|
||||
if err = ctx.Bind(req); err != nil {
|
||||
log.Println("bind request error:", err)
|
||||
log.Sugar.Errorf("bind request error: %v", err)
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err = c.ValidateStruct(ctx, req); err != nil {
|
||||
log.Println("validate request error:", err)
|
||||
log.Sugar.Errorf("validate request error: %v", err)
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
resp, err := service.CreateTransaction(req)
|
||||
resp, err := service.CreateTransaction(req, apiKeyFromContext(ctx))
|
||||
if err != nil {
|
||||
log.Println("create transaction error:", err)
|
||||
log.Sugar.Errorf("create transaction error: %v", err)
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
|
||||
fmt.Printf("create transaction response: %+v\n", resp)
|
||||
log.Sugar.Debugf("create transaction response: %+v", resp)
|
||||
|
||||
tradeID := resp.TradeId
|
||||
|
||||
ctx.Redirect(302, "/pay/checkout-counter/"+tradeID)
|
||||
|
||||
return nil
|
||||
|
||||
return ctx.Redirect(http.StatusFound, "/pay/checkout-counter/"+tradeID)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package comm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
@@ -14,35 +12,47 @@ import (
|
||||
)
|
||||
|
||||
// CheckoutCounter 收银台
|
||||
// @Summary Checkout counter page
|
||||
// @Description Render the payment checkout counter HTML page for a given trade
|
||||
// @Tags Payment
|
||||
// @Produce html
|
||||
// @Param trade_id path string true "Trade ID"
|
||||
// @Success 200 {string} string "HTML page"
|
||||
// @Router /pay/checkout-counter/{trade_id} [get]
|
||||
func (c *BaseCommController) CheckoutCounter(ctx echo.Context) (err error) {
|
||||
tradeId := ctx.Param("trade_id")
|
||||
resp, err := service.GetCheckoutCounterByTradeId(tradeId)
|
||||
if err != nil {
|
||||
if err == service.ErrOrder {
|
||||
tmpl, err := template.ParseFiles(filepath.Join(config.StaticFilePath, "index.html"))
|
||||
if err != nil {
|
||||
return ctx.String(http.StatusOK, err.Error())
|
||||
// Unknown trade id: render the page with empty payload
|
||||
// (client side shows a friendly "order not found" screen).
|
||||
tmpl, tmplErr := template.ParseFiles(filepath.Join(config.StaticFilePath, "index.html"))
|
||||
if tmplErr != nil {
|
||||
return ctx.String(http.StatusInternalServerError, tmplErr.Error())
|
||||
}
|
||||
ctx.Response().Status = http.StatusNotFound
|
||||
emptyResp := response.CheckoutCounterResponse{}
|
||||
return tmpl.Execute(ctx.Response(), emptyResp)
|
||||
}
|
||||
return ctx.String(http.StatusOK, err.Error())
|
||||
return ctx.String(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
tmpl, err := template.ParseFiles(filepath.Join(config.StaticFilePath, "index.html"))
|
||||
if err != nil {
|
||||
return ctx.String(http.StatusOK, err.Error())
|
||||
return ctx.String(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
jsonByte, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
return ctx.String(http.StatusOK, err.Error())
|
||||
}
|
||||
fmt.Printf("%v\n", string(jsonByte))
|
||||
|
||||
return tmpl.Execute(ctx.Response(), resp)
|
||||
}
|
||||
|
||||
// CheckStatus 支付状态检测
|
||||
// @Summary Check payment status
|
||||
// @Description Check the payment status of an order by trade ID
|
||||
// @Tags Payment
|
||||
// @Produce json
|
||||
// @Param trade_id path string true "Trade ID"
|
||||
// @Success 200 {object} response.ApiResponse{data=response.CheckStatusResponse}
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /pay/check-status/{trade_id} [get]
|
||||
func (c *BaseCommController) CheckStatus(ctx echo.Context) (err error) {
|
||||
tradeId := ctx.Param("trade_id")
|
||||
order, err := service.GetOrderInfoByTradeId(tradeId)
|
||||
|
||||
@@ -3,6 +3,7 @@ package comm
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/assimon/luuu/model/data"
|
||||
"github.com/assimon/luuu/model/response"
|
||||
@@ -10,21 +11,21 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type addSupportedAssetRequest struct {
|
||||
Network string `json:"network" validate:"required"`
|
||||
Token string `json:"token" validate:"required"`
|
||||
Status int64 `json:"status" validate:"required|in:1,2"`
|
||||
}
|
||||
|
||||
type updateSupportedAssetRequest struct {
|
||||
Network string `json:"network" validate:"required"`
|
||||
Token string `json:"token" validate:"required"`
|
||||
Status int64 `json:"status" validate:"required|in:1,2"`
|
||||
}
|
||||
|
||||
// GetSupportedAssets 对外公开可用链与 token 列表(无需鉴权,仅返回已启用项)。
|
||||
// GetSupportedAssets 对外公开可用链与 token 列表。数据源是管理后台
|
||||
// 的 chains + chain_tokens + wallet_address 三张表的实时查询,admin
|
||||
// 启用/禁用链或代币后,这个接口在下次调用时就会反映新状态(无缓存)。
|
||||
// 仅返回:链 enabled=true、代币 enabled=true、且该链上至少有一个可用
|
||||
// 钱包地址的组合。
|
||||
// @Summary List supported (network, tokens) groupings
|
||||
// @Description Public endpoint, Auth required in /admin/api/v1/supported-assets
|
||||
// @Tags Supported Assets
|
||||
// @Produce json
|
||||
// @Success 200 {object} response.ApiResponse{data=response.SupportedAssetsResponse}
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /payments/gmpay/v1/supported-assets [get]
|
||||
// @Router /admin/api/v1/supported-assets [get]
|
||||
func (c *BaseCommController) GetSupportedAssets(ctx echo.Context) error {
|
||||
list, err := data.ListEnabledSupportedAssets()
|
||||
chains, err := data.ListEnabledChains()
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
@@ -33,32 +34,42 @@ func (c *BaseCommController) GetSupportedAssets(ctx echo.Context) error {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
|
||||
networkSet := make(map[string]struct{})
|
||||
// A chain only qualifies if it has at least one wallet available —
|
||||
// without a recipient address, payments on that chain can't be
|
||||
// processed even if the chain + token config says "enabled".
|
||||
networkHasWallet := make(map[string]struct{})
|
||||
for _, w := range wallets {
|
||||
networkSet[w.Network] = struct{}{}
|
||||
networkHasWallet[strings.ToLower(w.Network)] = struct{}{}
|
||||
}
|
||||
|
||||
grouped := make(map[string][]string)
|
||||
for _, item := range list {
|
||||
if _, ok := networkSet[item.Network]; !ok {
|
||||
supports := make([]response.NetworkTokenSupport, 0, len(chains))
|
||||
for _, ch := range chains {
|
||||
network := strings.ToLower(ch.Network)
|
||||
if _, ok := networkHasWallet[network]; !ok {
|
||||
continue
|
||||
}
|
||||
grouped[item.Network] = append(grouped[item.Network], item.Token)
|
||||
}
|
||||
|
||||
networks := make([]string, 0, len(grouped))
|
||||
for network := range grouped {
|
||||
networks = append(networks, network)
|
||||
}
|
||||
sort.Strings(networks)
|
||||
|
||||
supports := make([]response.NetworkTokenSupport, 0, len(networks))
|
||||
for _, network := range networks {
|
||||
tokens := grouped[network]
|
||||
sort.Strings(tokens)
|
||||
tokens, err := data.ListEnabledChainTokensByNetwork(network)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
if len(tokens) == 0 {
|
||||
continue
|
||||
}
|
||||
symbols := make([]string, 0, len(tokens))
|
||||
for _, t := range tokens {
|
||||
sym := strings.ToUpper(strings.TrimSpace(t.Symbol))
|
||||
if sym == "" {
|
||||
continue
|
||||
}
|
||||
symbols = append(symbols, sym)
|
||||
}
|
||||
if len(symbols) == 0 {
|
||||
continue
|
||||
}
|
||||
sort.Strings(symbols)
|
||||
supports = append(supports, response.NetworkTokenSupport{
|
||||
Network: network,
|
||||
Tokens: tokens,
|
||||
Tokens: symbols,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,9 +77,17 @@ func (c *BaseCommController) GetSupportedAssets(ctx echo.Context) error {
|
||||
}
|
||||
|
||||
// ListSupportedAssetRecords 查询支持项明细(无需鉴权)。
|
||||
// @Summary List supported asset records
|
||||
// @Description Public endpoint. Returns enabled chain_tokens, optionally filtered by network.
|
||||
// @Tags Supported Assets
|
||||
// @Produce json
|
||||
// @Param network query string false "Network filter (e.g. tron, ethereum)"
|
||||
// @Success 200 {object} response.ApiResponse{data=[]mdb.ChainToken}
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /payments/gmpay/v1/supported-assets/records [get]
|
||||
func (c *BaseCommController) ListSupportedAssetRecords(ctx echo.Context) error {
|
||||
network := ctx.QueryParam("network")
|
||||
list, err := data.ListSupportedAssets(network)
|
||||
list, err := data.ListEnabledChainTokens(network)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
@@ -76,72 +95,25 @@ func (c *BaseCommController) ListSupportedAssetRecords(ctx echo.Context) error {
|
||||
}
|
||||
|
||||
// GetSupportedAsset 查询单条支持项(无需鉴权)。
|
||||
// @Summary Get a supported asset
|
||||
// @Description Public endpoint. Returns one chain_token row by ID. Returns 404 if the token is disabled.
|
||||
// @Tags Supported Assets
|
||||
// @Produce json
|
||||
// @Param id path int true "ChainToken ID"
|
||||
// @Success 200 {object} response.ApiResponse{data=mdb.ChainToken}
|
||||
// @Failure 400 {object} response.ApiResponse
|
||||
// @Router /payments/gmpay/v1/supported-assets/{id} [get]
|
||||
func (c *BaseCommController) GetSupportedAsset(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
asset, err := data.GetSupportedAssetByID(id)
|
||||
token, err := data.GetChainTokenByID(id)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
if asset.ID <= 0 {
|
||||
if token.ID <= 0 || !token.Enabled {
|
||||
return c.FailJson(ctx, constant.SupportedAssetNotFound)
|
||||
}
|
||||
return c.SucJson(ctx, asset)
|
||||
}
|
||||
|
||||
// AddSupportedAsset 新增支持项(鉴权)。
|
||||
func (c *BaseCommController) AddSupportedAsset(ctx echo.Context) error {
|
||||
req := new(addSupportedAssetRequest)
|
||||
if err := ctx.Bind(req); err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err := c.ValidateStruct(ctx, req); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
asset, err := data.AddSupportedAsset(req.Network, req.Token, req.Status)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, asset)
|
||||
}
|
||||
|
||||
// UpdateSupportedAsset 修改支持项(鉴权)。
|
||||
func (c *BaseCommController) UpdateSupportedAsset(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
req := new(updateSupportedAssetRequest)
|
||||
if err := ctx.Bind(req); err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err := c.ValidateStruct(ctx, req); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
asset, err := data.UpdateSupportedAsset(id, req.Network, req.Token, req.Status)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, asset)
|
||||
}
|
||||
|
||||
// DeleteSupportedAsset 删除支持项(鉴权)。
|
||||
func (c *BaseCommController) DeleteSupportedAsset(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
asset, err := data.GetSupportedAssetByID(id)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
if asset.ID <= 0 {
|
||||
return c.FailJson(ctx, constant.SupportedAssetNotFound)
|
||||
}
|
||||
if err := data.DeleteSupportedAssetByID(id); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, nil)
|
||||
return c.SucJson(ctx, token)
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package comm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/assimon/luuu/model/data"
|
||||
"github.com/assimon/luuu/model/mdb"
|
||||
"github.com/assimon/luuu/util/constant"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type addWalletRequest struct {
|
||||
Network string `json:"network" validate:"required"`
|
||||
Address string `json:"address" validate:"required"`
|
||||
}
|
||||
|
||||
type changeStatusRequest struct {
|
||||
Status int `json:"status" validate:"required|in:1,2"`
|
||||
}
|
||||
|
||||
// AddWallet 添加钱包地址
|
||||
func (c *BaseCommController) AddWallet(ctx echo.Context) error {
|
||||
req := new(addWalletRequest)
|
||||
if err := ctx.Bind(req); err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err := c.ValidateStruct(ctx, req); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
wallet, err := data.AddWalletAddressWithNetwork(req.Network, req.Address)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, wallet)
|
||||
}
|
||||
|
||||
// ListWallets 获取钱包列表
|
||||
func (c *BaseCommController) ListWallets(ctx echo.Context) error {
|
||||
network := ctx.QueryParam("network")
|
||||
var wallets []mdb.WalletAddress
|
||||
var err error
|
||||
if network != "" {
|
||||
wallets, err = data.GetAllWalletAddressByNetwork(network)
|
||||
} else {
|
||||
wallets, err = data.GetAllWalletAddress()
|
||||
}
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, wallets)
|
||||
}
|
||||
|
||||
// GetWallet 获取单个钱包
|
||||
func (c *BaseCommController) GetWallet(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
wallet, err := data.GetWalletAddressById(id)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
if wallet.ID <= 0 {
|
||||
return c.FailJson(ctx, constant.NotAvailableWalletAddress)
|
||||
}
|
||||
return c.SucJson(ctx, wallet)
|
||||
}
|
||||
|
||||
// ChangeWalletStatus 启用/禁用钱包
|
||||
func (c *BaseCommController) ChangeWalletStatus(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
req := new(changeStatusRequest)
|
||||
if err := ctx.Bind(req); err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err := c.ValidateStruct(ctx, req); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
if err := data.ChangeWalletAddressStatus(id, req.Status); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, nil)
|
||||
}
|
||||
|
||||
// DeleteWallet 删除钱包
|
||||
func (c *BaseCommController) DeleteWallet(ctx echo.Context) error {
|
||||
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
return c.FailJson(ctx, constant.ParamsMarshalErr)
|
||||
}
|
||||
if err := data.DeleteWalletAddressById(id); err != nil {
|
||||
return c.FailJson(ctx, err)
|
||||
}
|
||||
return c.SucJson(ctx, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user