商城c端前端

This commit is contained in:
richjohnrambo
2026-10-12 23:26:36 +08:00
parent 8f3a97a54d
commit dd729cb679
17 changed files with 744 additions and 41 deletions
+5
View File
@@ -36,6 +36,8 @@ func (a *API) Register(mux *http.ServeMux) {
mux.HandleFunc("/v1/banners", a.banners)
mux.HandleFunc("/v1/contents/", a.content)
mux.HandleFunc("/v1/member-levels", a.memberLevels)
mux.HandleFunc("/v1/leaderboard", a.leaderboard)
mux.HandleFunc("/v1/customer-services", a.customerServices)
mux.HandleFunc("/v1/task-halls", a.taskHalls)
mux.HandleFunc("/v1/tasks/eligibility", a.taskEligibility)
mux.HandleFunc("/v1/tasks/stats", a.taskStats)
@@ -45,6 +47,9 @@ func (a *API) Register(mux *http.ServeMux) {
mux.HandleFunc("/v1/manual-products", a.manualProducts)
mux.HandleFunc("/v1/manual-products/", a.manualProduct)
mux.HandleFunc("/v1/payment-accounts", a.paymentAccounts)
mux.HandleFunc("/v1/recharge/channels", a.rechargeChannels)
mux.HandleFunc("/v1/recharge-orders", a.rechargeOrders)
mux.HandleFunc("/v1/recharge-orders/", a.rechargeOrder)
mux.HandleFunc("/v1/withdrawal-orders/quote", a.withdrawalQuote)
mux.HandleFunc("/v1/withdrawal-orders", a.withdrawalOrders)
mux.HandleFunc("/v1/withdrawal-orders/", a.withdrawalOrder)
+101
View File
@@ -62,6 +62,91 @@ func (a *API) withdrawalQuote(w http.ResponseWriter, r *http.Request) {
writeSuccess(w, r, result)
}
func (a *API) rechargeChannels(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r, http.MethodGet)
return
}
principal, ok := a.authenticateFinanceMember(w, r)
if !ok {
return
}
result, err := a.finance.RechargeChannels(r.Context(), principal.TenantID)
if err != nil {
writeFinanceError(w, r, err)
return
}
writeSuccess(w, r, result)
}
func (a *API) rechargeOrders(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodPost {
methodNotAllowed(w, r, http.MethodGet, http.MethodPost)
return
}
principal, ok := a.authenticateFinanceMember(w, r)
if !ok {
return
}
if r.Method == http.MethodGet {
page, ok := optionalPositiveInt(w, r, "page")
if !ok {
writeFinanceError(w, r, finance.ErrPageInvalid)
return
}
pageSize, ok := optionalPositiveInt(w, r, "page_size")
if !ok {
writeFinanceError(w, r, finance.ErrPageSizeInvalid)
return
}
result, err := a.finance.RechargeOrders(r.Context(), principal.TenantID, principal.MemberID,
page, pageSize, r.URL.Query().Get("status"))
if err != nil {
writeFinanceError(w, r, err)
return
}
writeSuccess(w, r, result)
return
}
var body finance.CreateRechargeInput
if !decodeBody(w, r, &body) {
return
}
result, err := a.finance.CreateRecharge(r.Context(), principal.TenantID, principal.MemberID, body)
if err != nil {
writeFinanceError(w, r, err)
return
}
writeSuccess(w, r, result)
}
func (a *API) rechargeOrder(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r, http.MethodGet)
return
}
orderIDText := strings.Trim(strings.TrimPrefix(r.URL.Path, "/v1/recharge-orders/"), "/")
if orderIDText == "" || strings.Contains(orderIDText, "/") {
writeFinanceError(w, r, finance.ErrRechargeOrderIDInvalid)
return
}
orderID, err := strconv.ParseInt(orderIDText, 10, 64)
if err != nil || orderID <= 0 {
writeFinanceError(w, r, finance.ErrRechargeOrderIDInvalid)
return
}
principal, ok := a.authenticateFinanceMember(w, r)
if !ok {
return
}
result, err := a.finance.RechargeOrder(r.Context(), principal.TenantID, principal.MemberID, orderID)
if err != nil {
writeFinanceError(w, r, err)
return
}
writeSuccess(w, r, result)
}
func (a *API) withdrawalOrders(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodPost {
methodNotAllowed(w, r, http.MethodGet, http.MethodPost)
@@ -159,6 +244,12 @@ func writeFinanceError(w http.ResponseWriter, r *http.Request, err error) {
writeError(w, r, http.StatusBadRequest, 30000, "CURRENCY_INVALID", "validation failed", map[string]any{"field": "currency"})
case errors.Is(err, finance.ErrAmountInvalid), errors.Is(err, store.ErrWithdrawalAmount):
writeError(w, r, http.StatusUnprocessableEntity, 30022, "WITHDRAWAL_AMOUNT_INVALID", "withdrawal amount is outside the allowed range", nil)
case errors.Is(err, store.ErrRechargeAmount):
writeError(w, r, http.StatusUnprocessableEntity, 30022, "RECHARGE_AMOUNT_INVALID", "recharge amount is outside the allowed range", nil)
case errors.Is(err, finance.ErrRemarkInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "RECHARGE_REMARK_INVALID", "validation failed", map[string]any{"field": "remark"})
case errors.Is(err, finance.ErrVoucherInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "RECHARGE_VOUCHER_INVALID", "validation failed", map[string]any{"field": "voucher_attachment_ids"})
case errors.Is(err, finance.ErrIdempotencyKey):
writeError(w, r, http.StatusBadRequest, 30000, "IDEMPOTENCY_KEY_INVALID", "valid Idempotency-Key header is required", map[string]any{"field": "Idempotency-Key"})
case errors.Is(err, finance.ErrPageInvalid):
@@ -167,6 +258,10 @@ func writeFinanceError(w http.ResponseWriter, r *http.Request, err error) {
writeError(w, r, http.StatusBadRequest, 30000, "PAGE_SIZE_INVALID", "validation failed", map[string]any{"field": "page_size"})
case errors.Is(err, finance.ErrStatusInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "WITHDRAWAL_STATUS_INVALID", "validation failed", map[string]any{"field": "status"})
case errors.Is(err, finance.ErrRechargeStatusInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "RECHARGE_STATUS_INVALID", "validation failed", map[string]any{"field": "status"})
case errors.Is(err, finance.ErrRechargeOrderIDInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "RECHARGE_ORDER_ID_INVALID", "validation failed", map[string]any{"field": "order_id"})
case errors.Is(err, finance.ErrOrderIDInvalid):
writeError(w, r, http.StatusBadRequest, 30000, "WITHDRAWAL_ORDER_ID_INVALID", "validation failed", map[string]any{"field": "order_id"})
case errors.Is(err, store.ErrPaymentAccountExists):
@@ -177,6 +272,8 @@ func writeFinanceError(w http.ResponseWriter, r *http.Request, err error) {
writeError(w, r, http.StatusUnprocessableEntity, 30022, "WITHDRAWAL_DISABLED", "withdrawal feature is disabled", nil)
case errors.Is(err, store.ErrWithdrawalChannel):
writeError(w, r, http.StatusServiceUnavailable, 30053, "WITHDRAWAL_CHANNEL_UNAVAILABLE", "withdrawal channel is unavailable", nil)
case errors.Is(err, store.ErrRechargeChannel):
writeError(w, r, http.StatusServiceUnavailable, 30053, "RECHARGE_CHANNEL_UNAVAILABLE", "recharge channel is unavailable", nil)
case errors.Is(err, store.ErrWithdrawalLimit):
writeError(w, r, http.StatusUnprocessableEntity, 30022, "WITHDRAWAL_DAILY_LIMIT_REACHED", "daily withdrawal limit reached", nil)
case errors.Is(err, store.ErrInsufficientBalance):
@@ -187,6 +284,10 @@ func writeFinanceError(w http.ResponseWriter, r *http.Request, err error) {
writeError(w, r, http.StatusUnprocessableEntity, 30022, "ACCOUNT_RESTRICTED", "account is restricted", nil)
case errors.Is(err, store.ErrWithdrawalNotFound):
writeError(w, r, http.StatusNotFound, 30044, "WITHDRAWAL_ORDER_NOT_FOUND", "withdrawal order not found", nil)
case errors.Is(err, store.ErrRechargeNotFound):
writeError(w, r, http.StatusNotFound, 30044, "RECHARGE_ORDER_NOT_FOUND", "recharge order not found", nil)
case errors.Is(err, store.ErrRechargeVoucher):
writeError(w, r, http.StatusBadRequest, 30000, "RECHARGE_VOUCHER_INVALID", "validation failed", map[string]any{"field": "voucher_attachment_ids"})
case errors.Is(err, store.ErrIdempotencyConflict):
writeError(w, r, http.StatusConflict, 30040, "IDEMPOTENCY_CONFLICT", "Idempotency-Key was reused with different request data", nil)
case errors.Is(err, store.ErrAttachmentInvalid):
+40
View File
@@ -117,6 +117,46 @@ func (a *API) memberLevels(w http.ResponseWriter, r *http.Request) {
writeSuccess(w, r, items)
}
func (a *API) leaderboard(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r, http.MethodGet)
return
}
if !a.ensurePortal(w, r) {
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
items, err := a.portal.Leaderboard(r.Context(), principal.TenantID)
if err != nil {
writePortalError(w, r, err)
return
}
writeSuccess(w, r, items)
}
func (a *API) customerServices(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r, http.MethodGet)
return
}
if !a.ensurePortal(w, r) {
return
}
principal, ok := a.authenticateMember(w, r)
if !ok {
return
}
items, err := a.portal.CustomerServices(r.Context(), principal.TenantID)
if err != nil {
writePortalError(w, r, err)
return
}
writeSuccess(w, r, items)
}
func (a *API) ensurePortal(w http.ResponseWriter, r *http.Request) bool {
if a.portal != nil {
return true