763 lines
27 KiB
Go
763 lines
27 KiB
Go
package controller
|
||
|
||
import (
|
||
"io"
|
||
"math"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/QuantumNous/new-api/common"
|
||
"github.com/QuantumNous/new-api/model"
|
||
"github.com/QuantumNous/new-api/service"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type createDistributorWithdrawalRequest struct {
|
||
AccountType int `json:"account_type"`
|
||
RealName string `json:"real_name"`
|
||
BankName string `json:"bank_name"`
|
||
BankAccount string `json:"bank_account"`
|
||
WithdrawMonth string `json:"withdraw_month"`
|
||
// 使用 float64 兼容前端 JSON 中的小数(如 InputNumber),再取整
|
||
QuotaAmount float64 `json:"quota_amount"`
|
||
// 个人扩展
|
||
IdCardNo string `json:"id_card_no"`
|
||
IdCardExpiry string `json:"id_card_expiry"`
|
||
Mobile string `json:"mobile"`
|
||
BankReservedPhone string `json:"bank_reserved_phone"`
|
||
IdCardFrontUrl string `json:"id_card_front_url"`
|
||
IdCardBackUrl string `json:"id_card_back_url"`
|
||
BankCardPhotoUrl string `json:"bank_card_photo_url"`
|
||
// 企业扩展
|
||
CreditCode string `json:"credit_code"`
|
||
LegalPersonName string `json:"legal_person_name"`
|
||
LegalPersonPhone string `json:"legal_person_phone"`
|
||
BankBranchCode string `json:"bank_branch_code"`
|
||
ContactPerson string `json:"contact_person"`
|
||
BusinessLicenseUrl string `json:"business_license_url"`
|
||
CorporateAccountProofUrl string `json:"corporate_account_proof_url"`
|
||
InvoiceUrl string `json:"invoice_url"`
|
||
}
|
||
|
||
func distributorWithdrawalToJSON(w model.DistributorWithdrawal, username string) gin.H {
|
||
profile := model.ParseDistributorWithdrawalProfile(w.ProfileData)
|
||
return gin.H{
|
||
"id": w.Id,
|
||
"user_id": w.UserId,
|
||
"username": username,
|
||
"account_type": w.AccountType,
|
||
"real_name": w.RealName,
|
||
"bank_name": w.BankName,
|
||
"bank_account": w.BankAccount,
|
||
"profile_data": profile,
|
||
"voucher_urls": w.VoucherUrls,
|
||
"withdraw_month": w.WithdrawMonth,
|
||
"quota_amount": w.QuotaAmount,
|
||
"status": w.Status,
|
||
"reject_reason": w.RejectReason,
|
||
"reviewer_id": w.ReviewerId,
|
||
"reviewed_at": w.ReviewedAt,
|
||
"cancelled_at": w.CancelledAt,
|
||
"created_at": w.CreatedAt,
|
||
"updated_at": w.UpdatedAt,
|
||
"id_card_no": profile.IdCardNo,
|
||
"id_card_expiry": profile.IdCardExpiry,
|
||
"mobile": profile.Mobile,
|
||
"bank_reserved_phone": profile.BankReservedPhone,
|
||
"id_card_front_url": profile.IdCardFrontUrl,
|
||
"id_card_back_url": profile.IdCardBackUrl,
|
||
"bank_card_photo_url": profile.BankCardPhotoUrl,
|
||
"credit_code": profile.CreditCode,
|
||
"legal_person_name": profile.LegalPersonName,
|
||
"legal_person_phone": profile.LegalPersonPhone,
|
||
"bank_branch_code": profile.BankBranchCode,
|
||
"contact_person": profile.ContactPerson,
|
||
"business_license_url": profile.BusinessLicenseUrl,
|
||
"corporate_account_proof_url": profile.CorporateAccountProofUrl,
|
||
"invoice_url": profile.InvoiceUrl,
|
||
}
|
||
}
|
||
|
||
func buildWithdrawalProfileJSON(req createDistributorWithdrawalRequest) (string, error) {
|
||
accountType := req.AccountType
|
||
if accountType == 0 {
|
||
accountType = model.DistributorApplyTypePersonal
|
||
}
|
||
p := model.DistributorWithdrawalProfile{
|
||
IdCardNo: strings.TrimSpace(req.IdCardNo),
|
||
IdCardExpiry: strings.TrimSpace(req.IdCardExpiry),
|
||
Mobile: strings.TrimSpace(req.Mobile),
|
||
BankReservedPhone: strings.TrimSpace(req.BankReservedPhone),
|
||
IdCardFrontUrl: strings.TrimSpace(req.IdCardFrontUrl),
|
||
IdCardBackUrl: strings.TrimSpace(req.IdCardBackUrl),
|
||
BankCardPhotoUrl: strings.TrimSpace(req.BankCardPhotoUrl),
|
||
CreditCode: strings.TrimSpace(req.CreditCode),
|
||
LegalPersonName: strings.TrimSpace(req.LegalPersonName),
|
||
LegalPersonPhone: strings.TrimSpace(req.LegalPersonPhone),
|
||
BankBranchCode: strings.TrimSpace(req.BankBranchCode),
|
||
ContactPerson: strings.TrimSpace(req.ContactPerson),
|
||
BusinessLicenseUrl: strings.TrimSpace(req.BusinessLicenseUrl),
|
||
CorporateAccountProofUrl: strings.TrimSpace(req.CorporateAccountProofUrl),
|
||
InvoiceUrl: strings.TrimSpace(req.InvoiceUrl),
|
||
}
|
||
b, err := common.Marshal(p)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(b), nil
|
||
}
|
||
|
||
type submitDistributorApplicationRequest struct {
|
||
ApplyType int `json:"apply_type"`
|
||
RealName string `json:"real_name"`
|
||
IdCardNo string `json:"id_card_no"`
|
||
QualificationUrls []string `json:"qualification_urls"`
|
||
Contact string `json:"contact"`
|
||
}
|
||
|
||
// PostDistributorApplication 提交/重新提交分销商申请
|
||
func PostDistributorApplication(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
var req submitDistributorApplicationRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
if req.ApplyType == 0 {
|
||
req.ApplyType = model.DistributorApplyTypePersonal
|
||
}
|
||
urlsJSON, err := common.Marshal(req.QualificationUrls)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "资料序列化失败"})
|
||
return
|
||
}
|
||
err = model.UpsertDistributorApplication(userId, req.ApplyType, req.RealName, req.IdCardNo, string(urlsJSON), req.Contact)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// GetMyDistributorApplication 当前用户的申请状态
|
||
func GetMyDistributorApplication(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
app, err := model.GetDistributorApplicationByUserId(userId)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": app})
|
||
}
|
||
|
||
// GetDistributorCenterInfo 分销商中心汇总(邀请短链、默认比例等)
|
||
func GetDistributorCenterInfo(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
user, err := model.GetUserById(userId, true)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
if !model.UserIsDistributor(user) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "您不是分销商"})
|
||
return
|
||
}
|
||
if user.AffCode == "" {
|
||
user.AffCode = common.GetRandomString(4)
|
||
if err := user.Update(false); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
}
|
||
bps := user.DistributorCommissionBps
|
||
if bps <= 0 {
|
||
bps = common.AffiliateDefaultCommissionBps
|
||
}
|
||
applyType, applicationRealName, err := model.GetDistributorWithdrawAccountType(userId)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"message": "",
|
||
"data": gin.H{
|
||
"aff_code": user.AffCode,
|
||
"aff_quota": user.AffQuota,
|
||
"aff_history_quota": user.AffHistoryQuota,
|
||
"aff_count": user.AffCount,
|
||
"distributor_commission_bps": user.DistributorCommissionBps,
|
||
"effective_commission_bps": bps,
|
||
"default_commission_bps": common.AffiliateDefaultCommissionBps,
|
||
"apply_type": applyType,
|
||
"application_real_name": applicationRealName,
|
||
},
|
||
})
|
||
}
|
||
|
||
// GetDistributorInviteeCommissionLogs 分销商查看某一被邀请用户的充值分成明细(按笔:入账额度、当时比例、收益额度)。
|
||
func GetDistributorInviteeCommissionLogs(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
u, err := model.GetUserById(userId, false)
|
||
if err != nil || !model.UserIsDistributor(u) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "仅分销商可查看"})
|
||
return
|
||
}
|
||
inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
|
||
if err != nil || inviteeId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "参数错误"})
|
||
return
|
||
}
|
||
invitee, err := model.GetUserById(inviteeId, false)
|
||
if err != nil || invitee.InviterId != userId {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无权查看或用户不存在"})
|
||
return
|
||
}
|
||
pageInfo := common.GetPageQuery(c)
|
||
items, total, err := model.ListAffInviteCommissionLogs(userId, inviteeId, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
// GetDistributorInviteeProfitShareLogs 分销商查看某一被邀请用户的利润分成明细(按次结算)。
|
||
func GetDistributorInviteeProfitShareLogs(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
u, err := model.GetUserById(userId, false)
|
||
if err != nil || !model.UserIsDistributor(u) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "仅分销商可查看"})
|
||
return
|
||
}
|
||
inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
|
||
if err != nil || inviteeId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "参数错误"})
|
||
return
|
||
}
|
||
invitee, err := model.GetUserById(inviteeId, false)
|
||
if err != nil || invitee.InviterId != userId {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无权查看或用户不存在"})
|
||
return
|
||
}
|
||
pageInfo := common.GetPageQuery(c)
|
||
items, total, err := model.ListAffInviteProfitShareLogs(userId, inviteeId, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
type rejectApplicationRequest struct {
|
||
Reason string `json:"reason"`
|
||
}
|
||
|
||
// ListDistributorApplicationsAdmin 管理端:申请列表
|
||
func ListDistributorApplicationsAdmin(c *gin.Context) {
|
||
pageInfo := common.GetPageQuery(c)
|
||
status, _ := strconv.Atoi(c.Query("status"))
|
||
applyType, _ := strconv.Atoi(c.Query("apply_type"))
|
||
q := model.DistributorApplicationListQuery{
|
||
Keyword: c.Query("keyword"),
|
||
Status: status,
|
||
ApplyType: applyType,
|
||
DateFrom: parseInt64Query(c.Query("date_from")),
|
||
DateTo: parseInt64Query(c.Query("date_to")),
|
||
PageInfo: pageInfo,
|
||
}
|
||
rows, usernames, total, err := model.ListDistributorApplicationsAdmin(q)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
items := make([]gin.H, 0, len(rows))
|
||
for i := range rows {
|
||
items = append(items, gin.H{
|
||
"id": rows[i].Id,
|
||
"user_id": rows[i].UserId,
|
||
"username": usernames[i],
|
||
"apply_type": rows[i].ApplyType,
|
||
"real_name": rows[i].RealName,
|
||
"contact": rows[i].Contact,
|
||
"status": rows[i].Status,
|
||
"reject_reason": rows[i].RejectReason,
|
||
"created_at": rows[i].CreatedAt,
|
||
"id_card_no_mask": maskIdCard(rows[i].IdCardNo),
|
||
"qualification_urls": rows[i].QualificationUrls,
|
||
})
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
func parseInt64Query(s string) int64 {
|
||
s = strings.TrimSpace(s)
|
||
if s == "" {
|
||
return 0
|
||
}
|
||
n, _ := strconv.ParseInt(s, 10, 64)
|
||
return n
|
||
}
|
||
|
||
func maskIdCard(id string) string {
|
||
id = strings.TrimSpace(id)
|
||
if len(id) <= 8 {
|
||
return "****"
|
||
}
|
||
return id[:4] + strings.Repeat("*", len(id)-8) + id[len(id)-4:]
|
||
}
|
||
|
||
// GetDistributorApplicationAdmin 申请详情(管理员)
|
||
func GetDistributorApplicationAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
app, username, err := model.GetDistributorApplicationByIdAdmin(id)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"message": "",
|
||
"data": gin.H{
|
||
"id": app.Id,
|
||
"user_id": app.UserId,
|
||
"username": username,
|
||
"apply_type": app.ApplyType,
|
||
"real_name": app.RealName,
|
||
"id_card_no": app.IdCardNo,
|
||
"qualification_urls": app.QualificationUrls,
|
||
"contact": app.Contact,
|
||
"status": app.Status,
|
||
"reject_reason": app.RejectReason,
|
||
"reviewer_id": app.ReviewerId,
|
||
"reviewed_at": app.ReviewedAt,
|
||
"created_at": app.CreatedAt,
|
||
"updated_at": app.UpdatedAt,
|
||
},
|
||
})
|
||
}
|
||
|
||
type approveDistributorApplicationRequest struct {
|
||
DistributorCommissionBps *int `json:"distributor_commission_bps"`
|
||
}
|
||
|
||
// ApproveDistributorApplicationAdmin 通过申请(可选 body:distributor_commission_bps 万分之一,0=跟随系统)
|
||
func ApproveDistributorApplicationAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
var req approveDistributorApplicationRequest
|
||
body, readErr := io.ReadAll(c.Request.Body)
|
||
if readErr != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "读取请求失败"})
|
||
return
|
||
}
|
||
if len(strings.TrimSpace(string(body))) > 0 {
|
||
if err := common.Unmarshal(body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
}
|
||
reviewerId := c.GetInt("id")
|
||
if err := model.ApproveDistributorApplication(id, reviewerId, req.DistributorCommissionBps); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
if app, _, err := model.GetDistributorApplicationByIdAdmin(id); err == nil && app != nil {
|
||
service.NotifyDistributorApplicationApproved(app.UserId)
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// RejectDistributorApplicationAdmin 驳回
|
||
func RejectDistributorApplicationAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
var req rejectApplicationRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
reviewerId := c.GetInt("id")
|
||
app, _, errApp := model.GetDistributorApplicationByIdAdmin(id)
|
||
if err := model.RejectDistributorApplication(id, reviewerId, req.Reason); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
if errApp == nil && app != nil {
|
||
service.NotifyDistributorApplicationRejected(app.UserId, req.Reason)
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// ListDistributorsAdmin 分销商人员列表
|
||
func ListDistributorsAdmin(c *gin.Context) {
|
||
pageInfo := common.GetPageQuery(c)
|
||
keyword := c.Query("keyword")
|
||
applyType, _ := strconv.Atoi(c.Query("apply_type"))
|
||
rows, total, err := model.ListDistributorsAdmin(model.DistributorListAdminQuery{
|
||
Keyword: keyword,
|
||
ApplyType: applyType,
|
||
PageInfo: pageInfo,
|
||
})
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
items := make([]gin.H, 0, len(rows))
|
||
for _, it := range rows {
|
||
u := it.User
|
||
bps := u.DistributorCommissionBps
|
||
if bps <= 0 {
|
||
bps = common.AffiliateDefaultCommissionBps
|
||
}
|
||
items = append(items, gin.H{
|
||
"id": u.Id,
|
||
"username": u.Username,
|
||
"display_name": u.DisplayName,
|
||
"application_real_name": it.ApplicationRealName,
|
||
"application_apply_type": it.ApplicationApplyType,
|
||
"needs_supplement": it.NeedsSupplement,
|
||
"aff_code": u.AffCode,
|
||
"aff_count": u.AffCount,
|
||
"aff_quota": u.AffQuota,
|
||
"aff_history_quota": u.AffHistoryQuota,
|
||
"distributor_commission_bps": u.DistributorCommissionBps,
|
||
"effective_commission_bps": bps,
|
||
})
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
type putDistributorCommissionRequest struct {
|
||
DistributorCommissionBps int `json:"distributor_commission_bps"`
|
||
}
|
||
|
||
// PutDistributorCommissionAdmin 设置单个分销商默认分成比例
|
||
func PutDistributorCommissionAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
var req putDistributorCommissionRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
if err := model.SetUserDistributorCommissionBps(id, req.DistributorCommissionBps); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// GetDistributorInviteesAdmin 某分销商名下邀请用户明细
|
||
func GetDistributorInviteesAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
u, err := model.GetUserById(id, false)
|
||
if err != nil || !model.UserIsDistributor(u) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "用户不是分销商"})
|
||
return
|
||
}
|
||
pageInfo := common.GetPageQuery(c)
|
||
keyword := strings.TrimSpace(c.Query("keyword"))
|
||
if len(keyword) > 120 {
|
||
keyword = keyword[:120]
|
||
}
|
||
items, total, err := model.ListAffInvitees(id, keyword, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
// GetDistributorInviteeProfitSharesAdmin 管理端查看某分销商下某一被邀请用户的利润分成消费流水(分页)。
|
||
func GetDistributorInviteeProfitSharesAdmin(c *gin.Context) {
|
||
distributorId, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || distributorId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid distributor id"})
|
||
return
|
||
}
|
||
inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
|
||
if err != nil || inviteeId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid invitee id"})
|
||
return
|
||
}
|
||
if !common.IsDistributorProfitShareMode() {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "当前站点未启用利润分成模式"})
|
||
return
|
||
}
|
||
dist, err := model.GetUserById(distributorId, false)
|
||
if err != nil || !model.UserIsDistributor(dist) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "用户不是分销商"})
|
||
return
|
||
}
|
||
invitee, err := model.GetUserById(inviteeId, false)
|
||
if err != nil || invitee == nil || invitee.InviterId != distributorId {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "该用户不是此分销商邀请的下级"})
|
||
return
|
||
}
|
||
pageInfo := common.GetPageQuery(c)
|
||
items, total, err := model.ListAffInviteProfitShareLogs(distributorId, inviteeId, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
// PostDistributorWithdrawal 提交线下提现申请(暂扣 aff_quota)
|
||
func PostDistributorWithdrawal(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
var req createDistributorWithdrawalRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
accountType, _, err := model.GetDistributorWithdrawAccountType(userId)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
profileJSON, err := buildWithdrawalProfileJSON(req)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
quotaAmt := int(math.Round(req.QuotaAmount))
|
||
if err := model.CreateDistributorWithdrawal(
|
||
userId,
|
||
accountType,
|
||
strings.TrimSpace(req.RealName),
|
||
strings.TrimSpace(req.BankName),
|
||
strings.TrimSpace(req.BankAccount),
|
||
profileJSON,
|
||
"[]",
|
||
strings.TrimSpace(req.WithdrawMonth),
|
||
quotaAmt,
|
||
); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
service.NotifyDistributorWithdrawalSubmitted(userId, quotaAmt)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// GetDistributorWithdrawals 当前用户提现记录
|
||
func GetDistributorWithdrawals(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
pageInfo := common.GetPageQuery(c)
|
||
rows, total, err := model.ListDistributorWithdrawals(userId, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
items := make([]gin.H, 0, len(rows))
|
||
for i := range rows {
|
||
items = append(items, distributorWithdrawalToJSON(rows[i], ""))
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
// PostDistributorWithdrawalCancel 取消待审核提现,退回 aff_quota
|
||
func PostDistributorWithdrawalCancel(c *gin.Context) {
|
||
userId := c.GetInt("id")
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
if err := model.CancelDistributorWithdrawal(userId, id); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// ListDistributorWithdrawalsAdmin 管理端提现审核列表
|
||
func ListDistributorWithdrawalsAdmin(c *gin.Context) {
|
||
pageInfo := common.GetPageQuery(c)
|
||
status, _ := strconv.Atoi(c.Query("status"))
|
||
accountType, _ := strconv.Atoi(c.Query("account_type"))
|
||
keyword := c.Query("keyword")
|
||
rows, total, err := model.ListDistributorWithdrawalsAdmin(status, accountType, keyword, pageInfo)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
items := make([]gin.H, 0, len(rows))
|
||
for i := range rows {
|
||
items = append(items, distributorWithdrawalToJSON(rows[i].DistributorWithdrawal, rows[i].Username))
|
||
}
|
||
pageInfo.SetTotal(int(total))
|
||
pageInfo.SetItems(items)
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
|
||
}
|
||
|
||
// ApproveDistributorWithdrawalAdmin 审核通过
|
||
func ApproveDistributorWithdrawalAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
reviewerId := c.GetInt("id")
|
||
var wUserId int
|
||
if w, err := model.GetDistributorWithdrawalByID(id); err == nil && w != nil {
|
||
wUserId = w.UserId
|
||
}
|
||
if err := model.ApproveDistributorWithdrawalAdmin(id, reviewerId); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
if wUserId > 0 {
|
||
service.NotifyDistributorWithdrawalApproved(wUserId)
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
type rejectWithdrawalRequest struct {
|
||
Reason string `json:"reason"`
|
||
}
|
||
|
||
// RejectDistributorWithdrawalAdmin 驳回并退回 aff_quota
|
||
func RejectDistributorWithdrawalAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
var req rejectWithdrawalRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
reviewerId := c.GetInt("id")
|
||
var wUserId int
|
||
if w, err := model.GetDistributorWithdrawalByID(id); err == nil && w != nil {
|
||
wUserId = w.UserId
|
||
}
|
||
if err := model.RejectDistributorWithdrawalAdmin(id, reviewerId, req.Reason); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
if wUserId > 0 {
|
||
service.NotifyDistributorWithdrawalRejected(wUserId, req.Reason)
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
// PostDistributorSettleAdmin 结账:清空该分销商待结算 aff_quota
|
||
func PostDistributorSettleAdmin(c *gin.Context) {
|
||
id, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || id <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
if err := model.AdminSettleDistributorAffQuota(id); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|
||
|
||
type adminUpsertDistributorApplicationRequest struct {
|
||
ApplyType int `json:"apply_type"`
|
||
RealName string `json:"real_name"`
|
||
IdCardNo string `json:"id_card_no"`
|
||
QualificationUrls []string `json:"qualification_urls"`
|
||
Contact string `json:"contact"`
|
||
}
|
||
|
||
// GetDistributorApplicationByUserAdmin 管理端:查看某分销商的申请/认证资料(手工开通可能无记录)
|
||
func GetDistributorApplicationByUserAdmin(c *gin.Context) {
|
||
userId, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || userId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
username, app, needsManualEntry, err := model.GetDistributorApplicationProfileByUserIdAdmin(userId)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
data := gin.H{
|
||
"user_id": userId,
|
||
"username": username,
|
||
"needs_manual_entry": needsManualEntry,
|
||
}
|
||
if app != nil {
|
||
data["application"] = app
|
||
} else {
|
||
data["application"] = nil
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": data})
|
||
}
|
||
|
||
// PutDistributorApplicationByUserAdmin 管理端:补录或修改分销商申请资料
|
||
func PutDistributorApplicationByUserAdmin(c *gin.Context) {
|
||
userId, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil || userId <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
|
||
return
|
||
}
|
||
var req adminUpsertDistributorApplicationRequest
|
||
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
|
||
return
|
||
}
|
||
urls := make([]string, 0, len(req.QualificationUrls))
|
||
for _, u := range req.QualificationUrls {
|
||
u = strings.TrimSpace(u)
|
||
if u != "" {
|
||
urls = append(urls, u)
|
||
}
|
||
}
|
||
urlsJSON, err := common.Marshal(urls)
|
||
if err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "资料序列化失败"})
|
||
return
|
||
}
|
||
applyType := req.ApplyType
|
||
if applyType == 0 {
|
||
applyType = model.DistributorApplyTypePersonal
|
||
}
|
||
reviewerId := c.GetInt("id")
|
||
if err := model.AdminUpsertDistributorApplicationByUser(userId, reviewerId, applyType, req.RealName, req.IdCardNo, string(urlsJSON), req.Contact); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
|
||
}
|