This commit is contained in:
liangzai 2025-05-11 01:00:40 +08:00
parent 0d0ad3bee8
commit 26c403732a
15 changed files with 729 additions and 7 deletions

View File

@ -57,6 +57,14 @@ service main {
// 获取推广定价配置
@handler GetAgentProductConfig
get /product_config returns (AgentProductConfigResp)
// 获取下级分页列表
@handler GetAgentSubordinateList
get /subordinate/list (GetAgentSubordinateListReq) returns (GetAgentSubordinateListResp)
// 下级贡献详情
@handler GetAgentSubordinateContributionDetail
get /subordinate/contribution/detail (GetAgentSubordinateContributionDetailReq) returns (GetAgentSubordinateContributionDetailResp)
}
type (
@ -85,6 +93,63 @@ type (
AgentProductConfigResp {
AgentProductConfig []AgentProductConfig
}
GetAgentSubordinateListReq {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量
}
GetAgentSubordinateListResp {
Total int64 `json:"total"` // 总记录数
List []AgentSubordinateList `json:"list"` // 查询列表
}
AgentSubordinateList {
ID int64 `json:"id"`
Mobile string `json:"mobile"`
CreateTime string `json:"create_time"`
LevelName string `json:"level_name"`
TotalOrders int64 `json:"total_orders"` // 总单量
TotalEarnings float64 `json:"total_earnings"` // 总金额
TotalContribution float64 `json:"total_contribution"` // 总贡献
}
GetAgentSubordinateContributionDetailReq {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量
SubordinateID int64 `form:"subordinate_id"` // 下级ID
}
GetAgentSubordinateContributionDetailResp {
Mobile string `json:"mobile"`
Total int64 `json:"total"` // 总记录数
CreateTime string `json:"create_time"`
TotalEarnings float64 `json:"total_earnings"` // 总金额
TotalContribution float64 `json:"total_contribution"` // 总贡献
TotalOrders int64 `json:"total_orders"` // 总单量
LevelName string `json:"level_name"` // 等级名称
List []AgentSubordinateContributionDetail `json:"list"` // 查询列表
Stats AgentSubordinateContributionStats `json:"stats"` // 统计数据
}
AgentSubordinateContributionDetail {
ID int64 `json:"id"`
CreateTime string `json:"create_time"`
Amount float64 `json:"amount"`
Type string `json:"type"`
}
AgentSubordinateContributionStats {
CostCount int64 `json:"cost_count"` // 成本扣除次数
CostAmount float64 `json:"cost_amount"` // 成本扣除总额
PricingCount int64 `json:"pricing_count"` // 定价扣除次数
PricingAmount float64 `json:"pricing_amount"` // 定价扣除总额
DescendantPromotionCount int64 `json:"descendant_promotion_count"` // 下级推广次数
DescendantPromotionAmount float64 `json:"descendant_promotion_amount"` // 下级推广总额
DescendantUpgradeVipCount int64 `json:"descendant_upgrade_vip_count"` // 下级升级VIP次数
DescendantUpgradeVipAmount float64 `json:"descendant_upgrade_vip_amount"` // 下级升级VIP总额
DescendantUpgradeSvipCount int64 `json:"descendant_upgrade_svip_count"` // 下级升级SVIP次数
DescendantUpgradeSvipAmount float64 `json:"descendant_upgrade_svip_amount"` // 下级升级SVIP总额
DescendantStayActiveCount int64 `json:"descendant_stay_active_count"` // 下级保持活跃次数
DescendantStayActiveAmount float64 `json:"descendant_stay_active_amount"` // 下级保持活跃总额
DescendantNewActiveCount int64 `json:"descendant_new_active_count"` // 下级新增活跃次数
DescendantNewActiveAmount float64 `json:"descendant_new_active_amount"` // 下级新增活跃总额
DescendantWithdrawCount int64 `json:"descendant_withdraw_count"` // 下级提现次数
DescendantWithdrawAmount float64 `json:"descendant_withdraw_amount"` // 下级提现总额
}
)
@server (

View File

@ -45,6 +45,8 @@ Wxpay:
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
MchPublicKeyID: "XXXXXX"
MchPublicKeyPath: "etc/merchant/pub_key.pem"
NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback"
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback"
Applepay:

View File

@ -46,6 +46,8 @@ Wxpay:
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
MchPublicKeyID: "XXXXXX"
MchPublicKeyPath: "etc/merchant/pub_key.pem"
NotifyUrl: "https://www.quannengcha.com/api/v1/pay/wechat/callback"
RefundNotifyUrl: "https://www.quannengcha.com/api/v1/wechat/refund_callback"
Applepay:

View File

@ -56,6 +56,8 @@ type WxpayConfig struct {
MchCertificateSerialNumber string
MchApiv3Key string
MchPrivateKeyPath string
MchPublicKeyID string
MchPublicKeyPath string
NotifyUrl string
RefundNotifyUrl string
}

View File

@ -0,0 +1,30 @@
package agent
import (
"net/http"
"qnc-server/app/user/cmd/api/internal/logic/agent"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/common/result"
"qnc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func GetAgentSubordinateContributionDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetAgentSubordinateContributionDetailReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := agent.NewGetAgentSubordinateContributionDetailLogic(r.Context(), svcCtx)
resp, err := l.GetAgentSubordinateContributionDetail(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@ -0,0 +1,30 @@
package agent
import (
"net/http"
"qnc-server/app/user/cmd/api/internal/logic/agent"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/common/result"
"qnc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func GetAgentSubordinateListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetAgentSubordinateListReq
if err := httpx.Parse(r, &req); err != nil {
result.ParamErrorResult(r, w, err)
return
}
if err := validator.Validate(req); err != nil {
result.ParamValidateErrorResult(r, w, err)
return
}
l := agent.NewGetAgentSubordinateListLogic(r.Context(), svcCtx)
resp, err := l.GetAgentSubordinateList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@ -3,12 +3,13 @@ package pay
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"qnc-server/app/user/cmd/api/internal/logic/pay"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/common/result"
"qnc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func PaymentCheckHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -40,6 +40,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/product_config",
Handler: agent.GetAgentProductConfigHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/subordinate/contribution/detail",
Handler: agent.GetAgentSubordinateContributionDetailHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/subordinate/list",
Handler: agent.GetAgentSubordinateListHandler(serverCtx),
},
},
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
rest.WithPrefix("/api/v1/agent"),

View File

@ -3,12 +3,13 @@ package user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"qnc-server/app/user/cmd/api/internal/logic/user"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/common/result"
"qnc-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func BindMobileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {

View File

@ -0,0 +1,200 @@
package agent
import (
"context"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/common/ctxdata"
"qnc-server/common/xerr"
"qnc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetAgentSubordinateContributionDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAgentSubordinateContributionDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentSubordinateContributionDetailLogic {
return &GetAgentSubordinateContributionDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAgentSubordinateContributionDetailLogic) GetAgentSubordinateContributionDetail(req *types.GetAgentSubordinateContributionDetailReq) (resp *types.GetAgentSubordinateContributionDetailResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 获取用户ID%v", err)
}
// 获取当前代理信息
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取代理信息%v", err)
}
// 获取下级代理信息
subordinateAgent, err := l.svcCtx.AgentModel.FindOne(l.ctx, req.SubordinateID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取下级代理信息%v", err)
}
// 验证是否是当前代理的下级
closureBuilder := l.svcCtx.AgentClosureModel.SelectBuilder().Where(squirrel.Eq{
"ancestor_id": agentModel.Id,
"descendant_id": req.SubordinateID,
})
closureList, err := l.svcCtx.AgentClosureModel.FindAll(l.ctx, closureBuilder, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 验证代理关系%v", err)
}
if len(closureList) == 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 非法的代理关系")
}
closure := closureList[0]
// 获取佣金扣除记录
deductionBuilder := l.svcCtx.AgentCommissionDeductionModel.SelectBuilder().Where(squirrel.Eq{
"agent_id": agentModel.Id,
"deducted_agent_id": req.SubordinateID,
})
deductionList, err := l.svcCtx.AgentCommissionDeductionModel.FindAll(l.ctx, deductionBuilder, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取佣金扣除记录%v", err)
}
// 获取奖励记录
rewardsBuilder := l.svcCtx.AgentRewardsModel.SelectBuilder().Where(squirrel.Eq{
"agent_id": agentModel.Id,
"relation_agent_id": req.SubordinateID,
})
rewards, err := l.svcCtx.AgentRewardsModel.FindAll(l.ctx, rewardsBuilder, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取奖励记录%v", err)
}
// 计算总贡献
var totalContribution float64
for _, v := range deductionList {
totalContribution += v.Amount
}
// 加上奖励金额
for _, v := range rewards {
totalContribution += v.Amount
}
// 获取佣金记录
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{
"agent_id": req.SubordinateID,
})
commissionList, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, commissionBuilder, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取佣金记录%v", err)
}
// 计算总收益和总单量
var totalEarnings float64
for _, v := range commissionList {
totalEarnings += v.Amount
}
// 初始化统计数据
stats := types.AgentSubordinateContributionStats{
CostCount: 0,
CostAmount: 0,
PricingCount: 0,
PricingAmount: 0,
DescendantPromotionCount: 0,
DescendantPromotionAmount: 0,
DescendantUpgradeVipCount: 0,
DescendantUpgradeVipAmount: 0,
DescendantUpgradeSvipCount: 0,
DescendantUpgradeSvipAmount: 0,
DescendantStayActiveCount: 0,
DescendantStayActiveAmount: 0,
DescendantNewActiveCount: 0,
DescendantNewActiveAmount: 0,
DescendantWithdrawCount: 0,
DescendantWithdrawAmount: 0,
}
// 统计佣金扣除记录
for _, v := range deductionList {
switch v.Type {
case "cost":
stats.CostCount++
stats.CostAmount += v.Amount
case "pricing":
stats.PricingCount++
stats.PricingAmount += v.Amount
}
}
// 统计奖励记录
for _, v := range rewards {
switch v.Type {
case "descendant_promotion":
stats.DescendantPromotionCount++
stats.DescendantPromotionAmount += v.Amount
case "descendant_upgrade_vip":
stats.DescendantUpgradeVipCount++
stats.DescendantUpgradeVipAmount += v.Amount
case "descendant_upgrade_svip":
stats.DescendantUpgradeSvipCount++
stats.DescendantUpgradeSvipAmount += v.Amount
case "descendant_stay_active":
stats.DescendantStayActiveCount++
stats.DescendantStayActiveAmount += v.Amount
case "descendant_new_active":
stats.DescendantNewActiveCount++
stats.DescendantNewActiveAmount += v.Amount
case "descendant_withdraw":
stats.DescendantWithdrawCount++
stats.DescendantWithdrawAmount += v.Amount
}
}
// 解密手机号
secretKey := l.svcCtx.Config.Encrypt.SecretKey
mobile, err := crypto.DecryptMobile(subordinateAgent.Mobile, secretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级贡献详情, 解密手机号失败: %v", err)
}
// 获取合并后的分页列表
unionDetails, total, err := l.svcCtx.AgentClosureModel.FindUnionPageListByPageWithTotal(l.ctx, agentModel.Id, req.SubordinateID, req.Page, req.PageSize)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级贡献详情, 获取分页列表%v", err)
}
// 转换为响应类型
detailList := make([]types.AgentSubordinateContributionDetail, 0, len(unionDetails))
for _, v := range unionDetails {
detail := types.AgentSubordinateContributionDetail{
ID: v.Id,
CreateTime: v.CreateTime,
Amount: v.Amount,
Type: v.Type,
}
detailList = append(detailList, detail)
}
return &types.GetAgentSubordinateContributionDetailResp{
Mobile: maskPhone(mobile),
Total: total,
CreateTime: closure.CreateTime.Format("2006-01-02 15:04:05"),
TotalEarnings: totalEarnings,
TotalContribution: totalContribution,
TotalOrders: int64(len(commissionList)),
LevelName: subordinateAgent.LevelName,
List: detailList,
Stats: stats,
}, nil
}

View File

@ -0,0 +1,173 @@
package agent
import (
"context"
"strings"
"time"
"qnc-server/app/user/cmd/api/internal/svc"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/app/user/model"
"qnc-server/common/ctxdata"
"qnc-server/common/xerr"
"qnc-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type GetAgentSubordinateListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetAgentSubordinateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentSubordinateListLogic {
return &GetAgentSubordinateListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetAgentSubordinateListLogic) GetAgentSubordinateList(req *types.GetAgentSubordinateListReq) (resp *types.GetAgentSubordinateListResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理下级列表, 获取用户ID%v", err)
}
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理信息%v", err)
}
agentID := agentModel.Id
builder := l.svcCtx.AgentClosureModel.SelectBuilder().Where(squirrel.Eq{
"ancestor_id": agentID,
})
agentClosureModelList, total, err := l.svcCtx.AgentClosureModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理关系%v", err)
}
// 构建ID到CreateTime的映射
createTimeMap := make(map[int64]time.Time)
descendantIDs := make([]int64, 0)
for _, v := range agentClosureModelList {
descendantIDs = append(descendantIDs, v.DescendantId)
createTimeMap[v.DescendantId] = v.CreateTime
}
// 并发查询代理信息
agentMap := make(map[int64]*model.Agent)
var descendantList []types.AgentSubordinateList
err = mr.Finish(func() error {
return mr.MapReduceVoid(func(source chan<- interface{}) {
for _, id := range descendantIDs {
source <- id
}
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
id := item.(int64)
agent, err := l.svcCtx.AgentModel.FindOne(l.ctx, id)
if err != nil {
cancel(err)
return
}
writer.Write(agent)
}, func(pipe <-chan interface{}, cancel func(error)) {
for item := range pipe {
agent := item.(*model.Agent)
agentMap[agent.Id] = agent
}
})
}, func() error {
// 并发查询佣金扣除信息
deductionBuilder := l.svcCtx.AgentCommissionDeductionModel.SelectBuilder().
Where(squirrel.Eq{"agent_id": agentID}).
Where(squirrel.Eq{"deducted_agent_id": descendantIDs})
deductionList, err := l.svcCtx.AgentCommissionDeductionModel.FindAll(l.ctx, deductionBuilder, "")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理佣金扣除信息%v", err)
}
deductionMap := make(map[int64]float64)
for _, v := range deductionList {
deductionMap[v.DeductedAgentId] += v.Amount
}
// 并发查询奖励信息
rewardsBuilder := l.svcCtx.AgentRewardsModel.SelectBuilder().
Where(squirrel.Eq{"agent_id": agentID}).
Where(squirrel.Eq{"relation_agent_id": descendantIDs})
rewardsList, err := l.svcCtx.AgentRewardsModel.FindAll(l.ctx, rewardsBuilder, "")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理奖励信息%v", err)
}
rewardsMap := make(map[int64]float64)
for _, v := range rewardsList {
if v.RelationAgentId.Valid {
rewardsMap[v.RelationAgentId.Int64] += v.Amount
}
}
// 并发查询佣金信息
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where(squirrel.Eq{"agent_id": descendantIDs})
commissionList, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, commissionBuilder, "")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理下级列表, 获取代理佣金信息%v", err)
}
commissionMap := make(map[int64]float64)
orderCountMap := make(map[int64]int64)
for _, v := range commissionList {
commissionMap[v.AgentId] += v.Amount
orderCountMap[v.AgentId]++
}
// 构建返回结果
secretKey := l.svcCtx.Config.Encrypt.SecretKey
descendantList = make([]types.AgentSubordinateList, 0, len(descendantIDs))
for _, id := range descendantIDs {
agent, exists := agentMap[id]
if !exists {
continue
}
mobile, err := crypto.DecryptMobile(agent.Mobile, secretKey)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, 解密手机号失败: %v", err)
}
subordinate := types.AgentSubordinateList{
ID: id,
Mobile: maskPhone(mobile),
LevelName: agent.LevelName,
CreateTime: createTimeMap[id].Format("2006-01-02 15:04:05"),
TotalContribution: deductionMap[id] + rewardsMap[id],
TotalEarnings: commissionMap[id],
TotalOrders: orderCountMap[id],
}
descendantList = append(descendantList, subordinate)
}
return nil
})
if err != nil {
return nil, err
}
return &types.GetAgentSubordinateListResp{
Total: total,
List: descendantList,
}, nil
}
// 手机号脱敏
func maskPhone(phone string) string {
length := len(phone)
if length < 8 {
return phone // 如果长度太短,可能不是手机号,不处理
}
// 保留前3位和后4位
return phone[:3] + strings.Repeat("*", length-7) + phone[length-4:]
}

View File

@ -34,6 +34,14 @@ const (
TradeStatePayError = "PAYERROR" // 支付失败(其他原因,如银行返回失败)
)
// InitType 初始化类型
type InitType string
const (
InitTypePlatformCert InitType = "platform_cert" // 平台证书初始化
InitTypeWxPayPubKey InitType = "wxpay_pubkey" // 微信支付公钥初始化
)
type WechatPayService struct {
config config.WxpayConfig
wechatClient *core.Client
@ -41,8 +49,21 @@ type WechatPayService struct {
userAuthModel model.UserAuthModel
}
// NewWechatPayService 初始化微信支付服务
func NewWechatPayService(c config.Config, userAuthModel model.UserAuthModel) *WechatPayService {
// NewWechatPayService 创建微信支付服务实例
func NewWechatPayService(c config.Config, userAuthModel model.UserAuthModel, initType InitType) *WechatPayService {
switch initType {
case InitTypePlatformCert:
return newWechatPayServiceWithPlatformCert(c, userAuthModel)
case InitTypeWxPayPubKey:
return newWechatPayServiceWithWxPayPubKey(c, userAuthModel)
default:
logx.Errorf("不支持的初始化类型: %s", initType)
panic(fmt.Sprintf("初始化失败,服务停止: %s", initType))
}
}
// newWechatPayServiceWithPlatformCert 使用平台证书初始化微信支付服务
func newWechatPayServiceWithPlatformCert(c config.Config, userAuthModel model.UserAuthModel) *WechatPayService {
// 从配置中加载商户信息
mchID := c.Wxpay.MchID
mchCertificateSerialNumber := c.Wxpay.MchCertificateSerialNumber
@ -64,14 +85,62 @@ func NewWechatPayService(c config.Config, userAuthModel model.UserAuthModel) *We
logx.Errorf("创建微信支付客户端失败: %v", err)
panic(fmt.Sprintf("初始化失败,服务停止: %v", err)) // 记录错误并停止程序
}
// 在初始化时获取证书访问器并创建 notifyHandler
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
notifyHandler, err := notify.NewRSANotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
if err != nil {
logx.Errorf("获取证书访问器失败: %v", err)
panic(fmt.Sprintf("初始化失败,服务停止: %v", err)) // 记录错误并停止程序
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
}
logx.Infof("微信支付客户端初始化成功")
logx.Infof("微信支付客户端初始化成功(平台证书方式)")
return &WechatPayService{
config: c.Wxpay,
wechatClient: client,
notifyHandler: notifyHandler,
userAuthModel: userAuthModel,
}
}
// newWechatPayServiceWithWxPayPubKey 使用微信支付公钥初始化微信支付服务
func newWechatPayServiceWithWxPayPubKey(c config.Config, userAuthModel model.UserAuthModel) *WechatPayService {
// 从配置中加载商户信息
mchID := c.Wxpay.MchID
mchCertificateSerialNumber := c.Wxpay.MchCertificateSerialNumber
mchAPIv3Key := c.Wxpay.MchApiv3Key
mchPrivateKeyPath := c.Wxpay.MchPrivateKeyPath
mchPublicKeyID := c.Wxpay.MchPublicKeyID
mchPublicKeyPath := c.Wxpay.MchPublicKeyPath
// 从文件中加载商户私钥
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(mchPrivateKeyPath)
if err != nil {
logx.Errorf("加载商户私钥失败: %v", err)
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
}
// 从文件中加载微信支付平台证书
mchPublicKey, err := utils.LoadPublicKeyWithPath(mchPublicKeyPath)
if err != nil {
logx.Errorf("加载微信支付平台证书失败: %v", err)
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
}
// 使用商户私钥和其他参数初始化微信支付客户端
opts := []core.ClientOption{
option.WithWechatPayPublicKeyAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchPublicKeyID, mchPublicKey),
}
client, err := core.NewClient(context.Background(), opts...)
if err != nil {
logx.Errorf("创建微信支付客户端失败: %v", err)
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
}
// 初始化 notify.Handler
notifyHandler := notify.NewNotifyHandler(
mchAPIv3Key,
verifiers.NewSHA256WithRSAPubkeyVerifier(mchPublicKeyID, *mchPublicKey))
logx.Infof("微信支付客户端初始化成功(微信支付公钥方式)")
return &WechatPayService{
config: c.Wxpay,
wechatClient: client,

View File

@ -105,7 +105,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
exampleModel := model.NewExampleModel(db, c.CacheRedis)
alipayService := service.NewAliPayService(c)
wechatPayService := service.NewWechatPayService(c, userAuthModel)
wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypePlatformCert)
applePayService := service.NewApplePayService(c)
apiRequestService := service.NewApiRequestService(c, westDexService, yushanService, featureModel, productFeatureModel)
verificationService := service.NewVerificationService(c, westDexService, apiRequestService)

View File

@ -98,6 +98,42 @@ type AgentProductConfigResp struct {
AgentProductConfig []AgentProductConfig
}
type AgentSubordinateContributionDetail struct {
ID int64 `json:"id"`
CreateTime string `json:"create_time"`
Amount float64 `json:"amount"`
Type string `json:"type"`
}
type AgentSubordinateContributionStats struct {
CostCount int64 `json:"cost_count"` // 成本扣除次数
CostAmount float64 `json:"cost_amount"` // 成本扣除总额
PricingCount int64 `json:"pricing_count"` // 定价扣除次数
PricingAmount float64 `json:"pricing_amount"` // 定价扣除总额
DescendantPromotionCount int64 `json:"descendant_promotion_count"` // 下级推广次数
DescendantPromotionAmount float64 `json:"descendant_promotion_amount"` // 下级推广总额
DescendantUpgradeVipCount int64 `json:"descendant_upgrade_vip_count"` // 下级升级VIP次数
DescendantUpgradeVipAmount float64 `json:"descendant_upgrade_vip_amount"` // 下级升级VIP总额
DescendantUpgradeSvipCount int64 `json:"descendant_upgrade_svip_count"` // 下级升级SVIP次数
DescendantUpgradeSvipAmount float64 `json:"descendant_upgrade_svip_amount"` // 下级升级SVIP总额
DescendantStayActiveCount int64 `json:"descendant_stay_active_count"` // 下级保持活跃次数
DescendantStayActiveAmount float64 `json:"descendant_stay_active_amount"` // 下级保持活跃总额
DescendantNewActiveCount int64 `json:"descendant_new_active_count"` // 下级新增活跃次数
DescendantNewActiveAmount float64 `json:"descendant_new_active_amount"` // 下级新增活跃总额
DescendantWithdrawCount int64 `json:"descendant_withdraw_count"` // 下级提现次数
DescendantWithdrawAmount float64 `json:"descendant_withdraw_amount"` // 下级提现总额
}
type AgentSubordinateList struct {
ID int64 `json:"id"`
Mobile string `json:"mobile"`
CreateTime string `json:"create_time"`
LevelName string `json:"level_name"`
TotalOrders int64 `json:"total_orders"` // 总单量
TotalEarnings float64 `json:"total_earnings"` // 总金额
TotalContribution float64 `json:"total_contribution"` // 总贡献
}
type BindMobileReq struct {
Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"`
@ -137,6 +173,34 @@ type GetAgentRevenueInfoResp struct {
ActiveReward ActiveReward `json:"active_reward"` // 活跃下级奖励数据
}
type GetAgentSubordinateContributionDetailReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量
SubordinateID int64 `form:"subordinate_id"` // 下级ID
}
type GetAgentSubordinateContributionDetailResp struct {
Mobile string `json:"mobile"`
Total int64 `json:"total"` // 总记录数
CreateTime string `json:"create_time"`
TotalEarnings float64 `json:"total_earnings"` // 总金额
TotalContribution float64 `json:"total_contribution"` // 总贡献
TotalOrders int64 `json:"total_orders"` // 总单量
LevelName string `json:"level_name"` // 等级名称
List []AgentSubordinateContributionDetail `json:"list"` // 查询列表
Stats AgentSubordinateContributionStats `json:"stats"` // 统计数据
}
type GetAgentSubordinateListReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量
}
type GetAgentSubordinateListResp struct {
Total int64 `json:"total"` // 总记录数
List []AgentSubordinateList `json:"list"` // 查询列表
}
type GetCommissionReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数据量

View File

@ -1,6 +1,12 @@
package model
import (
"context"
"fmt"
"qnc-server/common/globalkey"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
@ -12,11 +18,20 @@ type (
// and implement the added methods in customAgentClosureModel.
AgentClosureModel interface {
agentClosureModel
FindUnionPageListByPageWithTotal(ctx context.Context, agentId, subordinateId int64, page, pageSize int64) ([]*UnionDetail, int64, error)
}
customAgentClosureModel struct {
*defaultAgentClosureModel
}
// UnionDetail 合并后的详情结构
UnionDetail struct {
Id int64 `db:"id"`
CreateTime string `db:"create_time"`
Amount float64 `db:"amount"`
Type string `db:"type"`
}
)
// NewAgentClosureModel returns a model for the database table.
@ -25,3 +40,61 @@ func NewAgentClosureModel(conn sqlx.SqlConn, c cache.CacheConf) AgentClosureMode
defaultAgentClosureModel: newAgentClosureModel(conn, c),
}
}
// FindUnionPageListByPageWithTotal 获取合并后的分页列表
func (m *customAgentClosureModel) FindUnionPageListByPageWithTotal(ctx context.Context, agentId, subordinateId int64, page, pageSize int64) ([]*UnionDetail, int64, error) {
// 构建UNION ALL查询
deductionQuery := fmt.Sprintf(`
SELECT id, create_time, amount, type
FROM agent_commission_deduction
WHERE agent_id = ? AND deducted_agent_id = ? AND del_state = ?
`)
rewardsQuery := fmt.Sprintf(`
SELECT id, create_time, amount, type
FROM agent_rewards
WHERE agent_id = ? AND relation_agent_id = ? AND del_state = ?
`)
// 计算总记录数
countQuery := fmt.Sprintf(`
SELECT COUNT(*) FROM (
%s
UNION ALL
%s
) AS union_table
`, deductionQuery, rewardsQuery)
var total int64
err := m.QueryRowNoCacheCtx(ctx, &total, countQuery, agentId, subordinateId, globalkey.DelStateNo, agentId, subordinateId, globalkey.DelStateNo)
if err != nil {
return nil, 0, errors.Wrapf(err, "查询总记录数失败")
}
// 构建分页查询
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
unionQuery := fmt.Sprintf(`
SELECT * FROM (
%s
UNION ALL
%s
) AS union_table
ORDER BY create_time DESC
LIMIT ? OFFSET ?
`, deductionQuery, rewardsQuery)
var resp []*UnionDetail
err = m.QueryRowsNoCacheCtx(ctx, &resp, unionQuery,
agentId, subordinateId, globalkey.DelStateNo,
agentId, subordinateId, globalkey.DelStateNo,
pageSize, offset)
if err != nil {
return nil, 0, errors.Wrapf(err, "查询分页数据失败")
}
return resp, total, nil
}