428 lines
13 KiB
Go
428 lines
13 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/models"
|
|
"server/pkg/jwtutil"
|
|
"server/services/payment"
|
|
|
|
"github.com/beego/beego/v2/client/orm"
|
|
beego "github.com/beego/beego/v2/server/web"
|
|
)
|
|
|
|
// PlatformCommissionController 平台端推广佣金(台账 / 统计 / 规则维护)
|
|
type PlatformCommissionController struct {
|
|
beego.Controller
|
|
}
|
|
|
|
func (c *PlatformCommissionController) platformClaims() (*jwtutil.Claims, error) {
|
|
return paymentPlatformClaims(&c.Controller)
|
|
}
|
|
|
|
func (c *PlatformCommissionController) jsonErr(httpStatus, bizCode int, msg string) {
|
|
c.Ctx.Output.SetStatus(httpStatus)
|
|
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
func (c *PlatformCommissionController) ok(data interface{}) {
|
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// commissionRuleDTO 规则出参。
|
|
// 比例值统一按「百分比」返回:库里存百分比×100(500 = 5%),接口返回 5,便于前端直接编辑。
|
|
func commissionRuleDTO(row *models.PlatformCommissionRule) map[string]interface{} {
|
|
dto := map[string]interface{}{
|
|
"id": row.ID, "rule_name": row.RuleName, "calc_type": row.CalcType,
|
|
"order_type": row.OrderType, "status": row.Status, "remark": row.Remark,
|
|
"create_user_id": row.CreateUserID, "create_user_name": row.CreateUserName,
|
|
"create_time": row.CreateTime, "update_time": row.UpdateTime,
|
|
"steps": []map[string]interface{}{},
|
|
}
|
|
if row.CalcType == models.CommissionCalcFixed {
|
|
dto["value"] = row.Value // 固定金额:分
|
|
} else {
|
|
dto["value"] = float64(row.Value) / 100 // 比例:5% -> 5
|
|
}
|
|
if row.CalcType == models.CommissionCalcTiered && row.TierJSON != nil && *row.TierJSON != "" {
|
|
var tiers []models.CommissionTier
|
|
if err := json.Unmarshal([]byte(*row.TierJSON), &tiers); err == nil {
|
|
steps := make([]map[string]interface{}, 0, len(tiers))
|
|
for _, t := range tiers {
|
|
var maxV interface{}
|
|
if t.Max != nil {
|
|
maxV = *t.Max
|
|
}
|
|
steps = append(steps, map[string]interface{}{
|
|
"min": t.Min, "max": maxV, "rate": float64(t.Rate) / 100,
|
|
})
|
|
}
|
|
dto["steps"] = steps
|
|
}
|
|
}
|
|
return dto
|
|
}
|
|
|
|
// commissionOrderDTO 佣金单出参
|
|
func commissionOrderDTO(row *models.PlatformCommissionOrder) map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"id": row.ID, "commission_no": row.CommissionNo,
|
|
"pay_no": row.PayNo, "out_trade_no": row.OutTradeNo,
|
|
"rule_id": row.RuleID, "rule_name": row.RuleName, "calc_type": row.CalcType,
|
|
"promoter_id": row.PromoterID, "promoter_name": row.PromoterName, "promoter_type": row.PromoterType,
|
|
"base_amount": row.BaseAmount, "amount": row.Amount,
|
|
"status": row.Status, "pay_method": row.PayMethod, "pay_voucher": row.PayVoucher,
|
|
"paid_at": row.PaidAt, "paid_user_id": row.PaidUserID, "paid_user_name": row.PaidUserName,
|
|
"remark": row.Remark, "create_time": row.CreateTime, "update_time": row.UpdateTime,
|
|
}
|
|
}
|
|
|
|
// List GET /platform/commission/list 佣金台账
|
|
func (c *PlatformCommissionController) List() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
page, _ := strconv.Atoi(c.GetString("page", "1"))
|
|
pageSize, _ := strconv.Atoi(c.GetString("pageSize", "10"))
|
|
rows, total, err := payment.ListCommissions(payment.CommissionListInput{
|
|
Promoter: strings.TrimSpace(c.GetString("promoter")),
|
|
Status: strings.TrimSpace(c.GetString("status")),
|
|
StartTime: strings.TrimSpace(c.GetString("start_time")),
|
|
EndTime: strings.TrimSpace(c.GetString("end_time")),
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
})
|
|
if err != nil {
|
|
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
|
return
|
|
}
|
|
if rows == nil {
|
|
rows = []models.PlatformCommissionOrder{}
|
|
}
|
|
list := make([]map[string]interface{}, 0, len(rows))
|
|
for i := range rows {
|
|
list = append(list, commissionOrderDTO(&rows[i]))
|
|
}
|
|
c.ok(map[string]interface{}{"list": list, "total": total, "page": page, "pageSize": pageSize})
|
|
}
|
|
|
|
// Stats GET /platform/commission/stats 台账统计
|
|
func (c *PlatformCommissionController) Stats() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
stats, err := payment.GetCommissionStats()
|
|
if err != nil {
|
|
c.jsonErr(500, 500, "统计失败: "+err.Error())
|
|
return
|
|
}
|
|
c.ok(stats)
|
|
}
|
|
|
|
// MarkPaid POST /platform/commission/:id/pay 标记已付(发放登记)
|
|
func (c *PlatformCommissionController) MarkPaid() {
|
|
claims, err := c.platformClaims()
|
|
if err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.jsonErr(400, 400, "无效ID")
|
|
return
|
|
}
|
|
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
|
var p struct {
|
|
PayMethod string `json:"pay_method"`
|
|
PayVoucher string `json:"pay_voucher"`
|
|
Remark string `json:"remark"`
|
|
}
|
|
if err := json.Unmarshal(body, &p); err != nil {
|
|
c.jsonErr(400, 400, "参数错误")
|
|
return
|
|
}
|
|
if strings.TrimSpace(p.PayMethod) == "" {
|
|
c.jsonErr(400, 400, "请选择发放方式")
|
|
return
|
|
}
|
|
row, err := payment.MarkCommissionPaid(context.Background(), payment.MarkCommissionPaidInput{
|
|
ID: id, PayMethod: strings.TrimSpace(p.PayMethod), PayVoucher: strings.TrimSpace(p.PayVoucher),
|
|
Remark: strings.TrimSpace(p.Remark),
|
|
OperatorID: fmt.Sprintf("%d", claims.UserID), OperatorName: claims.Username,
|
|
})
|
|
if err != nil {
|
|
c.jsonErr(400, 400, err.Error())
|
|
return
|
|
}
|
|
c.ok(commissionOrderDTO(row))
|
|
}
|
|
|
|
/* ---------------- 佣金规则 ---------------- */
|
|
|
|
type commissionRulePayload struct {
|
|
RuleName string `json:"rule_name"`
|
|
CalcType string `json:"calc_type"`
|
|
Value *float64 `json:"value"`
|
|
Steps []struct {
|
|
Min *float64 `json:"min"`
|
|
Max *float64 `json:"max"`
|
|
Rate *float64 `json:"rate"`
|
|
} `json:"steps"`
|
|
OrderType string `json:"order_type"`
|
|
Status *int8 `json:"status"`
|
|
Remark string `json:"remark"`
|
|
}
|
|
|
|
// buildTierJSON 组装阶梯配置(金额分;rate 存百分比×100)
|
|
func buildTierJSON(steps []struct {
|
|
Min *float64 `json:"min"`
|
|
Max *float64 `json:"max"`
|
|
Rate *float64 `json:"rate"`
|
|
}) (string, error) {
|
|
if len(steps) == 0 {
|
|
return "", fmt.Errorf("阶梯规则至少需要一档")
|
|
}
|
|
tiers := make([]models.CommissionTier, 0, len(steps))
|
|
for i, s := range steps {
|
|
if s.Min == nil || s.Rate == nil {
|
|
return "", fmt.Errorf("第 %d 档的起始金额与比例必填", i+1)
|
|
}
|
|
tier := models.CommissionTier{
|
|
Min: int64(*s.Min + 0.5),
|
|
Rate: int64(*s.Rate*100 + 0.5),
|
|
}
|
|
if s.Max != nil && *s.Max > 0 {
|
|
mx := int64(*s.Max + 0.5)
|
|
tier.Max = &mx
|
|
if mx <= tier.Min {
|
|
return "", fmt.Errorf("第 %d 档结束金额需大于起始金额", i+1)
|
|
}
|
|
}
|
|
if tier.Rate <= 0 {
|
|
return "", fmt.Errorf("第 %d 档比例需大于 0", i+1)
|
|
}
|
|
tiers = append(tiers, tier)
|
|
}
|
|
b, err := json.Marshal(tiers)
|
|
return string(b), err
|
|
}
|
|
|
|
// RulesList GET /platform/commission/rules 规则列表
|
|
func (c *PlatformCommissionController) RulesList() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
page, _ := strconv.Atoi(c.GetString("page", "1"))
|
|
pageSize, _ := strconv.Atoi(c.GetString("pageSize", "20"))
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
if pageSize <= 0 || pageSize > 200 {
|
|
pageSize = 20
|
|
}
|
|
qs := models.Orm.QueryTable(new(models.PlatformCommissionRule)).Filter("delete_time__isnull", true)
|
|
if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" {
|
|
qs = qs.Filter("rule_name__icontains", kw)
|
|
}
|
|
total, err := qs.Count()
|
|
if err != nil {
|
|
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
|
return
|
|
}
|
|
var rows []models.PlatformCommissionRule
|
|
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
|
if err != nil && err != orm.ErrNoRows {
|
|
c.jsonErr(500, 500, "查询失败: "+err.Error())
|
|
return
|
|
}
|
|
if rows == nil {
|
|
rows = []models.PlatformCommissionRule{}
|
|
}
|
|
list := make([]map[string]interface{}, 0, len(rows))
|
|
for i := range rows {
|
|
list = append(list, commissionRuleDTO(&rows[i]))
|
|
}
|
|
c.ok(map[string]interface{}{"list": list, "total": total, "page": page, "pageSize": pageSize})
|
|
}
|
|
|
|
// RuleCreate POST /platform/commission/rules 新增规则
|
|
func (c *PlatformCommissionController) RuleCreate() {
|
|
claims, err := c.platformClaims()
|
|
if err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
|
var p commissionRulePayload
|
|
if err := json.Unmarshal(body, &p); err != nil {
|
|
c.jsonErr(400, 400, "参数错误")
|
|
return
|
|
}
|
|
if strings.TrimSpace(p.RuleName) == "" {
|
|
c.jsonErr(400, 400, "规则名称不能为空")
|
|
return
|
|
}
|
|
row := &models.PlatformCommissionRule{
|
|
RuleName: strings.TrimSpace(p.RuleName),
|
|
CalcType: strings.TrimSpace(p.CalcType),
|
|
OrderType: strings.TrimSpace(p.OrderType),
|
|
Status: 1,
|
|
Remark: strings.TrimSpace(p.Remark),
|
|
CreateUserID: fmt.Sprintf("%d", claims.UserID),
|
|
CreateUserName: claims.Username,
|
|
}
|
|
if p.Status != nil {
|
|
row.Status = *p.Status
|
|
}
|
|
if err := applyCommissionRuleValues(row, &p); err != nil {
|
|
c.jsonErr(400, 400, err.Error())
|
|
return
|
|
}
|
|
if _, err := models.Orm.Insert(row); err != nil {
|
|
c.jsonErr(500, 500, "创建失败: "+err.Error())
|
|
return
|
|
}
|
|
c.ok(commissionRuleDTO(row))
|
|
}
|
|
|
|
// RuleUpdate POST /platform/commission/rules/:id 更新规则
|
|
func (c *PlatformCommissionController) RuleUpdate() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.jsonErr(400, 400, "无效ID")
|
|
return
|
|
}
|
|
row := &models.PlatformCommissionRule{}
|
|
if err := models.Orm.QueryTable(new(models.PlatformCommissionRule)).
|
|
Filter("id", id).Filter("delete_time__isnull", true).One(row); err != nil {
|
|
c.jsonErr(404, 404, "规则不存在")
|
|
return
|
|
}
|
|
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
|
var p commissionRulePayload
|
|
if err := json.Unmarshal(body, &p); err != nil {
|
|
c.jsonErr(400, 400, "参数错误")
|
|
return
|
|
}
|
|
if name := strings.TrimSpace(p.RuleName); name != "" {
|
|
row.RuleName = name
|
|
}
|
|
if calc := strings.TrimSpace(p.CalcType); calc != "" {
|
|
row.CalcType = calc
|
|
}
|
|
if ot := strings.TrimSpace(p.OrderType); ot != "" {
|
|
row.OrderType = ot
|
|
}
|
|
row.Remark = strings.TrimSpace(p.Remark)
|
|
if p.Status != nil {
|
|
row.Status = *p.Status
|
|
}
|
|
if err := applyCommissionRuleValues(row, &p); err != nil {
|
|
c.jsonErr(400, 400, err.Error())
|
|
return
|
|
}
|
|
if _, err := models.Orm.Update(row); err != nil {
|
|
c.jsonErr(500, 500, "更新失败: "+err.Error())
|
|
return
|
|
}
|
|
c.ok(commissionRuleDTO(row))
|
|
}
|
|
|
|
// applyCommissionRuleValues 按计算方式写入 value / tier_json
|
|
func applyCommissionRuleValues(row *models.PlatformCommissionRule, p *commissionRulePayload) error {
|
|
switch row.CalcType {
|
|
case models.CommissionCalcPercent:
|
|
if p.Value == nil || *p.Value <= 0 {
|
|
return fmt.Errorf("请输入大于 0 的比例值")
|
|
}
|
|
row.Value = int64(*p.Value*100 + 0.5)
|
|
row.TierJSON = nil
|
|
case models.CommissionCalcFixed:
|
|
if p.Value == nil || *p.Value <= 0 {
|
|
return fmt.Errorf("请输入大于 0 的固定金额(分)")
|
|
}
|
|
row.Value = int64(*p.Value + 0.5)
|
|
row.TierJSON = nil
|
|
case models.CommissionCalcTiered:
|
|
tierJSON, err := buildTierJSON(p.Steps)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row.Value = 0
|
|
row.TierJSON = &tierJSON
|
|
default:
|
|
return fmt.Errorf("计算方式不合法")
|
|
}
|
|
switch row.OrderType {
|
|
case models.CommissionOrderTypeAll, models.CommissionOrderTypePlatformUsage,
|
|
models.CommissionOrderTypeModuleShop, models.CommissionOrderTypeServiceFee:
|
|
default:
|
|
return fmt.Errorf("适用订单类型不合法")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RuleDelete DELETE /platform/commission/rules/:id 删除规则(软删除;历史佣金单不受影响)
|
|
func (c *PlatformCommissionController) RuleDelete() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.jsonErr(400, 400, "无效ID")
|
|
return
|
|
}
|
|
now := time.Now()
|
|
if _, err := models.Orm.QueryTable(new(models.PlatformCommissionRule)).
|
|
Filter("id", id).
|
|
Update(orm.Params{"delete_time": now, "update_time": now}); err != nil {
|
|
c.jsonErr(500, 500, "删除失败: "+err.Error())
|
|
return
|
|
}
|
|
c.ok(nil)
|
|
}
|
|
|
|
// RuleToggle POST /platform/commission/rules/:id/status 启用/停用
|
|
func (c *PlatformCommissionController) RuleToggle() {
|
|
if _, err := c.platformClaims(); err != nil {
|
|
c.jsonErr(401, 401, err.Error())
|
|
return
|
|
}
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.jsonErr(400, 400, "无效ID")
|
|
return
|
|
}
|
|
body, _ := io.ReadAll(c.Ctx.Request.Body)
|
|
var p struct {
|
|
Status *int8 `json:"status"`
|
|
}
|
|
if err := json.Unmarshal(body, &p); err != nil || p.Status == nil {
|
|
c.jsonErr(400, 400, "参数错误")
|
|
return
|
|
}
|
|
if _, err := models.Orm.QueryTable(new(models.PlatformCommissionRule)).
|
|
Filter("id", id).
|
|
Update(orm.Params{"status": *p.Status, "update_time": time.Now()}); err != nil {
|
|
c.jsonErr(500, 500, "操作失败: "+err.Error())
|
|
return
|
|
}
|
|
c.ok(map[string]interface{}{"status": *p.Status})
|
|
}
|