Merge branch 'main' of http://1.117.67.95:3000/team/qnc-server-v3
This commit is contained in:
@@ -83,9 +83,9 @@ service main {
|
|||||||
@handler AdminGetInviteCodeList
|
@handler AdminGetInviteCodeList
|
||||||
get /invite_code/list (AdminGetInviteCodeListReq) returns (AdminGetInviteCodeListResp)
|
get /invite_code/list (AdminGetInviteCodeListReq) returns (AdminGetInviteCodeListResp)
|
||||||
|
|
||||||
// 平台升级代理等级(免费升级,遵守代理系统逻辑规则)
|
// 平台升级代理等级
|
||||||
@handler AdminUpgradeAgent
|
@handler AdminUpgradeAgent
|
||||||
post /upgrade (AdminUpgradeAgentReq) returns (AdminUpgradeAgentResp)
|
post /upgrade/agent (AdminUpgradeAgentReq) returns (AdminUpgradeAgentResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -128,14 +128,6 @@ type (
|
|||||||
AdminAuditAgentResp {
|
AdminAuditAgentResp {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
// 平台升级代理等级
|
|
||||||
AdminUpgradeAgentReq {
|
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
|
||||||
ToLevel int64 `json:"to_level"` // 目标等级:2=黄金,3=钻石
|
|
||||||
}
|
|
||||||
AdminUpgradeAgentResp {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
}
|
|
||||||
// 推广链接分页查询
|
// 推广链接分页查询
|
||||||
AdminGetAgentLinkListReq {
|
AdminGetAgentLinkListReq {
|
||||||
Page int64 `form:"page"` // 页码
|
Page int64 `form:"page"` // 页码
|
||||||
@@ -165,6 +157,7 @@ type (
|
|||||||
AgentId *string `form:"agent_id,optional"` // 代理ID(可选)
|
AgentId *string `form:"agent_id,optional"` // 代理ID(可选)
|
||||||
OrderId *string `form:"order_id,optional"` // 订单ID(可选)
|
OrderId *string `form:"order_id,optional"` // 订单ID(可选)
|
||||||
ProcessStatus *int64 `form:"process_status,optional"` // 处理状态(可选)
|
ProcessStatus *int64 `form:"process_status,optional"` // 处理状态(可选)
|
||||||
|
OrderStatus *string `form:"order_status,optional"` // 订单状态(可选):pending-待支付,paid-已支付,refunded-已退款,closed-已关闭,failed-支付失败
|
||||||
}
|
}
|
||||||
AgentOrderListItem {
|
AgentOrderListItem {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
@@ -177,7 +170,8 @@ type (
|
|||||||
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
|
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
|
||||||
PriceCost float64 `json:"price_cost"` // 提价成本
|
PriceCost float64 `json:"price_cost"` // 提价成本
|
||||||
AgentProfit float64 `json:"agent_profit"` // 代理收益
|
AgentProfit float64 `json:"agent_profit"` // 代理收益
|
||||||
ProcessStatus int64 `json:"process_status"` // 处理状态
|
ProcessStatus int64 `json:"process_status"` // 处理状态(保留用于筛选,前端不显示)
|
||||||
|
OrderStatus string `json:"order_status"` // 订单状态:pending-待支付,paid-已支付,refunded-已退款,closed-已关闭,failed-支付失败
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
AdminGetAgentOrderListResp {
|
AdminGetAgentOrderListResp {
|
||||||
@@ -212,6 +206,7 @@ type (
|
|||||||
AgentId *string `form:"agent_id,optional"` // 代理ID(可选)
|
AgentId *string `form:"agent_id,optional"` // 代理ID(可选)
|
||||||
SourceAgentId *string `form:"source_agent_id,optional"` // 来源代理ID(可选)
|
SourceAgentId *string `form:"source_agent_id,optional"` // 来源代理ID(可选)
|
||||||
RebateType *int64 `form:"rebate_type,optional"` // 返佣类型(可选)
|
RebateType *int64 `form:"rebate_type,optional"` // 返佣类型(可选)
|
||||||
|
Status *int64 `form:"status,optional"` // 状态(可选):1=已发放,2=已冻结,3=已取消(已退款)
|
||||||
}
|
}
|
||||||
AgentRebateListItem {
|
AgentRebateListItem {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
@@ -220,6 +215,7 @@ type (
|
|||||||
OrderId string `json:"order_id"` // 订单ID
|
OrderId string `json:"order_id"` // 订单ID
|
||||||
RebateType int64 `json:"rebate_type"` // 返佣类型
|
RebateType int64 `json:"rebate_type"` // 返佣类型
|
||||||
Amount float64 `json:"amount"` // 金额
|
Amount float64 `json:"amount"` // 金额
|
||||||
|
Status int64 `json:"status"` // 状态:1=已发放,2=已冻结,3=已取消(已退款)
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
AdminGetAgentRebateListResp {
|
AdminGetAgentRebateListResp {
|
||||||
@@ -258,17 +254,20 @@ type (
|
|||||||
WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选)
|
WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选)
|
||||||
}
|
}
|
||||||
AgentWithdrawalListItem {
|
AgentWithdrawalListItem {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
WithdrawNo string `json:"withdraw_no"` // 提现单号
|
WithdrawNo string `json:"withdraw_no"` // 提现单号
|
||||||
Amount float64 `json:"amount"` // 金额
|
Amount float64 `json:"amount"` // 金额
|
||||||
TaxAmount float64 `json:"tax_amount"` // 税费金额
|
TaxAmount float64 `json:"tax_amount"` // 税费金额
|
||||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
|
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
|
||||||
Status int64 `json:"status"` // 状态
|
Status int64 `json:"status"` // 状态
|
||||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式:1=支付宝,2=银行卡
|
||||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
|
||||||
Remark string `json:"remark"` // 备注
|
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时使用)
|
||||||
|
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时使用)
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
AdminGetAgentWithdrawalListResp {
|
AdminGetAgentWithdrawalListResp {
|
||||||
Total int64 `json:"total"` // 总数
|
Total int64 `json:"total"` // 总数
|
||||||
@@ -434,5 +433,13 @@ type (
|
|||||||
Total int64 `json:"total"` // 总数
|
Total int64 `json:"total"` // 总数
|
||||||
Items []InviteCodeListItem `json:"items"` // 列表数据
|
Items []InviteCodeListItem `json:"items"` // 列表数据
|
||||||
}
|
}
|
||||||
|
// 平台升级代理等级
|
||||||
|
AdminUpgradeAgentReq {
|
||||||
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
|
ToLevel int64 `json:"to_level"` // 目标等级:2=黄金,3=钻石
|
||||||
|
}
|
||||||
|
AdminUpgradeAgentResp {
|
||||||
|
Success bool `json:"success"` // 是否成功
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ type (
|
|||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
PayTime string `json:"pay_time"` // 支付时间
|
PayTime string `json:"pay_time"` // 支付时间
|
||||||
RefundTime string `json:"refund_time"` // 退款时间
|
RefundTime string `json:"refund_time"` // 退款时间
|
||||||
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单
|
IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单
|
||||||
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态:not_agent-非代理订单,success-处理成功,failed-处理失败,pending-待处理
|
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态:not_agent-非代理订单,success-处理成功,failed-处理失败,pending-待处理
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
syntax = "v1"
|
syntax = "v1"
|
||||||
|
|
||||||
|
import "product.api"
|
||||||
|
|
||||||
info (
|
info (
|
||||||
title: "代理服务"
|
title: "代理服务"
|
||||||
desc: "新代理系统接口"
|
desc: "新代理系统接口"
|
||||||
@@ -121,6 +123,14 @@ type (
|
|||||||
GetInviteLinkResp {
|
GetInviteLinkResp {
|
||||||
InviteLink string `json:"invite_link"` // 邀请链接
|
InviteLink string `json:"invite_link"` // 邀请链接
|
||||||
}
|
}
|
||||||
|
// 生成邀请海报请求
|
||||||
|
GenerateInvitePosterReq {
|
||||||
|
InviteLink string `form:"invite_link"` // 邀请链接(用于生成二维码)
|
||||||
|
}
|
||||||
|
// 生成邀请海报响应
|
||||||
|
GenerateInvitePosterResp {
|
||||||
|
PosterUrl string `json:"poster_url"` // 海报图片URL(base64编码的data URL)
|
||||||
|
}
|
||||||
// 获取代理等级特权信息
|
// 获取代理等级特权信息
|
||||||
GetLevelPrivilegeResp {
|
GetLevelPrivilegeResp {
|
||||||
Levels []LevelPrivilegeItem `json:"levels"`
|
Levels []LevelPrivilegeItem `json:"levels"`
|
||||||
@@ -224,6 +234,10 @@ service main {
|
|||||||
@handler ApplyWithdrawal
|
@handler ApplyWithdrawal
|
||||||
post /withdrawal/apply (ApplyWithdrawalReq) returns (ApplyWithdrawalResp)
|
post /withdrawal/apply (ApplyWithdrawalReq) returns (ApplyWithdrawalResp)
|
||||||
|
|
||||||
|
// 获取上次提现信息(用于前端预填)
|
||||||
|
@handler GetLastWithdrawalInfo
|
||||||
|
get /withdrawal/last_info (GetLastWithdrawalInfoReq) returns (GetLastWithdrawalInfoResp)
|
||||||
|
|
||||||
// 实名认证
|
// 实名认证
|
||||||
@handler RealNameAuth
|
@handler RealNameAuth
|
||||||
post /real_name (RealNameAuthReq) returns (RealNameAuthResp)
|
post /real_name (RealNameAuthReq) returns (RealNameAuthResp)
|
||||||
@@ -244,6 +258,10 @@ service main {
|
|||||||
@handler GetInviteLink
|
@handler GetInviteLink
|
||||||
get /invite_link (GetInviteLinkReq) returns (GetInviteLinkResp)
|
get /invite_link (GetInviteLinkReq) returns (GetInviteLinkResp)
|
||||||
|
|
||||||
|
// 生成邀请海报(带二维码的图片)
|
||||||
|
@handler GenerateInvitePoster
|
||||||
|
get /invite/poster (GenerateInvitePosterReq) returns (GenerateInvitePosterResp)
|
||||||
|
|
||||||
// 获取代理等级特权信息
|
// 获取代理等级特权信息
|
||||||
@handler GetLevelPrivilege
|
@handler GetLevelPrivilege
|
||||||
get /level/privilege returns (GetLevelPrivilegeResp)
|
get /level/privilege returns (GetLevelPrivilegeResp)
|
||||||
@@ -277,6 +295,32 @@ service main {
|
|||||||
// 检查订单是否属于当前代理推广
|
// 检查订单是否属于当前代理推广
|
||||||
@handler CheckOrderAgent
|
@handler CheckOrderAgent
|
||||||
get /order/agent (CheckOrderAgentReq) returns (CheckOrderAgentResp)
|
get /order/agent (CheckOrderAgentReq) returns (CheckOrderAgentResp)
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 用户模块白名单相关接口
|
||||||
|
// ============================================
|
||||||
|
@handler GetWhitelistFeatures
|
||||||
|
get /whitelist/features (GetWhitelistFeaturesReq) returns (GetWhitelistFeaturesResp)
|
||||||
|
|
||||||
|
// 创建白名单订单
|
||||||
|
@handler CreateWhitelistOrder
|
||||||
|
post /whitelist/order/create (CreateWhitelistOrderReq) returns (CreateWhitelistOrderResp)
|
||||||
|
|
||||||
|
// 获取用户白名单列表
|
||||||
|
@handler GetWhitelistList
|
||||||
|
get /whitelist/list (GetWhitelistListReq) returns (GetWhitelistListResp)
|
||||||
|
|
||||||
|
// 检查模块是否已下架(用于显示下架按钮状态)
|
||||||
|
@handler CheckFeatureWhitelistStatus
|
||||||
|
get /whitelist/check (CheckFeatureWhitelistStatusReq) returns (CheckFeatureWhitelistStatusResp)
|
||||||
|
|
||||||
|
// 下架单个模块(创建订单并支付)
|
||||||
|
@handler OfflineFeature
|
||||||
|
post /whitelist/offline (OfflineFeatureReq) returns (OfflineFeatureResp)
|
||||||
|
|
||||||
|
// 检查订单是否属于当前代理推广
|
||||||
|
@handler CheckOrderAgent
|
||||||
|
get /order/agent (CheckOrderAgentReq) returns (CheckOrderAgentResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -439,28 +483,30 @@ type (
|
|||||||
LevelName string `json:"level_name"` // 等级名称
|
LevelName string `json:"level_name"` // 等级名称
|
||||||
Mobile string `json:"mobile"` // 手机号
|
Mobile string `json:"mobile"` // 手机号
|
||||||
CreateTime string `json:"create_time"` // 加入团队时间
|
CreateTime string `json:"create_time"` // 加入团队时间
|
||||||
TotalRebateAmount float64 `json:"total_rebate_amount"` // 返佣给我的总金额
|
|
||||||
TodayRebateAmount float64 `json:"today_rebate_amount"` // 返佣给我的今日金额
|
|
||||||
TodayInvites int64 `json:"today_invites"` // 邀请加入团队的今日人数
|
|
||||||
MonthInvites int64 `json:"month_invites"` // 邀请加入团队的本月人数
|
|
||||||
TotalInvites int64 `json:"total_invites"` // 邀请加入团队的总人数
|
|
||||||
TodayQueries int64 `json:"today_queries"` // 当日查询量
|
TodayQueries int64 `json:"today_queries"` // 当日查询量
|
||||||
MonthQueries int64 `json:"month_queries"` // 本月查询量
|
MonthQueries int64 `json:"month_queries"` // 本月查询量
|
||||||
TotalQueries int64 `json:"total_queries"` // 总查询量
|
TotalQueries int64 `json:"total_queries"` // 总查询量
|
||||||
|
TotalRebateAmount float64 `json:"total_rebate_amount"` // 返佣给我的总金额
|
||||||
|
TodayRebateAmount float64 `json:"today_rebate_amount"` // 返佣给我的今日金额
|
||||||
|
TotalInvites int64 `json:"total_invites"` // 邀请加入团队的总人数
|
||||||
|
TodayInvites int64 `json:"today_invites"` // 邀请加入团队的今日人数
|
||||||
|
MonthInvites int64 `json:"month_invites"` // 邀请加入团队的本月人数
|
||||||
IsDirect bool `json:"is_direct"` // 是否直接下级
|
IsDirect bool `json:"is_direct"` // 是否直接下级
|
||||||
}
|
}
|
||||||
// 收益信息
|
// 收益信息
|
||||||
GetRevenueInfoResp {
|
GetRevenueInfoResp {
|
||||||
Balance float64 `json:"balance"` // 可用余额
|
Balance float64 `json:"balance"` // 可用余额
|
||||||
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
|
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
|
||||||
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
|
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
|
||||||
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
|
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
|
||||||
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
|
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
|
||||||
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
|
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
|
||||||
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
|
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
|
||||||
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
|
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
|
||||||
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
|
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
|
||||||
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
|
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
|
||||||
|
AlipayMonthQuota float64 `json:"alipay_month_quota"` // 支付宝每月提现总额度
|
||||||
|
AlipayMonthUsed float64 `json:"alipay_month_used"` // 本月已使用的支付宝提现额度
|
||||||
}
|
}
|
||||||
// 佣金记录
|
// 佣金记录
|
||||||
GetCommissionListReq {
|
GetCommissionListReq {
|
||||||
@@ -499,6 +545,7 @@ type (
|
|||||||
OrderNo string `json:"order_no"` // 订单号
|
OrderNo string `json:"order_no"` // 订单号
|
||||||
RebateType int64 `json:"rebate_type"` // 返佣类型:1=直接上级,2=钻石上级,3=黄金上级
|
RebateType int64 `json:"rebate_type"` // 返佣类型:1=直接上级,2=钻石上级,3=黄金上级
|
||||||
Amount float64 `json:"amount"` // 返佣金额
|
Amount float64 `json:"amount"` // 返佣金额
|
||||||
|
Status int64 `json:"status"` // 状态:1=已发放,2=已冻结,3=已取消(已退款)
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
// 升级返佣记录
|
// 升级返佣记录
|
||||||
@@ -565,28 +612,46 @@ type (
|
|||||||
Total int64 `json:"total"` // 总数
|
Total int64 `json:"total"` // 总数
|
||||||
List []WithdrawalItem `json:"list"` // 列表
|
List []WithdrawalItem `json:"list"` // 列表
|
||||||
}
|
}
|
||||||
|
// 提现记录
|
||||||
WithdrawalItem {
|
WithdrawalItem {
|
||||||
Id string `json:"id"` // 记录ID
|
Id string `json:"id"` // 记录ID
|
||||||
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
||||||
Amount float64 `json:"amount"` // 提现金额
|
Amount float64 `json:"amount"` // 提现金额
|
||||||
TaxAmount float64 `json:"tax_amount"` // 税费金额
|
TaxAmount float64 `json:"tax_amount"` // 税费金额
|
||||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
|
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
|
||||||
Status int64 `json:"status"` // 状态:1=待审核,2=审核通过,3=审核拒绝,4=提现中,5=提现成功,6=提现失败
|
Status int64 `json:"status"` // 状态:1=待审核,2=审核通过,3=审核拒绝,4=提现中,5=提现成功,6=提现失败
|
||||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式:1=支付宝,2=银行卡
|
||||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
|
||||||
Remark string `json:"remark"` // 备注
|
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时填写)
|
||||||
|
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时填写)
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
// 申请提现
|
// 申请提现
|
||||||
ApplyWithdrawalReq {
|
ApplyWithdrawalReq {
|
||||||
Amount float64 `json:"amount"` // 提现金额
|
Amount float64 `json:"amount"` // 提现金额
|
||||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式:1=支付宝,2=银行卡
|
||||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
|
||||||
|
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||||
|
BankCardNo string `json:"bank_card_no,optional"` // 银行卡号(银行卡提现必填)
|
||||||
|
BankName string `json:"bank_name,optional"` // 开户行名称(银行卡提现必填)
|
||||||
}
|
}
|
||||||
ApplyWithdrawalResp {
|
ApplyWithdrawalResp {
|
||||||
WithdrawalId string `json:"withdrawal_id"` // 提现记录ID
|
WithdrawalId string `json:"withdrawal_id"` // 提现记录ID
|
||||||
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
||||||
}
|
}
|
||||||
|
// 获取上次提现信息(用于前端预填)
|
||||||
|
GetLastWithdrawalInfoReq {
|
||||||
|
WithdrawalType int64 `form:"withdrawal_type"` // 提现方式:1=支付宝,2=银行卡
|
||||||
|
}
|
||||||
|
GetLastWithdrawalInfoResp {
|
||||||
|
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式
|
||||||
|
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
|
||||||
|
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||||
|
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||||
|
BankName string `json:"bank_name"` // 开户行名称
|
||||||
|
}
|
||||||
// 实名认证
|
// 实名认证
|
||||||
RealNameAuthReq {
|
RealNameAuthReq {
|
||||||
Name string `json:"name"` // 姓名
|
Name string `json:"name"` // 姓名
|
||||||
@@ -607,13 +672,13 @@ type (
|
|||||||
List []PromotionQueryItem `json:"list"` // 列表
|
List []PromotionQueryItem `json:"list"` // 列表
|
||||||
}
|
}
|
||||||
PromotionQueryItem {
|
PromotionQueryItem {
|
||||||
Id string `json:"id"` // 查询ID
|
Id string `json:"id"` // 查询ID
|
||||||
OrderId string `json:"order_id"` // 订单ID
|
OrderId string `json:"order_id"` // 订单ID
|
||||||
ProductName string `json:"product_name"` // 产品名称
|
ProductName string `json:"product_name"` // 产品名称
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
QueryState string `json:"query_state"` // 查询状态
|
QueryState string `json:"query_state"` // 查询状态
|
||||||
Params map[string]interface{} `json:"params,optional"` // 查询参数(已脱敏)
|
Params map[string]interface{} `json:"params,optional"` // 查询参数(已脱敏)
|
||||||
Price float64 `json:"price"` // 查询价格
|
Price float64 `json:"price"` // 查询价格
|
||||||
}
|
}
|
||||||
// ============================================
|
// ============================================
|
||||||
// 用户模块白名单相关类型
|
// 用户模块白名单相关类型
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ type (
|
|||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
|
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
|
||||||
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
|
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
|
||||||
|
Code string `json:"code,optional"` // 微信小程序/H5授权码,用于自动绑定微信账号(当用户未绑定微信时)
|
||||||
}
|
}
|
||||||
PaymentResp {
|
PaymentResp {
|
||||||
PrepayData interface{} `json:"prepay_data"`
|
PrepayData interface{} `json:"prepay_data"`
|
||||||
|
|||||||
@@ -156,8 +156,26 @@ service main {
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
sendSmsReq {
|
sendSmsReq {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
||||||
|
CaptchaVerifyParam string `json:"captchaVerifyParam,optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//============================> captcha v1 <============================
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: captcha
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "get encrypted scene id for aliyun captcha"
|
||||||
|
@handler getEncryptedSceneId
|
||||||
|
post /captcha/encryptedSceneId returns (GetEncryptedSceneIdResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
GetEncryptedSceneIdResp {
|
||||||
|
EncryptedSceneId string `json:"encryptedSceneId"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ VerifyCode:
|
|||||||
SignName: "天远查"
|
SignName: "天远查"
|
||||||
TemplateCode: "SMS_302641455"
|
TemplateCode: "SMS_302641455"
|
||||||
ValidTime: 300
|
ValidTime: 300
|
||||||
|
Captcha:
|
||||||
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
|
EndpointURL: "captcha.cn-shanghai.aliyuncs.com"
|
||||||
|
SceneID: "wynt39to"
|
||||||
|
EKey: ""
|
||||||
Encrypt:
|
Encrypt:
|
||||||
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
||||||
WestConfig:
|
WestConfig:
|
||||||
@@ -67,10 +73,8 @@ WechatH5:
|
|||||||
AppID: "wx442ee1ac1ee75917"
|
AppID: "wx442ee1ac1ee75917"
|
||||||
AppSecret: "c80474909db42f63913b7a307b3bee17"
|
AppSecret: "c80474909db42f63913b7a307b3bee17"
|
||||||
WechatMini:
|
WechatMini:
|
||||||
AppID: "wx781abb66b3368963" # 小程序的AppID
|
AppID: "wx5bacc94add2da981" # 小程序的AppID
|
||||||
AppSecret: "c7d02cdb0fc23c35c93187af9243b00d" # 小程序的AppSecret
|
AppSecret: "48a2c1e8ff1b7d4c0ff82fbefa64d2d0" # 小程序的AppSecret
|
||||||
TycAppID: "wxe74617f3dd56c196"
|
|
||||||
TycAppSecret: "c8207e54aef5689b2a7c1f91ed7ae8a0"
|
|
||||||
Query:
|
Query:
|
||||||
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
||||||
AdminConfig:
|
AdminConfig:
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ VerifyCode:
|
|||||||
SignName: "海南海宇大数据"
|
SignName: "海南海宇大数据"
|
||||||
TemplateCode: "SMS_302641455"
|
TemplateCode: "SMS_302641455"
|
||||||
ValidTime: 300
|
ValidTime: 300
|
||||||
|
Captcha:
|
||||||
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
|
EndpointURL: "captcha.cn-shanghai.aliyuncs.com"
|
||||||
|
SceneID: "wynt39to"
|
||||||
|
EKey: ""
|
||||||
Encrypt:
|
Encrypt:
|
||||||
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
||||||
Alipay:
|
Alipay:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type Config struct {
|
|||||||
CacheRedis cache.CacheConf
|
CacheRedis cache.CacheConf
|
||||||
JwtAuth JwtAuth // JWT 鉴权相关配置
|
JwtAuth JwtAuth // JWT 鉴权相关配置
|
||||||
VerifyCode VerifyCode
|
VerifyCode VerifyCode
|
||||||
|
Captcha CaptchaConfig // 阿里云验证码配置
|
||||||
Encrypt Encrypt
|
Encrypt Encrypt
|
||||||
Alipay AlipayConfig
|
Alipay AlipayConfig
|
||||||
Wxpay WxpayConfig
|
Wxpay WxpayConfig
|
||||||
@@ -40,6 +41,15 @@ type VerifyCode struct {
|
|||||||
TemplateCode string
|
TemplateCode string
|
||||||
ValidTime int
|
ValidTime int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CaptchaConfig struct {
|
||||||
|
AccessKeyID string
|
||||||
|
AccessKeySecret string
|
||||||
|
EndpointURL string
|
||||||
|
SceneID string
|
||||||
|
EKey string // 加密模式用的 ekey(Base64)
|
||||||
|
}
|
||||||
|
|
||||||
type Encrypt struct {
|
type Encrypt struct {
|
||||||
SecretKey string
|
SecretKey string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Code scaffolded by goctl. Safe to edit.
|
||||||
|
// goctl 1.9.2
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/logic/agent"
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/common/result"
|
||||||
|
"qnc-server/pkg/lzkit/validator"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateInvitePosterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.GenerateInvitePosterReq
|
||||||
|
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.NewGenerateInvitePosterLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GenerateInvitePoster(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/logic/agent"
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/common/result"
|
||||||
|
"qnc-server/pkg/lzkit/validator"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetLastWithdrawalInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.GetLastWithdrawalInfoReq
|
||||||
|
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.NewGetLastWithdrawalInfoLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GetLastWithdrawalInfo(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,8 +23,27 @@ func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
result.ParamValidateErrorResult(r, w, err)
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 获取客户端真实 IP
|
||||||
|
clientIP := getClientIP(r)
|
||||||
|
// 获取 User-Agent 用于判断微信环境
|
||||||
|
userAgent := r.Header.Get("User-Agent")
|
||||||
l := auth.NewSendSmsLogic(r.Context(), svcCtx)
|
l := auth.NewSendSmsLogic(r.Context(), svcCtx)
|
||||||
err := l.SendSms(&req)
|
err := l.SendSms(&req, clientIP, userAgent)
|
||||||
result.HttpResult(r, w, nil, err)
|
result.HttpResult(r, w, nil, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getClientIP 获取客户端真实 IP
|
||||||
|
func getClientIP(r *http.Request) string {
|
||||||
|
// 优先从 X-Forwarded-For 获取
|
||||||
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||||
|
// X-Forwarded-For 可能包含多个 IP,取第一个
|
||||||
|
return xff
|
||||||
|
}
|
||||||
|
// 其次从 X-Real-IP 获取
|
||||||
|
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
||||||
|
return xri
|
||||||
|
}
|
||||||
|
// 最后使用 RemoteAddr
|
||||||
|
return r.RemoteAddr
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
"qnc-server/app/main/api/internal/logic/captcha"
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEncryptedSceneIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := captcha.NewGetEncryptedSceneIdLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GetEncryptedSceneId()
|
||||||
|
if err != nil {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, err)
|
||||||
|
} else {
|
||||||
|
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
app "qnc-server/app/main/api/internal/handler/app"
|
app "qnc-server/app/main/api/internal/handler/app"
|
||||||
auth "qnc-server/app/main/api/internal/handler/auth"
|
auth "qnc-server/app/main/api/internal/handler/auth"
|
||||||
authorization "qnc-server/app/main/api/internal/handler/authorization"
|
authorization "qnc-server/app/main/api/internal/handler/authorization"
|
||||||
|
captcha "qnc-server/app/main/api/internal/handler/captcha"
|
||||||
notification "qnc-server/app/main/api/internal/handler/notification"
|
notification "qnc-server/app/main/api/internal/handler/notification"
|
||||||
pay "qnc-server/app/main/api/internal/handler/pay"
|
pay "qnc-server/app/main/api/internal/handler/pay"
|
||||||
product "qnc-server/app/main/api/internal/handler/product"
|
product "qnc-server/app/main/api/internal/handler/product"
|
||||||
@@ -104,7 +105,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Method: http.MethodPost,
|
Method: http.MethodPost,
|
||||||
Path: "/upgrade",
|
Path: "/upgrade/agent",
|
||||||
Handler: admin_agent.AdminUpgradeAgentHandler(serverCtx),
|
Handler: admin_agent.AdminUpgradeAgentHandler(serverCtx),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -661,6 +662,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
Path: "/info",
|
Path: "/info",
|
||||||
Handler: agent.GetAgentInfoHandler(serverCtx),
|
Handler: agent.GetAgentInfoHandler(serverCtx),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/invite/poster",
|
||||||
|
Handler: agent.GenerateInvitePosterHandler(serverCtx),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Method: http.MethodPost,
|
Method: http.MethodPost,
|
||||||
Path: "/invite_code/delete",
|
Path: "/invite_code/delete",
|
||||||
@@ -786,6 +792,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
Path: "/withdrawal/apply",
|
Path: "/withdrawal/apply",
|
||||||
Handler: agent.ApplyWithdrawalHandler(serverCtx),
|
Handler: agent.ApplyWithdrawalHandler(serverCtx),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/withdrawal/last_info",
|
||||||
|
Handler: agent.GetLastWithdrawalInfoHandler(serverCtx),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Path: "/withdrawal/list",
|
Path: "/withdrawal/list",
|
||||||
@@ -862,6 +873,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
rest.WithPrefix("/api/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// get encrypted scene id for aliyun captcha
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/captcha/encryptedSceneId",
|
||||||
|
Handler: captcha.GetEncryptedSceneIdHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"os"
|
||||||
"qnc-server/common/globalkey"
|
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
"qnc-server/pkg/lzkit/lzUtils"
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
@@ -32,6 +32,13 @@ func NewAdminAuditWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseFloat 解析配置中的浮点数
|
||||||
|
func (l *AdminAuditWithdrawalLogic) parseFloat(s string) (float64, error) {
|
||||||
|
var result float64
|
||||||
|
_, err := fmt.Sscanf(s, "%f", &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWithdrawalReq) (resp *types.AdminAuditWithdrawalResp, err error) {
|
func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWithdrawalReq) (resp *types.AdminAuditWithdrawalResp, err error) {
|
||||||
// 1. 查询提现记录
|
// 1. 查询提现记录
|
||||||
withdrawal, err := l.svcCtx.AgentWithdrawalModel.FindOne(l.ctx, req.WithdrawalId)
|
withdrawal, err := l.svcCtx.AgentWithdrawalModel.FindOne(l.ctx, req.WithdrawalId)
|
||||||
@@ -46,39 +53,126 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
|
|||||||
|
|
||||||
// 4. 使用事务处理审核
|
// 4. 使用事务处理审核
|
||||||
err = l.svcCtx.AgentWithdrawalModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
err = l.svcCtx.AgentWithdrawalModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||||
if req.Status == 2 { // 审核通过
|
switch req.Status {
|
||||||
// 4.1 更新提现记录状态为提现中
|
case 2: // 审核通过
|
||||||
withdrawal.Status = 4 // 提现中
|
// 4.1 根据提现方式处理
|
||||||
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
|
switch withdrawal.WithdrawalType {
|
||||||
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
case 1:
|
||||||
return errors.Wrapf(err, "更新提现记录失败")
|
// 支付宝提现:审核通过前再次校验月度额度,避免一次性通过多笔超限
|
||||||
}
|
now := time.Now()
|
||||||
|
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||||
|
nextMonthStart := monthStart.AddDate(0, 1, 0)
|
||||||
|
|
||||||
// 4.2 调用支付宝转账接口
|
// 获取支付宝月度额度配置(默认 800 元)
|
||||||
outBizNo := withdrawal.WithdrawNo
|
alipayQuota := 800.0
|
||||||
transferResp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
|
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(transCtx, "alipay_month_quota"); cfgErr == nil {
|
||||||
if err != nil {
|
if parsed, parseErr := l.parseFloat(cfg.ConfigValue); parseErr == nil && parsed > 0 {
|
||||||
// 转账失败,更新状态为失败
|
alipayQuota = parsed
|
||||||
withdrawal.Status = 6 // 提现失败
|
}
|
||||||
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("转账失败: %v", err), Valid: true}
|
|
||||||
l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
|
|
||||||
|
|
||||||
// 解冻余额
|
|
||||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
|
|
||||||
if err == nil {
|
|
||||||
wallet.FrozenBalance -= withdrawal.Amount
|
|
||||||
wallet.Balance += withdrawal.Amount
|
|
||||||
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.Wrapf(err, "支付宝转账失败")
|
// 统计本月已成功的支付宝提现金额(status=5)
|
||||||
}
|
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
|
||||||
|
Where("agent_id = ? AND withdrawal_type = ? AND status = ? AND create_time >= ? AND create_time < ?",
|
||||||
|
withdrawal.AgentId, 1, 5, monthStart, nextMonthStart)
|
||||||
|
usedAmount, sumErr := l.svcCtx.AgentWithdrawalModel.FindSum(transCtx, withdrawBuilder, "amount")
|
||||||
|
if sumErr != nil {
|
||||||
|
return errors.Wrapf(sumErr, "查询本月支付宝提现额度使用情况失败")
|
||||||
|
}
|
||||||
|
|
||||||
// 4.3 根据转账结果更新状态
|
if usedAmount+withdrawal.Amount > alipayQuota {
|
||||||
switch transferResp.Status {
|
// 超出额度,不允许通过,保持待审核状态并提示原因
|
||||||
case "SUCCESS":
|
withdrawal.Status = 1
|
||||||
// 转账成功
|
withdrawal.Remark = sql.NullString{
|
||||||
|
String: fmt.Sprintf("超过本月支付宝提现额度(限额:%.2f 元,已用:%.2f 元),请使用银行卡提现或调整金额", alipayQuota, usedAmount),
|
||||||
|
Valid: true,
|
||||||
|
}
|
||||||
|
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
||||||
|
return errors.Wrapf(err, "更新提现记录失败")
|
||||||
|
}
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("超过本月支付宝提现额度,无法通过该笔提现"), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付宝提现:开发环境下做模拟,不调用真实支付宝转账
|
||||||
|
mockTransferStatus := "SUCCESS"
|
||||||
|
var transferResp struct {
|
||||||
|
Status string
|
||||||
|
SubCode string
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("ENV") == "development" {
|
||||||
|
transferResp.Status = mockTransferStatus
|
||||||
|
logx.Infof("【DEV】模拟支付宝转账成功,withdrawNo=%s, amount=%.2f, payee=%s",
|
||||||
|
withdrawal.WithdrawNo, withdrawal.ActualAmount, withdrawal.PayeeAccount)
|
||||||
|
} else {
|
||||||
|
// 生产环境:同步调用支付宝转账接口
|
||||||
|
outBizNo := withdrawal.WithdrawNo
|
||||||
|
resp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
|
||||||
|
if err != nil {
|
||||||
|
// 调用失败:保持状态为待审核(1),只记录备注,方便管理员重试
|
||||||
|
withdrawal.Status = 1 // 待审核
|
||||||
|
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("支付宝转账调用失败: %v", err), Valid: true}
|
||||||
|
_ = l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "支付宝转账失败")
|
||||||
|
}
|
||||||
|
transferResp.Status = resp.Status
|
||||||
|
transferResp.SubCode = resp.SubCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4.2 根据转账结果更新状态
|
||||||
|
switch transferResp.Status {
|
||||||
|
case "SUCCESS":
|
||||||
|
// 转账成功
|
||||||
|
withdrawal.Status = 5 // 提现成功
|
||||||
|
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
||||||
|
return errors.Wrapf(err, "更新提现记录失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新钱包(解冻并扣除)
|
||||||
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "查询钱包失败")
|
||||||
|
}
|
||||||
|
wallet.FrozenBalance -= withdrawal.Amount
|
||||||
|
wallet.WithdrawnAmount += withdrawal.Amount
|
||||||
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
|
||||||
|
return errors.Wrapf(err, "更新钱包失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新扣税记录状态
|
||||||
|
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
||||||
|
Where("withdrawal_id = ?", withdrawal.Id)
|
||||||
|
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
|
||||||
|
if err == nil && len(taxRecords) > 0 {
|
||||||
|
taxRecord := taxRecords[0]
|
||||||
|
taxRecord.TaxStatus = 2 // 已扣税
|
||||||
|
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
|
||||||
|
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "FAIL":
|
||||||
|
// 转账失败:保持待审核状态,方便人工处理或重试
|
||||||
|
withdrawal.Status = 1 // 待审核
|
||||||
|
errorMsg := l.mapAlipayError(transferResp.SubCode)
|
||||||
|
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
|
||||||
|
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
||||||
|
return errors.Wrapf(err, "更新提现记录失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
case "DEALING":
|
||||||
|
// 处理中:同样保持待审核状态(1),由管理员后续确认
|
||||||
|
withdrawal.Status = 1
|
||||||
|
withdrawal.Remark = sql.NullString{String: "支付宝处理中,请稍后重试或联系平台", Valid: true}
|
||||||
|
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
||||||
|
return errors.Wrapf(err, "更新提现记录失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// 银行卡提现:审核通过即视为提现成功(线下已/将立即打款)
|
||||||
withdrawal.Status = 5 // 提现成功
|
withdrawal.Status = 5 // 提现成功
|
||||||
|
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
|
||||||
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
||||||
return errors.Wrapf(err, "更新提现记录失败")
|
return errors.Wrapf(err, "更新提现记录失败")
|
||||||
}
|
}
|
||||||
@@ -96,7 +190,7 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
|
|||||||
|
|
||||||
// 更新扣税记录状态
|
// 更新扣税记录状态
|
||||||
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
||||||
Where("withdrawal_id = ? AND del_state = ?", withdrawal.Id, globalkey.DelStateNo)
|
Where("withdrawal_id = ?", withdrawal.Id)
|
||||||
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
|
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
|
||||||
if err == nil && len(taxRecords) > 0 {
|
if err == nil && len(taxRecords) > 0 {
|
||||||
taxRecord := taxRecords[0]
|
taxRecord := taxRecords[0]
|
||||||
@@ -104,30 +198,9 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
|
|||||||
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
|
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
|
||||||
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
|
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "FAIL":
|
|
||||||
// 转账失败
|
|
||||||
withdrawal.Status = 6 // 提现失败
|
|
||||||
errorMsg := l.mapAlipayError(transferResp.SubCode)
|
|
||||||
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
|
|
||||||
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
|
|
||||||
return errors.Wrapf(err, "更新提现记录失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解冻余额
|
|
||||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
|
|
||||||
if err == nil {
|
|
||||||
wallet.FrozenBalance -= withdrawal.Amount
|
|
||||||
wallet.Balance += withdrawal.Amount
|
|
||||||
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "DEALING":
|
|
||||||
// 处理中,保持提现中状态,后续通过轮询更新
|
|
||||||
// 状态已经是4(提现中),无需更新
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if req.Status == 3 { // 审核拒绝
|
case 3: // 审核拒绝
|
||||||
// 4.1 更新提现记录状态为拒绝
|
// 4.1 更新提现记录状态为拒绝
|
||||||
withdrawal.Status = 3 // 审核拒绝
|
withdrawal.Status = 3 // 审核拒绝
|
||||||
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
|
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
|
||||||
|
|||||||
@@ -49,6 +49,17 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
|
|||||||
item.Remark = v.Remark.String
|
item.Remark = v.Remark.String
|
||||||
}
|
}
|
||||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
|
// 如果是银行卡提现,填充银行卡信息
|
||||||
|
if v.WithdrawalType == 2 {
|
||||||
|
if v.BankCardNo.Valid {
|
||||||
|
item.BankCardNo = v.BankCardNo.String
|
||||||
|
}
|
||||||
|
if v.BankName.Valid {
|
||||||
|
item.BankName = v.BankName.String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
}
|
}
|
||||||
resp = &types.AdminGetAgentWithdrawalListResp{
|
resp = &types.AdminGetAgentWithdrawalListResp{
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
|
|||||||
return nil
|
return nil
|
||||||
}, func() error {
|
}, func() error {
|
||||||
var err error
|
var err error
|
||||||
orders, err = l.svcCtx.OrderModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
orders, err = l.svcCtx.OrderModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "update_time DESC")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询订单列表失败 err: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetOrderList, 查询订单列表失败 err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -264,6 +264,7 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
|
|||||||
Status: order.Status,
|
Status: order.Status,
|
||||||
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
QueryState: queryStateMap[order.Id],
|
QueryState: queryStateMap[order.Id],
|
||||||
|
UpdateTime: order.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||||
}
|
}
|
||||||
if order.PayTime.Valid {
|
if order.PayTime.Valid {
|
||||||
item.PayTime = order.PayTime.Time.Format("2006-01-02 15:04:05")
|
item.PayTime = order.PayTime.Time.Format("2006-01-02 15:04:05")
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/globalkey"
|
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
"qnc-server/pkg/lzkit/lzUtils"
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -62,53 +61,138 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
|
|||||||
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
|
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 验证提现金额
|
// 3. 验证提现方式
|
||||||
|
if req.WithdrawalType != 1 && req.WithdrawalType != 2 {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("提现方式无效"), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验证提现信息
|
||||||
|
if req.WithdrawalType == 1 {
|
||||||
|
// 支付宝提现:验证支付宝账号和姓名
|
||||||
|
if req.PayeeAccount == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("请输入支付宝账号"), "")
|
||||||
|
}
|
||||||
|
if req.PayeeName == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("请输入收款人姓名"), "")
|
||||||
|
}
|
||||||
|
} else if req.WithdrawalType == 2 {
|
||||||
|
// 银行卡提现:验证银行卡号、开户行和姓名
|
||||||
|
if req.BankCardNo == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("请输入银行卡号"), "")
|
||||||
|
}
|
||||||
|
if req.BankName == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("请输入开户行名称"), "")
|
||||||
|
}
|
||||||
|
if req.PayeeName == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("请输入收款人姓名"), "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 验证提现金额
|
||||||
if req.Amount <= 0 {
|
if req.Amount <= 0 {
|
||||||
return nil, errors.Wrapf(xerr.NewErrMsg("提现金额必须大于0"), "")
|
return nil, errors.Wrapf(xerr.NewErrMsg("提现金额必须大于0"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取钱包信息
|
// 6. 获取钱包信息
|
||||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败, %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 验证余额
|
// 7. 验证余额(包括检查是否为负数)
|
||||||
|
if wallet.Balance < 0 {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("账户存在欠款,请先补足欠款后再申请提现,当前余额:%.2f", wallet.Balance)), "")
|
||||||
|
}
|
||||||
if wallet.Balance < req.Amount {
|
if wallet.Balance < req.Amount {
|
||||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("余额不足,当前余额:%.2f", wallet.Balance)), "")
|
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("余额不足,当前余额:%.2f", wallet.Balance)), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 计算税费
|
// 8. 支付宝月度提现额度校验(仅针对支付宝提现)
|
||||||
|
if req.WithdrawalType == 1 {
|
||||||
|
now := time.Now()
|
||||||
|
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||||
|
nextMonthStart := monthStart.AddDate(0, 1, 0)
|
||||||
|
|
||||||
|
// 8.1 获取支付宝月度额度配置(默认 800 元)
|
||||||
|
alipayQuota := 800.0
|
||||||
|
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, "alipay_month_quota"); cfgErr == nil {
|
||||||
|
if parsed, parseErr := l.parseFloat(cfg.ConfigValue); parseErr == nil && parsed > 0 {
|
||||||
|
alipayQuota = parsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.2 统计本月已申请/成功的支付宝提现金额(status IN (1,5)),避免多次申请占用超额
|
||||||
|
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
|
||||||
|
Where("agent_id = ? AND withdrawal_type = ? AND status IN (1,5) AND create_time >= ? AND create_time < ?",
|
||||||
|
agent.Id, 1, monthStart, nextMonthStart)
|
||||||
|
usedAmount, sumErr := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, withdrawBuilder, "amount")
|
||||||
|
if sumErr != nil {
|
||||||
|
return nil, errors.Wrapf(sumErr, "查询本月支付宝提现额度使用情况失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
remainQuota := alipayQuota - usedAmount
|
||||||
|
if remainQuota <= 0 {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
xerr.NewErrMsg(fmt.Sprintf("本月支付宝提现额度已用完(额度:%.2f 元),请使用银行卡提现", alipayQuota)),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Amount > remainQuota {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
xerr.NewErrMsg(fmt.Sprintf("本月支付宝最高可提现 %.2f 元,请调整提现金额或使用银行卡提现", remainQuota)),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 计算税费
|
||||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||||
taxInfo, err := l.calculateTax(l.ctx, agent.Id, req.Amount, yearMonth)
|
taxInfo, err := l.calculateTax(l.ctx, agent.Id, req.Amount, yearMonth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "计算税费失败")
|
return nil, errors.Wrapf(err, "计算税费失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 生成提现单号
|
// 10. 生成提现单号(WD开头 + GenerateOutTradeNo生成的订单号,确保总长度不超过32个字符)
|
||||||
withdrawNo := fmt.Sprintf("WD%d%d", time.Now().Unix(), agent.Id)
|
orderNo := l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||||
|
withdrawNo := "WD" + orderNo
|
||||||
|
// 确保总长度不超过32个字符
|
||||||
|
if len(withdrawNo) > 32 {
|
||||||
|
withdrawNo = withdrawNo[:32]
|
||||||
|
}
|
||||||
|
|
||||||
// 8. 使用事务处理提现申请
|
// 11. 使用事务处理提现申请
|
||||||
var withdrawalId string
|
var withdrawalId string
|
||||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||||
// 8.1 冻结余额
|
// 11.1 冻结余额
|
||||||
wallet.FrozenBalance += req.Amount
|
wallet.FrozenBalance += req.Amount
|
||||||
wallet.Balance -= req.Amount
|
wallet.Balance -= req.Amount
|
||||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
|
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
|
||||||
return errors.Wrapf(err, "冻结余额失败")
|
return errors.Wrapf(err, "冻结余额失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.2 创建提现记录
|
// 11.2 创建提现记录
|
||||||
withdrawal := &model.AgentWithdrawal{
|
withdrawal := &model.AgentWithdrawal{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
AgentId: agent.Id,
|
AgentId: agent.Id,
|
||||||
WithdrawNo: withdrawNo,
|
WithdrawNo: withdrawNo,
|
||||||
PayeeAccount: req.PayeeAccount,
|
WithdrawalType: req.WithdrawalType,
|
||||||
PayeeName: req.PayeeName,
|
PayeeAccount: req.PayeeAccount,
|
||||||
Amount: req.Amount,
|
PayeeName: req.PayeeName,
|
||||||
ActualAmount: taxInfo.ActualAmount,
|
Amount: req.Amount,
|
||||||
TaxAmount: taxInfo.TaxAmount,
|
ActualAmount: taxInfo.ActualAmount,
|
||||||
Status: 1, // 处理中(待审核)
|
TaxAmount: taxInfo.TaxAmount,
|
||||||
|
Status: 1, // 待审核
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是银行卡提现,设置银行卡相关字段
|
||||||
|
if req.WithdrawalType == 2 {
|
||||||
|
withdrawal.BankCardNo = lzUtils.StringToNullString(req.BankCardNo)
|
||||||
|
withdrawal.BankName = lzUtils.StringToNullString(req.BankName)
|
||||||
|
// 银行卡提现时,payee_account 可以存储银行卡号(便于查询),也可以留空
|
||||||
|
if req.PayeeAccount == "" {
|
||||||
|
withdrawal.PayeeAccount = req.BankCardNo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
|
_, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
|
||||||
@@ -117,7 +201,7 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
|
|||||||
}
|
}
|
||||||
withdrawalId = withdrawal.Id
|
withdrawalId = withdrawal.Id
|
||||||
|
|
||||||
// 8.3 创建扣税记录
|
// 11.3 创建扣税记录
|
||||||
taxRecord := &model.AgentWithdrawalTax{
|
taxRecord := &model.AgentWithdrawalTax{
|
||||||
AgentId: agent.Id,
|
AgentId: agent.Id,
|
||||||
WithdrawalId: withdrawalId,
|
WithdrawalId: withdrawalId,
|
||||||
@@ -167,8 +251,10 @@ func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询本月已提现金额
|
// 查询本月已提现金额
|
||||||
|
// 注意:FindAll 方法会自动添加 del_state = ? 条件,所以这里不需要手动添加
|
||||||
|
// 这里对 year_month 使用反引号包裹,避免与某些数据库版本/SQL 模式下的关键字冲突
|
||||||
builder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
builder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
||||||
Where("agent_id = ? AND year_month = ? AND del_state = ?", agentId, yearMonth, globalkey.DelStateNo)
|
Where("agent_id = ? AND `year_month` = ?", agentId, yearMonth)
|
||||||
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(ctx, builder, "")
|
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(ctx, builder, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "查询月度提现记录失败")
|
return nil, errors.Wrapf(err, "查询月度提现记录失败")
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckFeatureWhitelistStatusLogic struct {
|
type CheckFeatureWhitelistStatusLogic struct {
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckOrderAgentLogic struct {
|
type CheckOrderAgentLogic struct {
|
||||||
|
|||||||
@@ -4,15 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateWhitelistOrderLogic struct {
|
type CreateWhitelistOrderLogic struct {
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Code scaffolded by goctl. Safe to edit.
|
||||||
|
// goctl 1.9.2
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenerateInvitePosterLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenerateInvitePosterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateInvitePosterLogic {
|
||||||
|
return &GenerateInvitePosterLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GenerateInvitePosterLogic) GenerateInvitePoster(req *types.GenerateInvitePosterReq) (resp *types.GenerateInvitePosterResp, err error) {
|
||||||
|
if req.InviteLink == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("邀请链接不能为空"), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用ImageService生成海报
|
||||||
|
imageData, mimeType, err := l.svcCtx.ImageService.ProcessImageWithQRCode("invitation", req.InviteLink)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("生成邀请海报失败: %v", err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成海报失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将图片数据转换为base64编码的data URL
|
||||||
|
base64Data := base64.StdEncoding.EncodeToString(imageData)
|
||||||
|
posterUrl := fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data)
|
||||||
|
|
||||||
|
return &types.GenerateInvitePosterResp{
|
||||||
|
PosterUrl: posterUrl,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -4,15 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/globalkey"
|
"qnc-server/common/globalkey"
|
||||||
"qnc-server/common/tool"
|
"qnc-server/common/tool"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
"qnc-server/pkg/lzkit/crypto"
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@@ -124,7 +124,18 @@ func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId string, inviteCo
|
|||||||
// 先查询是否已存在短链
|
// 先查询是否已存在短链
|
||||||
existingShortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""}, 2, globalkey.DelStateNo)
|
existingShortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""}, 2, globalkey.DelStateNo)
|
||||||
if err == nil && existingShortLink != nil {
|
if err == nil && existingShortLink != nil {
|
||||||
// 已存在短链,直接返回
|
// 已存在短链,检查 target_path 是否需要更新
|
||||||
|
if existingShortLink.TargetPath != targetPath {
|
||||||
|
// target_path 已变化,更新短链记录
|
||||||
|
oldTargetPath := existingShortLink.TargetPath
|
||||||
|
existingShortLink.TargetPath = targetPath
|
||||||
|
if updateErr := l.svcCtx.AgentShortLinkModel.UpdateWithVersion(l.ctx, nil, existingShortLink); updateErr != nil {
|
||||||
|
l.Errorf("更新短链 target_path 失败: %v", updateErr)
|
||||||
|
// 即使更新失败,也返回旧短链,避免影响用户体验
|
||||||
|
} else {
|
||||||
|
l.Infof("短链 target_path 已更新: shortCode=%s, old=%s, new=%s", existingShortLink.ShortCode, oldTargetPath, targetPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingShortLink.ShortCode), nil
|
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingShortLink.ShortCode), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +147,18 @@ func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId string, inviteCo
|
|||||||
if inviteCodeId == "" && inviteCode != "" {
|
if inviteCodeId == "" && inviteCode != "" {
|
||||||
existingByCode, err2 := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeTypeDelState(l.ctx, sql.NullString{String: inviteCode, Valid: true}, 2, globalkey.DelStateNo)
|
existingByCode, err2 := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeTypeDelState(l.ctx, sql.NullString{String: inviteCode, Valid: true}, 2, globalkey.DelStateNo)
|
||||||
if err2 == nil && existingByCode != nil {
|
if err2 == nil && existingByCode != nil {
|
||||||
|
// 已存在短链,检查 target_path 是否需要更新
|
||||||
|
if existingByCode.TargetPath != targetPath {
|
||||||
|
// target_path 已变化,更新短链记录
|
||||||
|
oldTargetPath := existingByCode.TargetPath
|
||||||
|
existingByCode.TargetPath = targetPath
|
||||||
|
if updateErr := l.svcCtx.AgentShortLinkModel.UpdateWithVersion(l.ctx, nil, existingByCode); updateErr != nil {
|
||||||
|
l.Errorf("更新短链 target_path 失败: %v", updateErr)
|
||||||
|
// 即使更新失败,也返回旧短链,避免影响用户体验
|
||||||
|
} else {
|
||||||
|
l.Infof("短链 target_path 已更新: shortCode=%s, old=%s, new=%s", existingByCode.ShortCode, oldTargetPath, targetPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingByCode.ShortCode), nil
|
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingByCode.ShortCode), nil
|
||||||
}
|
}
|
||||||
if err2 != nil && !errors.Is(err2, model.ErrNotFound) {
|
if err2 != nil && !errors.Is(err2, model.ErrNotFound) {
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"qnc-server/app/main/model"
|
||||||
|
"qnc-server/common/ctxdata"
|
||||||
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetLastWithdrawalInfoLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetLastWithdrawalInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLastWithdrawalInfoLogic {
|
||||||
|
return &GetLastWithdrawalInfoLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetLastWithdrawalInfoLogic) GetLastWithdrawalInfo(req *types.GetLastWithdrawalInfoReq) (resp *types.GetLastWithdrawalInfoResp, err error) {
|
||||||
|
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 获取代理信息
|
||||||
|
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证提现方式
|
||||||
|
if req.WithdrawalType != 1 && req.WithdrawalType != 2 {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("提现方式无效"), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 查询该代理最近一次该类型的提现记录
|
||||||
|
// 注意:FindAll 方法会自动添加 del_state = ? 条件,所以这里不需要手动添加
|
||||||
|
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
|
||||||
|
Where("agent_id = ? AND withdrawal_type = ?", agent.Id, req.WithdrawalType).
|
||||||
|
OrderBy("create_time DESC").
|
||||||
|
Limit(1)
|
||||||
|
|
||||||
|
withdrawals, err := l.svcCtx.AgentWithdrawalModel.FindAll(l.ctx, builder, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上次提现记录失败, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 如果没有找到记录,返回空信息
|
||||||
|
if len(withdrawals) == 0 {
|
||||||
|
return &types.GetLastWithdrawalInfoResp{
|
||||||
|
WithdrawalType: req.WithdrawalType,
|
||||||
|
PayeeAccount: "",
|
||||||
|
PayeeName: "",
|
||||||
|
BankCardNo: "",
|
||||||
|
BankName: "",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 组装响应
|
||||||
|
lastWithdrawal := withdrawals[0]
|
||||||
|
resp = &types.GetLastWithdrawalInfoResp{
|
||||||
|
WithdrawalType: lastWithdrawal.WithdrawalType,
|
||||||
|
PayeeAccount: lastWithdrawal.PayeeAccount,
|
||||||
|
PayeeName: lastWithdrawal.PayeeName,
|
||||||
|
BankCardNo: "",
|
||||||
|
BankName: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是银行卡提现,填充银行卡信息
|
||||||
|
if lastWithdrawal.WithdrawalType == 2 {
|
||||||
|
if lastWithdrawal.BankCardNo.Valid {
|
||||||
|
resp.BankCardNo = lastWithdrawal.BankCardNo.String
|
||||||
|
}
|
||||||
|
if lastWithdrawal.BankName.Valid {
|
||||||
|
resp.BankName = lastWithdrawal.BankName.String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
@@ -3,11 +3,12 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetWhitelistListLogic struct {
|
type GetWhitelistListLogic struct {
|
||||||
|
|||||||
@@ -81,18 +81,31 @@ func (l *GetWithdrawalListLogic) GetWithdrawalList(req *types.GetWithdrawalListR
|
|||||||
remark = withdrawal.Remark.String
|
remark = withdrawal.Remark.String
|
||||||
}
|
}
|
||||||
|
|
||||||
list = append(list, types.WithdrawalItem{
|
item := types.WithdrawalItem{
|
||||||
Id: withdrawal.Id,
|
Id: withdrawal.Id,
|
||||||
WithdrawalNo: withdrawal.WithdrawNo,
|
WithdrawalNo: withdrawal.WithdrawNo,
|
||||||
Amount: withdrawal.Amount,
|
WithdrawalType: withdrawal.WithdrawalType,
|
||||||
TaxAmount: withdrawal.TaxAmount,
|
Amount: withdrawal.Amount,
|
||||||
ActualAmount: withdrawal.ActualAmount,
|
TaxAmount: withdrawal.TaxAmount,
|
||||||
Status: withdrawal.Status,
|
ActualAmount: withdrawal.ActualAmount,
|
||||||
PayeeAccount: withdrawal.PayeeAccount,
|
Status: withdrawal.Status,
|
||||||
PayeeName: withdrawal.PayeeName,
|
PayeeAccount: withdrawal.PayeeAccount,
|
||||||
Remark: remark,
|
PayeeName: withdrawal.PayeeName,
|
||||||
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
|
Remark: remark,
|
||||||
})
|
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是银行卡提现,填充银行卡信息
|
||||||
|
if withdrawal.WithdrawalType == 2 {
|
||||||
|
if withdrawal.BankCardNo.Valid {
|
||||||
|
item.BankCardNo = withdrawal.BankCardNo.String
|
||||||
|
}
|
||||||
|
if withdrawal.BankName.Valid {
|
||||||
|
item.BankName = withdrawal.BankName.String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.GetWithdrawalListResp{
|
return &types.GetWithdrawalListResp{
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OfflineFeatureLogic struct {
|
type OfflineFeatureLogic struct {
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/globalkey"
|
"qnc-server/common/globalkey"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
"qnc-server/pkg/lzkit/crypto"
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"qnc-server/common/xerr"
|
|
||||||
"qnc-server/pkg/lzkit/crypto"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"qnc-server/common/xerr"
|
||||||
|
"qnc-server/pkg/captcha"
|
||||||
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -34,13 +35,58 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq, clientIP string, userAgent string) error {
|
||||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 加密手机号失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 加密手机号失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 滑块验证码校验(可选,支持微信环境跳过验证)
|
||||||
|
cfg := l.svcCtx.Config.Captcha
|
||||||
|
captchaResult := captcha.VerifyOptionalWithUserAgent(captcha.Config{
|
||||||
|
AccessKeyID: cfg.AccessKeyID,
|
||||||
|
AccessKeySecret: cfg.AccessKeySecret,
|
||||||
|
EndpointURL: cfg.EndpointURL,
|
||||||
|
SceneID: cfg.SceneID,
|
||||||
|
}, req.CaptchaVerifyParam, userAgent)
|
||||||
|
|
||||||
|
if captchaResult.VerifyErr != nil {
|
||||||
|
return captchaResult.VerifyErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 防刷策略
|
||||||
|
if captchaResult.Skipped {
|
||||||
|
// 没有滑块验证码,使用更严格的限流策略
|
||||||
|
// 2.1 IP 限流:同一 IP 每小时最多发送 10 次
|
||||||
|
ipLimitKey := fmt.Sprintf("ip_limit:%s", clientIP)
|
||||||
|
ipCount, err := l.svcCtx.Redis.Incr(ipLimitKey)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取IP限流缓存失败: %v", err)
|
||||||
|
}
|
||||||
|
if ipCount == 1 {
|
||||||
|
// 第一次访问,设置 1 小时过期
|
||||||
|
l.svcCtx.Redis.Expire(ipLimitKey, 3600)
|
||||||
|
}
|
||||||
|
if ipCount > 10 {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("请求过于频繁,请稍后再试"), "短信发送, IP限流: %s, count: %d", clientIP, ipCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2 手机号限流:同一手机号每小时最多发送 5 次(无滑块时更严格)
|
||||||
|
hourLimitKey := fmt.Sprintf("hour_limit:%s:%s", req.ActionType, encryptedMobile)
|
||||||
|
hourCount, err := l.svcCtx.Redis.Incr(hourLimitKey)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取小时限流缓存失败: %v", err)
|
||||||
|
}
|
||||||
|
if hourCount == 1 {
|
||||||
|
l.svcCtx.Redis.Expire(hourLimitKey, 3600)
|
||||||
|
}
|
||||||
|
if hourCount > 5 {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("该手机号请求过于频繁,请稍后再试"), "短信发送, 手机号小时限流: %s, count: %d", encryptedMobile, hourCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 检查手机号是否在一分钟内已发送过验证码
|
|
||||||
|
// 3. 检查手机号是否在一分钟内已发送过验证码(通用)
|
||||||
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile)
|
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile)
|
||||||
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
|
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,7 +94,6 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
|
|
||||||
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送验证码: %s", encryptedMobile)
|
return errors.Wrapf(xerr.NewErrMsg("一分钟内不能重复发送验证码"), "短信发送, 手机号1分钟内重复请求发送验证码: %s", encryptedMobile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"qnc-server/app/main/api/internal/svc"
|
||||||
|
"qnc-server/app/main/api/internal/types"
|
||||||
|
"qnc-server/pkg/captcha"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetEncryptedSceneIdLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetEncryptedSceneIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetEncryptedSceneIdLogic {
|
||||||
|
return &GetEncryptedSceneIdLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetEncryptedSceneIdLogic) GetEncryptedSceneId() (*types.GetEncryptedSceneIdResp, error) {
|
||||||
|
cfg := l.svcCtx.Config.Captcha
|
||||||
|
encrypted, err := captcha.GenerateEncryptedSceneID(cfg.SceneID, cfg.EKey, 3600)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("generate encrypted scene id error: %+v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &types.GetEncryptedSceneIdResp{
|
||||||
|
EncryptedSceneId: encrypted,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -6,15 +6,15 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/api/internal/svc"
|
"qnc-server/app/main/api/internal/svc"
|
||||||
"qnc-server/app/main/api/internal/types"
|
"qnc-server/app/main/api/internal/types"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
"qnc-server/pkg/lzkit/lzUtils"
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -35,6 +35,9 @@ type PaymentTypeResp struct {
|
|||||||
orderID string // 订单ID,用于开发环境测试支付模式
|
orderID string // 订单ID,用于开发环境测试支付模式
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enableDevTestPayment 测试支付功能开关,设置为 true 以启用测试支付功能
|
||||||
|
const enableDevTestPayment = false
|
||||||
|
|
||||||
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
||||||
return &PaymentLogic{
|
return &PaymentLogic{
|
||||||
Logger: logx.WithContext(ctx),
|
Logger: logx.WithContext(ctx),
|
||||||
@@ -50,8 +53,8 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
|||||||
|
|
||||||
// 检查是否为开发环境的测试支付模式
|
// 检查是否为开发环境的测试支付模式
|
||||||
env := os.Getenv("ENV")
|
env := os.Getenv("ENV")
|
||||||
isDevTestPayment := env == "development" && (req.PayMethod == "test" || req.PayMethod == "test_empty")
|
isDevTestPayment := enableDevTestPayment && env == "development" && (req.PayMethod == "test" || req.PayMethod == "test_empty")
|
||||||
isEmptyReportMode := env == "development" && req.PayMethod == "test_empty"
|
isEmptyReportMode := enableDevTestPayment && env == "development" && req.PayMethod == "test_empty"
|
||||||
|
|
||||||
l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||||
switch req.PayType {
|
switch req.PayType {
|
||||||
@@ -95,7 +98,7 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
|||||||
// 正常支付流程
|
// 正常支付流程
|
||||||
var createOrderErr error
|
var createOrderErr error
|
||||||
if req.PayMethod == "wechat" {
|
if req.PayMethod == "wechat" {
|
||||||
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo, req.Code)
|
||||||
} else if req.PayMethod == "alipay" {
|
} else if req.PayMethod == "alipay" {
|
||||||
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||||
} else if req.PayMethod == "appleiap" {
|
} else if req.PayMethod == "appleiap" {
|
||||||
|
|||||||
@@ -118,7 +118,37 @@ func (l *WxMiniAuthLogic) GetSessionKey(code string) (*SessionKeyResp, error) {
|
|||||||
|
|
||||||
// 检查微信返回的错误码
|
// 检查微信返回的错误码
|
||||||
if sessionKeyResp.ErrCode != 0 {
|
if sessionKeyResp.ErrCode != 0 {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
// 针对不同的微信错误码返回更明确的错误信息
|
||||||
|
var errMsg string
|
||||||
|
switch sessionKeyResp.ErrCode {
|
||||||
|
case 40029:
|
||||||
|
// code 无效(已使用、已过期或格式错误)
|
||||||
|
errMsg = "微信授权码无效或已过期,请重新打开小程序"
|
||||||
|
l.Errorf("微信code无效: errcode=%d, errmsg=%s, code前6位=%s",
|
||||||
|
sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg,
|
||||||
|
func() string {
|
||||||
|
if len(code) > 6 {
|
||||||
|
return code[:6] + "..."
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}())
|
||||||
|
case 40013:
|
||||||
|
// 无效的 AppID
|
||||||
|
errMsg = "小程序配置错误,请联系管理员"
|
||||||
|
l.Errorf("微信AppID无效: errcode=%d, errmsg=%s", sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||||
|
case 40125:
|
||||||
|
// 无效的 AppSecret
|
||||||
|
errMsg = "小程序配置错误,请联系管理员"
|
||||||
|
l.Errorf("微信AppSecret无效: errcode=%d, errmsg=%s", sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||||
|
case 45011:
|
||||||
|
// 频率限制
|
||||||
|
errMsg = "请求过于频繁,请稍后再试"
|
||||||
|
l.Errorf("微信API频率限制: errcode=%d, errmsg=%s", sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||||
|
default:
|
||||||
|
errMsg = fmt.Sprintf("微信授权失败: %s", sessionKeyResp.ErrMsg)
|
||||||
|
l.Errorf("微信接口返回错误: errcode=%d, errmsg=%s", sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg(errMsg),
|
||||||
"微信接口返回错误: errcode=%d, errmsg=%s",
|
"微信接口返回错误: errcode=%d, errmsg=%s",
|
||||||
sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/api/internal/config"
|
"qnc-server/app/main/api/internal/config"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/pkg/lzkit/lzUtils"
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/smartwalle/alipay/v3"
|
"github.com/smartwalle/alipay/v3"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,15 +2,19 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"qnc-server/app/main/api/internal/config"
|
"qnc-server/app/main/api/internal/config"
|
||||||
"qnc-server/app/main/model"
|
"qnc-server/app/main/model"
|
||||||
"qnc-server/common/ctxdata"
|
"qnc-server/common/ctxdata"
|
||||||
"qnc-server/pkg/lzkit/lzUtils"
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||||
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
||||||
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
||||||
@@ -251,9 +255,16 @@ func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
||||||
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string) (interface{}, error) {
|
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string, code string) (interface{}, error) {
|
||||||
// 根据 ctx 中的 platform 判断平台
|
// 根据 ctx 中的 platform 判断平台(请求头 X-Platform: wxmini / wxh5 / app)
|
||||||
platform := ctx.Value("platform").(string)
|
platformValue := ctx.Value("platform")
|
||||||
|
if platformValue == nil {
|
||||||
|
return "", fmt.Errorf("平台信息不存在,请检查请求头 X-Platform")
|
||||||
|
}
|
||||||
|
platform, ok := platformValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("平台信息格式错误")
|
||||||
|
}
|
||||||
|
|
||||||
var prepayData interface{}
|
var prepayData interface{}
|
||||||
var err error
|
var err error
|
||||||
@@ -266,7 +277,30 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
|||||||
}
|
}
|
||||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
||||||
if findAuthModelErr != nil {
|
if findAuthModelErr != nil {
|
||||||
return "", findAuthModelErr
|
if errors.Is(findAuthModelErr, model.ErrNotFound) {
|
||||||
|
// 用户未绑定微信,尝试通过 code 自动绑定
|
||||||
|
if code == "" {
|
||||||
|
return "", fmt.Errorf("用户未绑定微信小程序账号,请先完成微信登录或提供授权码")
|
||||||
|
}
|
||||||
|
// 通过 code 获取 OpenID
|
||||||
|
openid, getOpenidErr := w.getWechatMiniOpenID(ctx, code)
|
||||||
|
if getOpenidErr != nil {
|
||||||
|
return "", fmt.Errorf("获取微信小程序OpenID失败: %v", getOpenidErr)
|
||||||
|
}
|
||||||
|
// 自动绑定微信账号
|
||||||
|
bindErr := w.bindWechatAuth(ctx, userID, model.UserAuthTypeWxMiniOpenID, openid)
|
||||||
|
if bindErr != nil {
|
||||||
|
return "", fmt.Errorf("绑定微信小程序账号失败: %v", bindErr)
|
||||||
|
}
|
||||||
|
// 重新查询绑定后的认证信息
|
||||||
|
userAuthModel, findAuthModelErr = w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
||||||
|
if findAuthModelErr != nil {
|
||||||
|
return "", fmt.Errorf("查询绑定后的微信认证信息失败: %v", findAuthModelErr)
|
||||||
|
}
|
||||||
|
logx.Infof("用户 %s 已自动绑定微信小程序账号,OpenID: %s", userID, openid)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("查询用户微信认证信息失败: %v", findAuthModelErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prepayData, err = w.CreateWechatMiniProgramOrder(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
prepayData, err = w.CreateWechatMiniProgramOrder(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -279,7 +313,30 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
|||||||
}
|
}
|
||||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||||
if findAuthModelErr != nil {
|
if findAuthModelErr != nil {
|
||||||
return "", findAuthModelErr
|
if errors.Is(findAuthModelErr, model.ErrNotFound) {
|
||||||
|
// 用户未绑定微信,尝试通过 code 自动绑定
|
||||||
|
if code == "" {
|
||||||
|
return "", fmt.Errorf("用户未绑定微信H5账号,请先完成微信登录或提供授权码")
|
||||||
|
}
|
||||||
|
// 通过 code 获取 OpenID
|
||||||
|
openid, getOpenidErr := w.getWechatH5OpenID(ctx, code)
|
||||||
|
if getOpenidErr != nil {
|
||||||
|
return "", fmt.Errorf("获取微信H5 OpenID失败: %v", getOpenidErr)
|
||||||
|
}
|
||||||
|
// 自动绑定微信账号
|
||||||
|
bindErr := w.bindWechatAuth(ctx, userID, model.UserAuthTypeWxh5OpenID, openid)
|
||||||
|
if bindErr != nil {
|
||||||
|
return "", fmt.Errorf("绑定微信H5账号失败: %v", bindErr)
|
||||||
|
}
|
||||||
|
// 重新查询绑定后的认证信息
|
||||||
|
userAuthModel, findAuthModelErr = w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||||
|
if findAuthModelErr != nil {
|
||||||
|
return "", fmt.Errorf("查询绑定后的微信认证信息失败: %v", findAuthModelErr)
|
||||||
|
}
|
||||||
|
logx.Infof("用户 %s 已自动绑定微信H5账号,OpenID: %s", userID, openid)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("查询用户微信认证信息失败: %v", findAuthModelErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -384,3 +441,112 @@ func (w *WechatPayService) GenerateOutTradeNo() string {
|
|||||||
|
|
||||||
return combined
|
return combined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getWechatMiniOpenID 通过 code 获取微信小程序 OpenID
|
||||||
|
func (w *WechatPayService) getWechatMiniOpenID(ctx context.Context, code string) (string, error) {
|
||||||
|
appID := w.config.WechatMini.AppID
|
||||||
|
appSecret := w.config.WechatMini.AppSecret
|
||||||
|
url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appID, appSecret, code)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("请求微信API失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Openid string `json:"openid"`
|
||||||
|
ErrCode int `json:"errcode,omitempty"`
|
||||||
|
ErrMsg string `json:"errmsg,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, &data); err != nil {
|
||||||
|
return "", fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ErrCode != 0 {
|
||||||
|
return "", fmt.Errorf("微信API返回错误: errcode=%d, errmsg=%s", data.ErrCode, data.ErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Openid == "" {
|
||||||
|
return "", fmt.Errorf("openid为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Openid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWechatH5OpenID 通过 code 获取微信H5 OpenID
|
||||||
|
func (w *WechatPayService) getWechatH5OpenID(ctx context.Context, code string) (string, error) {
|
||||||
|
appID := w.config.WechatH5.AppID
|
||||||
|
appSecret := w.config.WechatH5.AppSecret
|
||||||
|
url := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", appID, appSecret, code)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("请求微信API失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Openid string `json:"openid"`
|
||||||
|
ErrCode int `json:"errcode,omitempty"`
|
||||||
|
ErrMsg string `json:"errmsg,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, &data); err != nil {
|
||||||
|
return "", fmt.Errorf("解析响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.ErrCode != 0 {
|
||||||
|
return "", fmt.Errorf("微信API返回错误: errcode=%d, errmsg=%s", data.ErrCode, data.ErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Openid == "" {
|
||||||
|
return "", fmt.Errorf("openid为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Openid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindWechatAuth 绑定微信认证信息到用户账号
|
||||||
|
func (w *WechatPayService) bindWechatAuth(ctx context.Context, userID string, authType string, openid string) error {
|
||||||
|
// 检查该 OpenID 是否已被其他用户绑定
|
||||||
|
existingAuth, err := w.userAuthModel.FindOneByAuthTypeAuthKey(ctx, authType, openid)
|
||||||
|
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||||
|
return fmt.Errorf("查询认证信息失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingAuth != nil {
|
||||||
|
// 如果 OpenID 已被其他用户绑定,检查是否是当前用户
|
||||||
|
if existingAuth.UserId != userID {
|
||||||
|
return fmt.Errorf("该微信账号已被其他用户绑定")
|
||||||
|
}
|
||||||
|
// 如果已经是当前用户绑定的,直接返回成功
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的认证记录
|
||||||
|
userAuth := &model.UserAuth{
|
||||||
|
Id: uuid.NewString(),
|
||||||
|
UserId: userID,
|
||||||
|
AuthType: authType,
|
||||||
|
AuthKey: openid,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.userAuthModel.Insert(ctx, nil, userAuth)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建认证记录失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,25 @@ type AdminAuditAgentResp struct {
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AdminAssignRoleApiReq struct {
|
||||||
|
RoleId string `json:"role_id"`
|
||||||
|
ApiIds []string `json:"api_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdminAssignRoleApiResp struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdminAuditAgentReq struct {
|
||||||
|
AuditId int64 `json:"audit_id"` // 审核记录ID
|
||||||
|
Status int64 `json:"status"` // 审核状态:1=通过,2=拒绝
|
||||||
|
AuditReason string `json:"audit_reason"` // 审核原因(拒绝时必填)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdminAuditAgentResp struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
type AdminAuditRealNameReq struct {
|
type AdminAuditRealNameReq struct {
|
||||||
RealNameId int64 `json:"real_name_id"` // 实名认证记录ID
|
RealNameId int64 `json:"real_name_id"` // 实名认证记录ID
|
||||||
Status int64 `json:"status"` // 审核状态:2=通过,3=拒绝
|
Status int64 `json:"status"` // 审核状态:2=通过,3=拒绝
|
||||||
@@ -1000,6 +1019,22 @@ type AgentApplyResp struct {
|
|||||||
AgentCode int64 `json:"agent_code"`
|
AgentCode int64 `json:"agent_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentApplyReq struct {
|
||||||
|
Region string `json:"region,optional"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Referrer string `json:"referrer"`
|
||||||
|
InviteCode string `json:"invite_code,optional"`
|
||||||
|
AgentCode int64 `json:"agent_code,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentApplyResp struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
AgentCode int64 `json:"agent_code"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentCommissionListItem struct {
|
type AgentCommissionListItem struct {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
@@ -1033,6 +1068,29 @@ type AgentInfoResp struct {
|
|||||||
AgentCode int64 `json:"agent_code"`
|
AgentCode int64 `json:"agent_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentGeneratingLinkReq struct {
|
||||||
|
ProductId string `json:"product_id"` // 产品ID
|
||||||
|
SetPrice float64 `json:"set_price"` // 设定价格
|
||||||
|
TargetPath string `json:"target_path,optional"` // 目标地址(可选,默认为推广报告页面)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentGeneratingLinkResp struct {
|
||||||
|
LinkIdentifier string `json:"link_identifier"` // 推广链接标识
|
||||||
|
FullLink string `json:"full_link"` // 完整短链URL
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentInfoResp struct {
|
||||||
|
AgentId string `json:"agent_id"`
|
||||||
|
Level int64 `json:"level"`
|
||||||
|
LevelName string `json:"level_name"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
WechatId string `json:"wechat_id"`
|
||||||
|
TeamLeaderId string `json:"team_leader_id"`
|
||||||
|
IsRealName bool `json:"is_real_name"`
|
||||||
|
AgentCode int64 `json:"agent_code"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentLinkListItem struct {
|
type AgentLinkListItem struct {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
@@ -1073,7 +1131,8 @@ type AgentOrderListItem struct {
|
|||||||
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
|
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
|
||||||
PriceCost float64 `json:"price_cost"` // 提价成本
|
PriceCost float64 `json:"price_cost"` // 提价成本
|
||||||
AgentProfit float64 `json:"agent_profit"` // 代理收益
|
AgentProfit float64 `json:"agent_profit"` // 代理收益
|
||||||
ProcessStatus int64 `json:"process_status"` // 处理状态
|
ProcessStatus int64 `json:"process_status"` // 处理状态(保留用于筛选,前端不显示)
|
||||||
|
OrderStatus string `json:"order_status"` // 订单状态:pending-待支付,paid-已支付,refunded-已退款,closed-已关闭,failed-支付失败
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1093,6 +1152,10 @@ type AgentProductConfigResp struct {
|
|||||||
List []ProductConfigItem `json:"list"`
|
List []ProductConfigItem `json:"list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentProductConfigResp struct {
|
||||||
|
List []ProductConfigItem `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
type AgentRealNameListItem struct {
|
type AgentRealNameListItem struct {
|
||||||
Id string `json:"id"` // 主键
|
Id string `json:"id"` // 主键
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
@@ -1111,6 +1174,7 @@ type AgentRebateListItem struct {
|
|||||||
OrderId string `json:"order_id"` // 订单ID
|
OrderId string `json:"order_id"` // 订单ID
|
||||||
RebateType int64 `json:"rebate_type"` // 返佣类型
|
RebateType int64 `json:"rebate_type"` // 返佣类型
|
||||||
Amount float64 `json:"amount"` // 金额
|
Amount float64 `json:"amount"` // 金额
|
||||||
|
Status int64 `json:"status"` // 状态:1=已发放,2=已冻结,3=已取消(已退款)
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1310,6 +1374,75 @@ type DeleteRoleResp struct {
|
|||||||
Success bool `json:"success"` // 是否成功
|
Success bool `json:"success"` // 是否成功
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConversionRateResp struct {
|
||||||
|
MyConversionRate ConversionRateData `json:"my_conversion_rate"` // 我的转化率
|
||||||
|
SubordinateConversionRate ConversionRateData `json:"subordinate_conversion_rate"` // 我的下级转化率
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateMenuReq struct {
|
||||||
|
Pid string `json:"pid,optional"` // 父菜单ID
|
||||||
|
Name string `json:"name"` // 路由名称
|
||||||
|
Path string `json:"path,optional"` // 路由路径
|
||||||
|
Component string `json:"component,optional"` // 组件路径
|
||||||
|
Redirect string `json:"redirect,optional"` // 重定向路径
|
||||||
|
Meta map[string]interface{} `json:"meta"` // 路由元数据
|
||||||
|
Status int64 `json:"status,optional,default=1"` // 状态:0-禁用,1-启用
|
||||||
|
Type string `json:"type"` // 类型
|
||||||
|
Sort int64 `json:"sort,optional"` // 排序
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateMenuResp struct {
|
||||||
|
Id string `json:"id"` // 菜单ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateRoleReq struct {
|
||||||
|
RoleName string `json:"role_name"` // 角色名称
|
||||||
|
RoleCode string `json:"role_code"` // 角色编码
|
||||||
|
Description string `json:"description"` // 角色描述
|
||||||
|
Status int64 `json:"status,default=1"` // 状态:0-禁用,1-启用
|
||||||
|
Sort int64 `json:"sort,default=0"` // 排序
|
||||||
|
MenuIds []string `json:"menu_ids"` // 关联的菜单ID列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateRoleResp struct {
|
||||||
|
Id string `json:"id"` // 角色ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateWhitelistOrderReq struct {
|
||||||
|
IdCard string `json:"id_card"` // 身份证号(查询对象标识)
|
||||||
|
FeatureIds []string `json:"feature_ids"` // 要屏蔽的feature ID列表
|
||||||
|
OrderId string `json:"order_id,optional"` // 关联的查询订单ID(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateWhitelistOrderResp struct {
|
||||||
|
OrderId string `json:"order_id"` // 订单ID
|
||||||
|
OrderNo string `json:"order_no"` // 订单号
|
||||||
|
TotalAmount float64 `json:"total_amount"` // 总金额
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteInviteCodeReq struct {
|
||||||
|
Id string `json:"id"` // 邀请码ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteInviteCodeResp struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteMenuReq struct {
|
||||||
|
Id string `path:"id"` // 菜单ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteMenuResp struct {
|
||||||
|
Success bool `json:"success"` // 是否成功
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteRoleReq struct {
|
||||||
|
Id string `path:"id"` // 角色ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteRoleResp struct {
|
||||||
|
Success bool `json:"success"` // 是否成功
|
||||||
|
}
|
||||||
|
|
||||||
type DirectParentRebateConfig struct {
|
type DirectParentRebateConfig struct {
|
||||||
Diamond float64 `json:"diamond"` // 直接上级是钻石的返佣金额(6元)
|
Diamond float64 `json:"diamond"` // 直接上级是钻石的返佣金额(6元)
|
||||||
Gold float64 `json:"gold"` // 直接上级是黄金的返佣金额(3元)
|
Gold float64 `json:"gold"` // 直接上级是黄金的返佣金额(3元)
|
||||||
@@ -1325,6 +1458,15 @@ type DownloadAuthorizationDocumentResp struct {
|
|||||||
FileUrl string `json:"fileUrl"` // 文件访问URL
|
FileUrl string `json:"fileUrl"` // 文件访问URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DownloadAuthorizationDocumentReq struct {
|
||||||
|
DocumentId string `json:"documentId" validate:"required"` // 授权书ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadAuthorizationDocumentResp struct {
|
||||||
|
FileName string `json:"fileName"` // 文件名
|
||||||
|
FileUrl string `json:"fileUrl"` // 文件访问URL
|
||||||
|
}
|
||||||
|
|
||||||
type Feature struct {
|
type Feature struct {
|
||||||
ID string `json:"id"` // 功能ID
|
ID string `json:"id"` // 功能ID
|
||||||
ApiID string `json:"api_id"` // API标识
|
ApiID string `json:"api_id"` // API标识
|
||||||
@@ -1744,6 +1886,32 @@ type MobileCodeLoginResp struct {
|
|||||||
RefreshAfter int64 `json:"refreshAfter"`
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MenuListItem struct {
|
||||||
|
Id string `json:"id"` // 菜单ID
|
||||||
|
Pid string `json:"pid"` // 父菜单ID
|
||||||
|
Name string `json:"name"` // 路由名称
|
||||||
|
Path string `json:"path"` // 路由路径
|
||||||
|
Component string `json:"component"` // 组件路径
|
||||||
|
Redirect string `json:"redirect"` // 重定向路径
|
||||||
|
Meta map[string]interface{} `json:"meta"` // 路由元数据
|
||||||
|
Status int64 `json:"status"` // 状态:0-禁用,1-启用
|
||||||
|
Type string `json:"type"` // 类型
|
||||||
|
Sort int64 `json:"sort"` // 排序
|
||||||
|
CreateTime string `json:"createTime"` // 创建时间
|
||||||
|
Children []MenuListItem `json:"children"` // 子菜单
|
||||||
|
}
|
||||||
|
|
||||||
|
type MobileCodeLoginReq struct {
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MobileCodeLoginResp struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
Title string `json:"title"` // 通知标题
|
Title string `json:"title"` // 通知标题
|
||||||
Content string `json:"content"` // 通知内容 (富文本)
|
Content string `json:"content"` // 通知内容 (富文本)
|
||||||
@@ -1801,6 +1969,7 @@ type OrderListItem struct {
|
|||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
PayTime string `json:"pay_time"` // 支付时间
|
PayTime string `json:"pay_time"` // 支付时间
|
||||||
RefundTime string `json:"refund_time"` // 退款时间
|
RefundTime string `json:"refund_time"` // 退款时间
|
||||||
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单
|
IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单
|
||||||
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态:not_agent-非代理订单,success-处理成功,failed-处理失败,pending-待处理
|
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态:not_agent-非代理订单,success-处理成功,failed-处理失败,pending-待处理
|
||||||
}
|
}
|
||||||
@@ -1894,6 +2063,10 @@ type ProductResponse struct {
|
|||||||
Product
|
Product
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProductResponse struct {
|
||||||
|
Product
|
||||||
|
}
|
||||||
|
|
||||||
type PromotionQueryItem struct {
|
type PromotionQueryItem struct {
|
||||||
Id string `json:"id"` // 查询ID
|
Id string `json:"id"` // 查询ID
|
||||||
OrderId string `json:"order_id"` // 订单ID
|
OrderId string `json:"order_id"` // 订单ID
|
||||||
@@ -1971,6 +2144,27 @@ type QueryGenerateShareLinkResp struct {
|
|||||||
ShareLink string `json:"share_link"`
|
ShareLink string `json:"share_link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryDetailByOrderIdReq struct {
|
||||||
|
OrderId string `path:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDetailByOrderNoReq struct {
|
||||||
|
OrderNo string `path:"order_no"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryExampleReq struct {
|
||||||
|
Feature string `form:"feature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryGenerateShareLinkReq struct {
|
||||||
|
OrderId *string `json:"order_id,optional"`
|
||||||
|
OrderNo *string `json:"order_no,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryGenerateShareLinkResp struct {
|
||||||
|
ShareLink string `json:"share_link"`
|
||||||
|
}
|
||||||
|
|
||||||
type QueryItem struct {
|
type QueryItem struct {
|
||||||
Feature interface{} `json:"feature"`
|
Feature interface{} `json:"feature"`
|
||||||
Data interface{} `json:"data"` // 这里可以是 map 或 具体的 struct
|
Data interface{} `json:"data"` // 这里可以是 map 或 具体的 struct
|
||||||
@@ -1997,6 +2191,27 @@ type QueryProvisionalOrderResp struct {
|
|||||||
Product Product `json:"product"`
|
Product Product `json:"product"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryListReq struct {
|
||||||
|
Page int64 `form:"page"` // 页码
|
||||||
|
PageSize int64 `form:"page_size"` // 每页数据量
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryListResp struct {
|
||||||
|
Total int64 `json:"total"` // 总记录数
|
||||||
|
List []Query `json:"list"` // 查询列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryProvisionalOrderReq struct {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryProvisionalOrderResp struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
IdCard string `json:"id_card"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Product Product `json:"product"`
|
||||||
|
}
|
||||||
|
|
||||||
type QueryReq struct {
|
type QueryReq struct {
|
||||||
Data string `json:"data" validate:"required"`
|
Data string `json:"data" validate:"required"`
|
||||||
}
|
}
|
||||||
@@ -2052,6 +2267,53 @@ type RealNameAuthResp struct {
|
|||||||
Status string `json:"status"` // 状态:pending=待审核,approved=已通过,rejected=已拒绝
|
Status string `json:"status"` // 状态:pending=待审核,approved=已通过,rejected=已拒绝
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryRetryReq struct {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryRetryResp struct {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryServiceReq struct {
|
||||||
|
Product string `path:"product"`
|
||||||
|
Data string `json:"data" validate:"required"`
|
||||||
|
AgentIdentifier string `json:"agent_identifier,optional"`
|
||||||
|
App bool `json:"app,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryServiceResp struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryShareDetailReq struct {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuerySingleTestReq struct {
|
||||||
|
Params map[string]interface{} `json:"params"`
|
||||||
|
Api string `json:"api"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuerySingleTestResp struct {
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
Api string `json:"api"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealNameAuthReq struct {
|
||||||
|
Name string `json:"name"` // 姓名
|
||||||
|
IdCard string `json:"id_card"` // 身份证号
|
||||||
|
Mobile string `json:"mobile"` // 手机号
|
||||||
|
Code string `json:"code"` // 验证码
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealNameAuthResp struct {
|
||||||
|
Status string `json:"status"` // 状态:pending=待审核,approved=已通过,rejected=已拒绝
|
||||||
|
}
|
||||||
|
|
||||||
type RebateItem struct {
|
type RebateItem struct {
|
||||||
Id string `json:"id"` // 记录ID
|
Id string `json:"id"` // 记录ID
|
||||||
SourceAgentId string `json:"source_agent_id"` // 来源代理ID
|
SourceAgentId string `json:"source_agent_id"` // 来源代理ID
|
||||||
@@ -2061,6 +2323,7 @@ type RebateItem struct {
|
|||||||
OrderNo string `json:"order_no"` // 订单号
|
OrderNo string `json:"order_no"` // 订单号
|
||||||
RebateType int64 `json:"rebate_type"` // 返佣类型:1=直接上级,2=钻石上级,3=黄金上级
|
RebateType int64 `json:"rebate_type"` // 返佣类型:1=直接上级,2=钻石上级,3=黄金上级
|
||||||
Amount float64 `json:"amount"` // 返佣金额
|
Amount float64 `json:"amount"` // 返佣金额
|
||||||
|
Status int64 `json:"status"` // 状态:1=已发放,2=已冻结,3=已取消(已退款)
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2090,6 +2353,26 @@ type RegisterByInviteCodeResp struct {
|
|||||||
AgentCode int64 `json:"agent_code"`
|
AgentCode int64 `json:"agent_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegisterByInviteCodeReq struct {
|
||||||
|
Referrer string `json:"referrer"`
|
||||||
|
InviteCode string `json:"invite_code,optional"`
|
||||||
|
AgentCode int64 `json:"agent_code,optional"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Region string `json:"region,optional"`
|
||||||
|
WechatId string `json:"wechat_id,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterByInviteCodeResp struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
|
Level int64 `json:"level"` // 代理等级
|
||||||
|
LevelName string `json:"level_name"` // 等级名称
|
||||||
|
AgentCode int64 `json:"agent_code"`
|
||||||
|
}
|
||||||
|
|
||||||
type RoleListItem struct {
|
type RoleListItem struct {
|
||||||
Id string `json:"id"` // 角色ID
|
Id string `json:"id"` // 角色ID
|
||||||
RoleName string `json:"role_name"` // 角色名称
|
RoleName string `json:"role_name"` // 角色名称
|
||||||
@@ -2104,6 +2387,9 @@ type RoleListItem struct {
|
|||||||
type ShortLinkRedirectResp struct {
|
type ShortLinkRedirectResp struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ShortLinkRedirectResp struct {
|
||||||
|
}
|
||||||
|
|
||||||
type SubordinateItem struct {
|
type SubordinateItem struct {
|
||||||
AgentId string `json:"agent_id"` // 代理ID
|
AgentId string `json:"agent_id"` // 代理ID
|
||||||
Level int64 `json:"level"` // 等级
|
Level int64 `json:"level"` // 等级
|
||||||
@@ -2120,14 +2406,14 @@ type TeamMemberItem struct {
|
|||||||
LevelName string `json:"level_name"` // 等级名称
|
LevelName string `json:"level_name"` // 等级名称
|
||||||
Mobile string `json:"mobile"` // 手机号
|
Mobile string `json:"mobile"` // 手机号
|
||||||
CreateTime string `json:"create_time"` // 加入团队时间
|
CreateTime string `json:"create_time"` // 加入团队时间
|
||||||
TotalRebateAmount float64 `json:"total_rebate_amount"` // 返佣给我的总金额
|
|
||||||
TodayRebateAmount float64 `json:"today_rebate_amount"` // 返佣给我的今日金额
|
|
||||||
TodayInvites int64 `json:"today_invites"` // 邀请加入团队的今日人数
|
|
||||||
MonthInvites int64 `json:"month_invites"` // 邀请加入团队的本月人数
|
|
||||||
TotalInvites int64 `json:"total_invites"` // 邀请加入团队的总人数
|
|
||||||
TodayQueries int64 `json:"today_queries"` // 当日查询量
|
TodayQueries int64 `json:"today_queries"` // 当日查询量
|
||||||
MonthQueries int64 `json:"month_queries"` // 本月查询量
|
MonthQueries int64 `json:"month_queries"` // 本月查询量
|
||||||
TotalQueries int64 `json:"total_queries"` // 总查询量
|
TotalQueries int64 `json:"total_queries"` // 总查询量
|
||||||
|
TotalRebateAmount float64 `json:"total_rebate_amount"` // 返佣给我的总金额
|
||||||
|
TodayRebateAmount float64 `json:"today_rebate_amount"` // 返佣给我的今日金额
|
||||||
|
TotalInvites int64 `json:"total_invites"` // 邀请加入团队的总人数
|
||||||
|
TodayInvites int64 `json:"today_invites"` // 邀请加入团队的今日人数
|
||||||
|
MonthInvites int64 `json:"month_invites"` // 邀请加入团队的本月人数
|
||||||
IsDirect bool `json:"is_direct"` // 是否直接下级
|
IsDirect bool `json:"is_direct"` // 是否直接下级
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2194,6 +2480,57 @@ type UpdateRoleResp struct {
|
|||||||
Success bool `json:"success"` // 是否成功
|
Success bool `json:"success"` // 是否成功
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TeamStatisticsResp struct {
|
||||||
|
TotalCount int64 `json:"total_count"` // 团队总人数(不包括自己)
|
||||||
|
DirectCount int64 `json:"direct_count"` // 直接下级数量
|
||||||
|
IndirectCount int64 `json:"indirect_count"` // 间接下级数量
|
||||||
|
GoldCount int64 `json:"gold_count"` // 黄金代理数量
|
||||||
|
NormalCount int64 `json:"normal_count"` // 普通代理数量
|
||||||
|
TodayNewMembers int64 `json:"today_new_members"` // 今日新增成员
|
||||||
|
MonthNewMembers int64 `json:"month_new_members"` // 本月新增成员
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateMenuReq struct {
|
||||||
|
Id string `path:"id"` // 菜单ID
|
||||||
|
Pid *string `json:"pid,optional"` // 父菜单ID
|
||||||
|
Name string `json:"name"` // 路由名称
|
||||||
|
Path string `json:"path,optional"` // 路由路径
|
||||||
|
Component string `json:"component,optional"` // 组件路径
|
||||||
|
Redirect string `json:"redirect,optional"` // 重定向路径
|
||||||
|
Meta map[string]interface{} `json:"meta"` // 路由元数据
|
||||||
|
Status int64 `json:"status,optional"` // 状态:0-禁用,1-启用
|
||||||
|
Type string `json:"type"` // 类型
|
||||||
|
Sort int64 `json:"sort,optional"` // 排序
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateMenuResp struct {
|
||||||
|
Success bool `json:"success"` // 是否成功
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateQueryDataReq struct {
|
||||||
|
Id string `json:"id"` // 查询ID
|
||||||
|
QueryData string `json:"query_data"` // 查询数据(未加密的JSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateQueryDataResp struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
UpdatedAt string `json:"updated_at"` // 更新时间
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRoleReq struct {
|
||||||
|
Id string `path:"id"` // 角色ID
|
||||||
|
RoleName *string `json:"role_name,optional"` // 角色名称
|
||||||
|
RoleCode *string `json:"role_code,optional"` // 角色编码
|
||||||
|
Description *string `json:"description,optional"` // 角色描述
|
||||||
|
Status *int64 `json:"status,optional"` // 状态:0-禁用,1-启用
|
||||||
|
Sort *int64 `json:"sort,optional"` // 排序
|
||||||
|
MenuIds []string `json:"menu_ids,optional"` // 关联的菜单ID列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRoleResp struct {
|
||||||
|
Success bool `json:"success"` // 是否成功
|
||||||
|
}
|
||||||
|
|
||||||
type UpgradeFeeConfig struct {
|
type UpgradeFeeConfig struct {
|
||||||
NormalToGold float64 `json:"normal_to_gold"` // 普通→黄金:199
|
NormalToGold float64 `json:"normal_to_gold"` // 普通→黄金:199
|
||||||
NormalToDiamond float64 `json:"normal_to_diamond"` // 普通→钻石:980
|
NormalToDiamond float64 `json:"normal_to_diamond"` // 普通→钻石:980
|
||||||
@@ -2236,6 +2573,15 @@ type UpgradeSubordinateResp struct {
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpgradeSubordinateReq struct {
|
||||||
|
SubordinateId string `json:"subordinate_id"` // 下级代理ID
|
||||||
|
ToLevel int64 `json:"to_level"` // 目标等级(只能是2=黄金)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpgradeSubordinateResp struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
@@ -2286,6 +2632,49 @@ type WhitelistItem struct {
|
|||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserInfoResp struct {
|
||||||
|
UserInfo User `json:"userInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXH5AuthReq struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXH5AuthResp struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXMiniAuthReq struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WXMiniAuthResp struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AccessExpire int64 `json:"accessExpire"`
|
||||||
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhitelistFeatureItem struct {
|
||||||
|
FeatureId string `json:"feature_id"` // Feature的UUID
|
||||||
|
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
|
||||||
|
FeatureName string `json:"feature_name"` // Feature的名称
|
||||||
|
WhitelistPrice float64 `json:"whitelist_price"` // 屏蔽价格(单位:元)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhitelistItem struct {
|
||||||
|
Id string `json:"id"` // 白名单记录ID
|
||||||
|
IdCard string `json:"id_card"` // 身份证号
|
||||||
|
FeatureId string `json:"feature_id"` // Feature的UUID
|
||||||
|
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
|
||||||
|
FeatureName string `json:"feature_name"` // Feature的名称
|
||||||
|
Amount float64 `json:"amount"` // 费用
|
||||||
|
Status int64 `json:"status"` // 状态:1=生效,2=已失效
|
||||||
|
StatusText string `json:"status_text"` // 状态文本
|
||||||
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
type WithdrawalItem struct {
|
type WithdrawalItem struct {
|
||||||
Id string `json:"id"` // 记录ID
|
Id string `json:"id"` // 记录ID
|
||||||
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"qnc-server/common/globalkey"
|
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -21,6 +19,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
"qnc-server/common/globalkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -59,21 +59,24 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
AgentWithdrawal struct {
|
AgentWithdrawal struct {
|
||||||
Id string `db:"id"`
|
Id string `db:"id"`
|
||||||
AgentId string `db:"agent_id"`
|
AgentId string `db:"agent_id"`
|
||||||
WithdrawNo string `db:"withdraw_no"` // 提现单号
|
WithdrawalType int64 `db:"withdrawal_type"` // 提现方式:1=支付宝,2=银行卡
|
||||||
PayeeAccount string `db:"payee_account"` // 收款账户
|
WithdrawNo string `db:"withdraw_no"` // 提现单号
|
||||||
PayeeName string `db:"payee_name"` // 收款人姓名
|
PayeeAccount string `db:"payee_account"` // 收款账户
|
||||||
Amount float64 `db:"amount"` // 提现金额
|
BankCardNo sql.NullString `db:"bank_card_no"` // 银行卡号(银行卡提现时使用)
|
||||||
ActualAmount float64 `db:"actual_amount"` // 实际到账金额(扣除税费后)
|
BankName sql.NullString `db:"bank_name"` // 开户行名称(银行卡提现时使用)
|
||||||
TaxAmount float64 `db:"tax_amount"` // 税费金额
|
PayeeName string `db:"payee_name"` // 收款人姓名
|
||||||
Status int64 `db:"status"` // 状态:1=处理中,2=成功,3=失败
|
Amount float64 `db:"amount"` // 提现金额
|
||||||
Remark sql.NullString `db:"remark"` // 备注
|
ActualAmount float64 `db:"actual_amount"` // 实际到账金额(扣除税费后)
|
||||||
CreateTime time.Time `db:"create_time"` // 创建时间
|
TaxAmount float64 `db:"tax_amount"` // 税费金额
|
||||||
UpdateTime time.Time `db:"update_time"` // 更新时间
|
Status int64 `db:"status"` // 状态:1=处理中,2=成功,3=失败
|
||||||
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
|
Remark sql.NullString `db:"remark"` // 备注
|
||||||
DelState int64 `db:"del_state"` // 删除状态:0=未删除,1=已删除
|
CreateTime time.Time `db:"create_time"` // 创建时间
|
||||||
Version int64 `db:"version"` // 版本号(乐观锁)
|
UpdateTime time.Time `db:"update_time"` // 更新时间
|
||||||
|
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
|
||||||
|
DelState int64 `db:"del_state"` // 删除状态:0=未删除,1=已删除
|
||||||
|
Version int64 `db:"version"` // 版本号(乐观锁)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,11 +93,11 @@ func (m *defaultAgentWithdrawalModel) Insert(ctx context.Context, session sqlx.S
|
|||||||
qncAgentWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalIdPrefix, data.Id)
|
qncAgentWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalIdPrefix, data.Id)
|
||||||
qncAgentWithdrawalWithdrawNoKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalWithdrawNoPrefix, data.WithdrawNo)
|
qncAgentWithdrawalWithdrawNoKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalWithdrawNoPrefix, data.WithdrawNo)
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentWithdrawalRowsExpectAutoSet)
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentWithdrawalRowsExpectAutoSet)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawNo, data.PayeeAccount, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
return session.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawalType, data.WithdrawNo, data.PayeeAccount, data.BankCardNo, data.BankName, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawNo, data.PayeeAccount, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
return conn.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawalType, data.WithdrawNo, data.PayeeAccount, data.BankCardNo, data.BankName, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||||
}
|
}
|
||||||
func (m *defaultAgentWithdrawalModel) insertUUID(data *AgentWithdrawal) {
|
func (m *defaultAgentWithdrawalModel) insertUUID(data *AgentWithdrawal) {
|
||||||
@@ -161,9 +164,9 @@ func (m *defaultAgentWithdrawalModel) Update(ctx context.Context, session sqlx.S
|
|||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentWithdrawalRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentWithdrawalRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,9 +187,9 @@ func (m *defaultAgentWithdrawalModel) UpdateWithVersion(ctx context.Context, ses
|
|||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentWithdrawalRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentWithdrawalRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ $tables = @(
|
|||||||
# "admin_user_role",
|
# "admin_user_role",
|
||||||
# "agent",
|
# "agent",
|
||||||
# "agent_commission",
|
# "agent_commission",
|
||||||
# "agent_config"
|
# # "agent_config"
|
||||||
# "agent_freeze_task",
|
# "agent_freeze_task",
|
||||||
# "agent_invite_code",
|
# "agent_invite_code",
|
||||||
# "agent_invite_code_usage",
|
# "agent_invite_code_usage",
|
||||||
@@ -49,8 +49,8 @@ $tables = @(
|
|||||||
# "agent_short_link",
|
# "agent_short_link",
|
||||||
# "agent_upgrade",
|
# "agent_upgrade",
|
||||||
# "agent_wallet",
|
# "agent_wallet",
|
||||||
# "agent_withdrawal",
|
"agent_withdrawal",
|
||||||
# "agent_withdrawal_tax",
|
"agent_withdrawal_tax"
|
||||||
# "authorization_document",
|
# "authorization_document",
|
||||||
# "example",
|
# "example",
|
||||||
# "feature",
|
# "feature",
|
||||||
@@ -70,8 +70,9 @@ $tables = @(
|
|||||||
# 为每个表生成模型
|
# 为每个表生成模型
|
||||||
foreach ($table in $tables) {
|
foreach ($table in $tables) {
|
||||||
Write-Host "正在生成表: $table" -ForegroundColor Green
|
Write-Host "正在生成表: $table" -ForegroundColor Green
|
||||||
|
# goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:21201)/qnc" -table="$table" -dir="./model" --home="$HOME_DIR" -cache=true --style=goZero
|
||||||
goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:21201)/qnc" -table="$table" -dir="./model" --home="$HOME_DIR" -cache=true --style=goZero
|
goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:21201)/qnc" -table="$table" -dir="./model" --home="$HOME_DIR" -cache=true --style=goZero
|
||||||
|
|
||||||
# 移动生成的文件到目标目录
|
# 移动生成的文件到目标目录
|
||||||
if (Test-Path $OUTPUT_DIR) {
|
if (Test-Path $OUTPUT_DIR) {
|
||||||
$sourceFiles = Get-ChildItem -Path $OUTPUT_DIR -File
|
$sourceFiles = Get-ChildItem -Path $OUTPUT_DIR -File
|
||||||
|
|||||||
26
deploy/sql/withdrawal_alipay_quota_migration.sql
Normal file
26
deploy/sql/withdrawal_alipay_quota_migration.sql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- 支付宝月度提现额度配置
|
||||||
|
-- ============================================
|
||||||
|
-- 说明:为代理系统增加支付宝每月提现额度配置,默认 800 元
|
||||||
|
-- 执行时间:2026-01-06
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
`agent_config` (
|
||||||
|
`id`,
|
||||||
|
`config_key`,
|
||||||
|
`config_value`,
|
||||||
|
`config_type`,
|
||||||
|
`description`
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
||||||
|
'alipay_month_quota',
|
||||||
|
'800.00',
|
||||||
|
'withdrawal',
|
||||||
|
'支付宝每月提现额度(单位:元)'
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`config_value` = VALUES(`config_value`),
|
||||||
|
`config_type` = VALUES(`config_type`),
|
||||||
|
`description` = VALUES(`description`);
|
||||||
36
deploy/sql/withdrawal_bank_card_migration.sql
Normal file
36
deploy/sql/withdrawal_bank_card_migration.sql
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- 代理提现表 - 添加银行卡提现支持
|
||||||
|
-- ============================================
|
||||||
|
-- 说明:为 agent_withdrawal 表添加提现方式字段和银行卡相关字段
|
||||||
|
-- 执行时间:2024-XX-XX
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- 1. 添加提现方式字段(1=支付宝,2=银行卡)
|
||||||
|
ALTER TABLE `agent_withdrawal`
|
||||||
|
ADD COLUMN `withdrawal_type` tinyint NOT NULL DEFAULT 1 COMMENT '提现方式:1=支付宝,2=银行卡' AFTER `agent_id`;
|
||||||
|
|
||||||
|
-- 2. 添加银行卡号字段(银行卡提现时使用)
|
||||||
|
ALTER TABLE `agent_withdrawal`
|
||||||
|
ADD COLUMN `bank_card_no` varchar(100) DEFAULT NULL COMMENT '银行卡号(银行卡提现时使用)' AFTER `payee_account`;
|
||||||
|
|
||||||
|
-- 3. 添加开户行名称字段(银行卡提现时使用)
|
||||||
|
ALTER TABLE `agent_withdrawal`
|
||||||
|
ADD COLUMN `bank_name` varchar(100) DEFAULT NULL COMMENT '开户行名称(银行卡提现时使用)' AFTER `bank_card_no`;
|
||||||
|
|
||||||
|
-- 4. 为提现方式字段添加索引(用于按提现方式查询)
|
||||||
|
ALTER TABLE `agent_withdrawal`
|
||||||
|
ADD INDEX `idx_withdrawal_type` (`withdrawal_type`);
|
||||||
|
|
||||||
|
-- 5. 为提现方式和状态添加复合索引(用于后台审核查询)
|
||||||
|
ALTER TABLE `agent_withdrawal`
|
||||||
|
ADD INDEX `idx_type_status` (`withdrawal_type`, `status`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 说明:
|
||||||
|
-- 1. withdrawal_type 默认值为 1(支付宝),兼容历史数据
|
||||||
|
-- 2. bank_card_no 和 bank_name 为可选字段,支付宝提现时为空
|
||||||
|
-- 3. 对于支付宝提现,payee_account 存储支付宝账号
|
||||||
|
-- 4. 对于银行卡提现,payee_account 可以存储银行卡号(与 bank_card_no 保持一致),或留空
|
||||||
|
-- ============================================
|
||||||
10
go.mod
10
go.mod
@@ -6,9 +6,10 @@ toolchain go1.23.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.4
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3
|
||||||
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13
|
||||||
github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6
|
github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6
|
||||||
github.com/alibabacloud-go/tea v1.2.2
|
github.com/alibabacloud-go/tea v1.3.13
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
||||||
github.com/bytedance/sonic v1.13.0
|
github.com/bytedance/sonic v1.13.0
|
||||||
github.com/fogleman/gg v1.3.0
|
github.com/fogleman/gg v1.3.0
|
||||||
@@ -39,13 +40,12 @@ require (
|
|||||||
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
||||||
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
|
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
github.com/aliyun/credentials-go v1.4.5 // indirect
|
||||||
github.com/aliyun/credentials-go v1.3.10 // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.2 // indirect
|
github.com/bytedance/sonic/loader v0.2.2 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
|||||||
42
go.sum
42
go.sum
@@ -13,6 +13,8 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2
|
|||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
||||||
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3 h1:0Aobw12m3x28aeDMPjwjXsfF8MuLvRjlQ4Hhoy5hFOY=
|
||||||
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3/go.mod h1:ydzBIN2OiM7eeQPpAFyBrv1H5TY1MtUP2rQig44C4UQ=
|
||||||
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
||||||
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
||||||
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
||||||
@@ -20,8 +22,8 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F
|
|||||||
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
|
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
|
||||||
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
|
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
|
||||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
||||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
|
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
|
||||||
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
|
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
|
||||||
@@ -44,26 +46,24 @@ github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/Ke
|
|||||||
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||||
github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||||
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||||
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
|
|
||||||
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
||||||
|
github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94=
|
||||||
|
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
|
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.3/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.3/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
|
||||||
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
|
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
|
||||||
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
|
||||||
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
||||||
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
|
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
|
||||||
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
|
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
|
||||||
github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA=
|
github.com/aliyun/credentials-go v1.4.5 h1:O76WYKgdy1oQYYiJkERjlA2dxGuvLRrzuO2ScrtGWSk=
|
||||||
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
@@ -81,8 +81,9 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
|||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
|
||||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
|
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||||
|
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
@@ -326,10 +327,13 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -342,6 +346,9 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -356,10 +363,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -370,6 +380,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -384,20 +397,27 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -406,6 +426,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
@@ -420,6 +442,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
153
pkg/captcha/aliyun.go
Normal file
153
pkg/captcha/aliyun.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"qnc-server/common/xerr"
|
||||||
|
|
||||||
|
captcha20230305 "github.com/alibabacloud-go/captcha-20230305/client"
|
||||||
|
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
AccessKeyID string
|
||||||
|
AccessKeySecret string
|
||||||
|
EndpointURL string
|
||||||
|
SceneID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyResult 验证结果
|
||||||
|
type VerifyResult struct {
|
||||||
|
Verified bool // 是否通过滑块验证
|
||||||
|
Skipped bool // 是否跳过验证(无滑块参数)
|
||||||
|
VerifyErr error // 验证错误(如果有)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWeChatUserAgent 检测是否为微信浏览器的 User-Agent
|
||||||
|
func isWeChatUserAgent(userAgent string) bool {
|
||||||
|
if userAgent == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ua := strings.ToLower(userAgent)
|
||||||
|
return strings.Contains(ua, "micromessenger") || strings.Contains(ua, "wechat")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyOptionalWithUserAgent 可选验证阿里云验证码(支持微信环境跳过验证)
|
||||||
|
// 当 captchaVerifyParam 为空时返回 Skipped=true,由调用方决定后续处理
|
||||||
|
// 当 userAgent 为微信浏览器时,跳过验证返回 Verified=true
|
||||||
|
func VerifyOptionalWithUserAgent(cfg Config, captchaVerifyParam string, userAgent string) VerifyResult {
|
||||||
|
// 开发环境可跳过验证
|
||||||
|
if os.Getenv("ENV") == "development" {
|
||||||
|
return VerifyResult{Verified: true, Skipped: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信环境下跳过图形验证码校验
|
||||||
|
if isWeChatUserAgent(userAgent) {
|
||||||
|
return VerifyResult{Verified: true, Skipped: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有滑块验证码参数,报错提示
|
||||||
|
if captchaVerifyParam == "" {
|
||||||
|
return VerifyResult{VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCfg := &openapi.Config{
|
||||||
|
AccessKeyId: tea.String(cfg.AccessKeyID),
|
||||||
|
AccessKeySecret: tea.String(cfg.AccessKeySecret),
|
||||||
|
}
|
||||||
|
clientCfg.Endpoint = tea.String(cfg.EndpointURL)
|
||||||
|
client, err := captcha20230305.NewClient(clientCfg)
|
||||||
|
if err != nil {
|
||||||
|
return VerifyResult{VerifyErr: errors.Wrapf(err, "create aliyun captcha client error")}
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
|
||||||
|
SceneId: tea.String(cfg.SceneID),
|
||||||
|
CaptchaVerifyParam: tea.String(captchaVerifyParam),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.VerifyIntelligentCaptcha(req)
|
||||||
|
if err != nil {
|
||||||
|
return VerifyResult{VerifyErr: errors.Wrapf(err, "verify aliyun captcha error")}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tea.BoolValue(resp.Body.Result.VerifyResult) {
|
||||||
|
return VerifyResult{Verified: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VerifyResult{
|
||||||
|
VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "aliyun captcha verify failed: code=%s, msg=%s",
|
||||||
|
tea.StringValue(resp.Body.Code), tea.StringValue(resp.Body.Message)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWithUserAgent 验证阿里云验证码(必须提供验证码参数,支持微信环境跳过验证)
|
||||||
|
func VerifyWithUserAgent(cfg Config, captchaVerifyParam string, userAgent string) error {
|
||||||
|
result := VerifyOptionalWithUserAgent(cfg, captchaVerifyParam, userAgent)
|
||||||
|
if result.VerifyErr != nil {
|
||||||
|
return result.VerifyErr
|
||||||
|
}
|
||||||
|
if result.Skipped {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyOptional 可选验证阿里云验证码(兼容旧接口,不判断 User-Agent)
|
||||||
|
// 当 captchaVerifyParam 为空时返回 Skipped=true,由调用方决定后续处理
|
||||||
|
func VerifyOptional(cfg Config, captchaVerifyParam string) VerifyResult {
|
||||||
|
// 开发环境可跳过验证
|
||||||
|
if os.Getenv("ENV") == "development" {
|
||||||
|
return VerifyResult{Verified: true, Skipped: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有滑块验证码参数,返回跳过状态
|
||||||
|
if captchaVerifyParam == "" {
|
||||||
|
return VerifyResult{Skipped: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCfg := &openapi.Config{
|
||||||
|
AccessKeyId: tea.String(cfg.AccessKeyID),
|
||||||
|
AccessKeySecret: tea.String(cfg.AccessKeySecret),
|
||||||
|
}
|
||||||
|
clientCfg.Endpoint = tea.String(cfg.EndpointURL)
|
||||||
|
client, err := captcha20230305.NewClient(clientCfg)
|
||||||
|
if err != nil {
|
||||||
|
return VerifyResult{VerifyErr: errors.Wrapf(err, "create aliyun captcha client error")}
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
|
||||||
|
SceneId: tea.String(cfg.SceneID),
|
||||||
|
CaptchaVerifyParam: tea.String(captchaVerifyParam),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.VerifyIntelligentCaptcha(req)
|
||||||
|
if err != nil {
|
||||||
|
return VerifyResult{VerifyErr: errors.Wrapf(err, "verify aliyun captcha error")}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tea.BoolValue(resp.Body.Result.VerifyResult) {
|
||||||
|
return VerifyResult{Verified: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VerifyResult{
|
||||||
|
VerifyErr: errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "aliyun captcha verify failed: code=%s, msg=%s",
|
||||||
|
tea.StringValue(resp.Body.Code), tea.StringValue(resp.Body.Message)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify 验证阿里云验证码(必须提供验证码参数,兼容旧接口)
|
||||||
|
func Verify(cfg Config, captchaVerifyParam string) error {
|
||||||
|
result := VerifyOptional(cfg, captchaVerifyParam)
|
||||||
|
if result.VerifyErr != nil {
|
||||||
|
return result.VerifyErr
|
||||||
|
}
|
||||||
|
if result.Skipped {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
29
pkg/captcha/encrypt_scene.go
Normal file
29
pkg/captcha/encrypt_scene.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateEncryptedSceneID: sceneId×tamp&expireTime -> AES-256-CBC + PKCS7 -> Base64(IV + ciphertext)
|
||||||
|
func GenerateEncryptedSceneID(sceneId, ekey string, expireSeconds int) (string, error) {
|
||||||
|
if expireSeconds <= 0 || expireSeconds > 86400 {
|
||||||
|
expireSeconds = 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := time.Now().Unix()
|
||||||
|
plaintext := fmt.Sprintf("%s&%d&%d", sceneId, ts, expireSeconds)
|
||||||
|
|
||||||
|
keyBytes, err := base64.StdEncoding.DecodeString(ekey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("decode ekey error: %w", err)
|
||||||
|
}
|
||||||
|
if len(keyBytes) != 32 {
|
||||||
|
return "", fmt.Errorf("invalid ekey length, need 32 bytes after base64 decode, got %d", len(keyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.AesEncrypt([]byte(plaintext), keyBytes)
|
||||||
|
}
|
||||||
@@ -104,7 +104,6 @@ func validDate(fl validator.FieldLevel) bool {
|
|||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 自定义身份证校验(增强版)
|
// 自定义身份证校验(增强版)
|
||||||
// 校验规则:
|
// 校验规则:
|
||||||
// 1. 格式:18位,前6位地区码(首位不为0),7-14位出生日期,15-17位顺序码,18位校验码
|
// 1. 格式:18位,前6位地区码(首位不为0),7-14位出生日期,15-17位顺序码,18位校验码
|
||||||
@@ -216,8 +215,12 @@ func validatePayMethod(fl validator.FieldLevel) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validTypes := map[string]bool{
|
validTypes := map[string]bool{
|
||||||
"alipay": true, // 中国电信
|
"alipay": true,
|
||||||
"wechatpay": true, // 中国移动
|
"wechat": true,
|
||||||
|
"wechatpay": true,
|
||||||
|
"appleiap": true,
|
||||||
|
"test": true,
|
||||||
|
"test_empty": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return validTypes[payMethod]
|
return validTypes[payMethod]
|
||||||
|
|||||||
Reference in New Issue
Block a user