Compare commits

..

34 Commits

Author SHA1 Message Date
5973457bd8 f 2026-04-08 18:34:19 +08:00
Mrx
4947679b83 close 2026-03-02 12:42:14 +08:00
Mrx
3942553ab3 fadd 2026-02-28 12:29:30 +08:00
Mrx
1e2d5b199a f 2026-02-28 12:00:07 +08:00
Mrx
38c4f65b5d add 2026-02-27 12:06:51 +08:00
Mrx
7805d795c3 f 2026-02-25 17:49:45 +08:00
Mrx
5c8b99b28d up seo 2026-02-14 11:15:00 +08:00
97fa4696df Merge branch 'main' of http://1.117.67.95:3000/team/hm-server 2026-02-11 17:58:20 +08:00
c2de066043 f 2026-02-11 17:56:31 +08:00
Mrx
0d25783e48 fadd 2026-02-11 17:53:50 +08:00
Mrx
e1c5512532 f 2026-02-04 13:15:01 +08:00
Mrx
11c1c2f930 f 2026-02-02 14:58:48 +08:00
67eea00b63 f 2026-02-01 17:16:41 +08:00
Mrx
82d8915d30 f 2026-01-27 18:59:00 +08:00
Mrx
602d5f5f71 f 2026-01-27 18:45:44 +08:00
Mrx
67082972a9 f 2026-01-26 15:47:59 +08:00
Mrx
018abc95d6 f 2026-01-26 15:34:01 +08:00
Mrx
38b275d019 f 2026-01-26 15:17:04 +08:00
Mrx
53a97aa04f f 2026-01-26 15:12:39 +08:00
3dcef00566 f ‘部分退款’ 2026-01-20 20:18:46 +08:00
ed8d311e02 f注释 2026-01-07 17:24:09 +08:00
ec72e47695 f add 2026-01-07 16:50:42 +08:00
9e42257e4e add config 2026-01-05 12:49:57 +08:00
64b758a36d fix add 修改钱包余额 2026-01-04 16:10:47 +08:00
ba2624e310 try fix 2026-01-04 11:24:48 +08:00
21b4069465 fix and add 2026-01-03 17:53:19 +08:00
0211c67f86 fix 2025-12-31 15:21:31 +08:00
cc039fcf35 fix 2025-12-31 14:56:31 +08:00
7d76b2e3fa fix 2025-12-31 12:58:55 +08:00
f155c64a0b add extension_time 2025-12-31 12:53:19 +08:00
193b47893d add and fix 一键解冻 2025-12-31 12:40:41 +08:00
5abfd7adbd add.fixstatus=0 2025-12-30 18:29:36 +08:00
12659c1d14 fix add 2025-12-30 17:55:00 +08:00
1150c43219 fix 2025-12-30 11:35:38 +08:00
93 changed files with 3987 additions and 397 deletions

8
.gitignore vendored
View File

@@ -20,3 +20,11 @@ data/*
/tmp/ /tmp/
/app/api /app/api
# debug binary (Delve)
_debug_bin.*
*.exe
# authorization documents (PDF files)
app/main/api/data/authorization_docs/
**/authorization_docs/**/*.pdf

View File

@@ -26,6 +26,14 @@ service main {
@handler AdminGetAgentCommissionList @handler AdminGetAgentCommissionList
get /agent-commission/list (AdminGetAgentCommissionListReq) returns (AdminGetAgentCommissionListResp) get /agent-commission/list (AdminGetAgentCommissionListReq) returns (AdminGetAgentCommissionListResp)
// 代理佣金状态更新
@handler AdminUpdateAgentCommissionStatus
post /agent-commission/update-status (AdminUpdateAgentCommissionStatusReq) returns (AdminUpdateAgentCommissionStatusResp)
// 批量解冻代理佣金
@handler AdminBatchUnfreezeAgentCommission
post /agent-commission/batch-unfreeze (AdminBatchUnfreezeAgentCommissionReq) returns (AdminBatchUnfreezeAgentCommissionResp)
// 代理奖励分页查询 // 代理奖励分页查询
@handler AdminGetAgentRewardList @handler AdminGetAgentRewardList
get /agent-reward/list (AdminGetAgentRewardListReq) returns (AdminGetAgentRewardListResp) get /agent-reward/list (AdminGetAgentRewardListReq) returns (AdminGetAgentRewardListResp)
@@ -38,6 +46,14 @@ service main {
@handler AdminGetAgentCommissionDeductionList @handler AdminGetAgentCommissionDeductionList
get /agent-commission-deduction/list (AdminGetAgentCommissionDeductionListReq) returns (AdminGetAgentCommissionDeductionListResp) get /agent-commission-deduction/list (AdminGetAgentCommissionDeductionListReq) returns (AdminGetAgentCommissionDeductionListResp)
// 获取代理钱包信息
@handler AdminGetAgentWallet
get /wallet/:agent_id (AdminGetAgentWalletReq) returns (AdminGetAgentWalletResp)
// 修改代理钱包余额
@handler AdminUpdateAgentWalletBalance
post /wallet/update-balance (AdminUpdateAgentWalletBalanceReq) returns (AdminUpdateAgentWalletBalanceResp)
// 平台抽佣分页查询 // 平台抽佣分页查询
@handler AdminGetAgentPlatformDeductionList @handler AdminGetAgentPlatformDeductionList
get /agent-platform-deduction/list (AdminGetAgentPlatformDeductionListReq) returns (AdminGetAgentPlatformDeductionListResp) get /agent-platform-deduction/list (AdminGetAgentPlatformDeductionListReq) returns (AdminGetAgentPlatformDeductionListResp)
@@ -81,6 +97,19 @@ service main {
// 获取代理链接产品统计 // 获取代理链接产品统计
@handler AdminGetAgentLinkProductStatistics @handler AdminGetAgentLinkProductStatistics
get /agent-link/product-statistics (AdminGetAgentLinkProductStatisticsReq) returns (AdminGetAgentLinkProductStatisticsResp) get /agent-link/product-statistics (AdminGetAgentLinkProductStatisticsReq) returns (AdminGetAgentLinkProductStatisticsResp)
// 获取系统配置
@handler AdminGetSystemConfig
get /system-config (AdminGetSystemConfigResp)
// 更新系统配置
@handler AdminUpdateSystemConfig
post /system-config (AdminUpdateSystemConfigReq) returns (AdminUpdateSystemConfigResp)
// 代理钱包流水分页查询
@handler AdminGetAgentWalletTransactionList
get /wallet-transaction/list (AdminGetAgentWalletTransactionListReq) returns (AdminGetAgentWalletTransactionListResp)
} }
type ( type (
@@ -148,8 +177,11 @@ type (
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选 AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
OrderId *int64 `form:"order_id,optional"` // 订单ID可选
ProductName *string `form:"product_name,optional"` // 产品名(可选) ProductName *string `form:"product_name,optional"` // 产品名(可选)
Status *int64 `form:"status,optional"` // 状态(可选) Status *int64 `form:"status,optional"` // 状态(可选)
CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选)
CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选)
} }
// 代理佣金列表项 // 代理佣金列表项
@@ -169,6 +201,29 @@ type (
Items []AgentCommissionListItem `json:"items"` // 列表数据 Items []AgentCommissionListItem `json:"items"` // 列表数据
} }
// 代理佣金状态更新请求
AdminUpdateAgentCommissionStatusReq {
Id int64 `json:"id"` // 佣金记录ID
Status int64 `json:"status"` // 状态:0-已结算,1-冻结中,2-已取消
}
// 代理佣金状态更新响应
AdminUpdateAgentCommissionStatusResp {
Success bool `json:"success"` // 是否成功
}
// 批量解冻代理佣金请求
AdminBatchUnfreezeAgentCommissionReq {
AgentId *int64 `json:"agent_id,optional"` // 代理ID可选。如果不传则解冻所有冻结中的佣金
}
// 批量解冻代理佣金响应
AdminBatchUnfreezeAgentCommissionResp {
Success bool `json:"success"` // 是否成功
Count int64 `json:"count"` // 解冻的数量
Amount float64 `json:"amount"` // 解冻的总金额
}
// 代理奖励分页查询请求 // 代理奖励分页查询请求
AdminGetAgentRewardListReq { AdminGetAgentRewardListReq {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
@@ -201,6 +256,7 @@ type (
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选 AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
Status *int64 `form:"status,optional"` // 状态(可选) Status *int64 `form:"status,optional"` // 状态(可选)
WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选) WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选)
WithdrawType *int64 `form:"withdraw_type,optional"` // 提现类型可选1-支付宝,2-银行卡
} }
// 代理提现列表项 // 代理提现列表项
@@ -468,4 +524,76 @@ type (
AdminGetAgentLinkProductStatisticsResp { AdminGetAgentLinkProductStatisticsResp {
Items []AgentLinkProductStatisticsItem `json:"items"` // 列表数据 Items []AgentLinkProductStatisticsItem `json:"items"` // 列表数据
} }
// 获取代理钱包信息请求
AdminGetAgentWalletReq {
AgentId int64 `path:"agent_id"` // 代理ID
}
// 获取代理钱包信息响应
AdminGetAgentWalletResp {
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 总收益
}
// 修改代理钱包余额请求
AdminUpdateAgentWalletBalanceReq {
AgentId int64 `json:"agent_id"` // 代理ID
Amount float64 `json:"amount"` // 修改金额(正数增加,负数减少)
}
// 修改代理钱包余额响应
AdminUpdateAgentWalletBalanceResp {
Success bool `json:"success"` // 是否成功
Balance float64 `json:"balance"` // 修改后的余额
}
// 更新系统配置请求
AdminUpdateSystemConfigReq {
CommissionSafeMode *bool `json:"commission_safe_mode,optional"` // 佣金安全防御模式true-冻结模式false-直接结算模式
}
// 更新系统配置响应
AdminUpdateSystemConfigResp {
Success bool `json:"success"` // 是否成功
}
// 获取系统配置响应
AdminGetSystemConfigResp {
CommissionSafeMode bool `json:"commission_safe_mode"` // 佣金安全防御模式
}
// 代理钱包流水分页查询请求
AdminGetAgentWalletTransactionListReq {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量
AgentId int64 `form:"agent_id"` // 代理ID
TransactionType *string `form:"transaction_type,optional"` // 交易类型(可选)
CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选)
CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选)
}
// 代理钱包流水列表项
AgentWalletTransactionListItem {
Id int64 `json:"id"` // 主键
AgentId int64 `json:"agent_id"` // 代理ID
TransactionType string `json:"transaction_type"` // 交易类型
Amount float64 `json:"amount"` // 变动金额
BalanceBefore float64 `json:"balance_before"` // 变动前余额
BalanceAfter float64 `json:"balance_after"` // 变动后余额
FrozenBalanceBefore float64 `json:"frozen_balance_before"` // 变动前冻结余额
FrozenBalanceAfter float64 `json:"frozen_balance_after"` // 变动后冻结余额
TransactionId *string `json:"transaction_id"` // 关联交易ID
RelatedUserId *int64 `json:"related_user_id"` // 关联用户ID
Remark *string `json:"remark"` // 备注说明
CreateTime string `json:"create_time"` // 创建时间
}
// 代理钱包流水分页查询响应
AdminGetAgentWalletTransactionListResp {
Total int64 `json:"total"` // 总数
Items []AgentWalletTransactionListItem `json:"items"` // 列表数据
}
) )

View File

@@ -0,0 +1,21 @@
syntax = "v1"
info(
title: "Admin Queue管理"
desc: "管理员队列管理接口"
author: "team"
version: "v1"
)
type (
)
@server (
prefix: api/v1
group: admin_queue
middleware: AdminAuthInterceptor
)
service main {
}

View File

@@ -77,6 +77,9 @@ type (
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始 RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束 RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
SalesCost float64 `form:"sales_cost,optional"` // 成本价 SalesCost float64 `form:"sales_cost,optional"` // 成本价
QueryName string `form:"query_name,optional"` // 被查询人姓名(通过 query_user_record 表追溯订单)
QueryIdCard string `form:"query_id_card,optional"` // 被查询人身份证(通过 query_user_record 表追溯订单)
QueryMobile string `form:"query_mobile,optional"` // 被查询人手机号(通过 query_user_record 表追溯订单)
} }
// 列表响应 // 列表响应
AdminGetOrderListResp { AdminGetOrderListResp {

View File

@@ -61,6 +61,7 @@ type (
Nickname string `json:"nickname"` // 昵称 Nickname string `json:"nickname"` // 昵称
Info string `json:"info"` // 备注信息 Info string `json:"info"` // 备注信息
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否 Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
Disable int64 `json:"disable"` // 封禁状态 0-可用 1-禁用
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间 UpdateTime string `json:"update_time"` // 更新时间
} }
@@ -77,6 +78,7 @@ type (
Nickname string `json:"nickname"` // 昵称 Nickname string `json:"nickname"` // 昵称
Info string `json:"info"` // 备注信息 Info string `json:"info"` // 备注信息
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否 Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
Disable int64 `json:"disable"` // 封禁状态 0-可用 1-禁用
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间 UpdateTime string `json:"update_time"` // 更新时间
} }
@@ -103,6 +105,7 @@ type (
Nickname *string `json:"nickname,optional"` // 昵称 Nickname *string `json:"nickname,optional"` // 昵称
Info *string `json:"info,optional"` // 备注信息 Info *string `json:"info,optional"` // 备注信息
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否 Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
Disable *int64 `json:"disable,optional"` // 封禁状态 0-可用 1-禁用
} }
// 更新响应 // 更新响应

View File

@@ -328,7 +328,10 @@ type (
Commission { Commission {
OrderId string `json:"order_id"` // 订单号 OrderId string `json:"order_id"` // 订单号
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
Amount float64 `json:"amount"` Amount float64 `json:"amount"` // 原始佣金金额
RefundedAmount float64 `json:"refunded_amount"` // 已退款佣金金额
NetAmount float64 `json:"net_amount"` // 剩余净佣金金额 = amount - refunded_amount
Status int64 `json:"status"` // 状态0-已结算1-冻结中2-已退款
CreateTime string `json:"create_time"` CreateTime string `json:"create_time"`
QueryParams map[string]interface{} `json:"query_params,omitempty"` QueryParams map[string]interface{} `json:"query_params,omitempty"`
} }

View File

@@ -47,7 +47,7 @@ type (
PaymentReq { PaymentReq {
Id string `json:"id"` Id string `json:"id"`
PayMethod string `json:"pay_method"` PayMethod string `json:"pay_method"`
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"` PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
} }
PaymentResp { PaymentResp {
PrepayData interface{} `json:"prepay_data"` PrepayData interface{} `json:"prepay_data"`

View File

@@ -142,7 +142,8 @@ 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"` CaptchaVerifyParam string `json:"captchaVerifyParam"`
ActionType string `json:"actionType,optional" validate:"oneof=login register query agentApply realName bindMobile"`
} }
) )

View File

@@ -29,3 +29,4 @@ import "./admin/admin_query.api"
import "./admin/admin_agent.api" import "./admin/admin_agent.api"
import "./admin/admin_api.api" import "./admin/admin_api.api"
import "./admin/admin_role_api.api" import "./admin/admin_role_api.api"
import "./admin/admin_queue.api"

View File

@@ -17,6 +17,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:
@@ -41,13 +47,13 @@ Alipay:
Wxpay: Wxpay:
AppID: "wxa581992dc74d860e" AppID: "wxa581992dc74d860e"
MchID: "1682635136" MchID: "1687993434"
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61" MchCertificateSerialNumber: "241E4BCF5B69AAAC48451DB2C7ED794EF8B3A3D3"
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f" MchApiv3Key: "aB3cD5eF7gH9iJ1kL2mN4oP6qR8sT0uV"
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem" MchPrivateKeyPath: "etc/merchant/wxpay/1687993434_20260210_cert/apiclient_key.pem"
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601" MchPublicKeyID: "PUB_KEY_ID_0116879934342025120200181745004208"
MchPublicKeyPath: "etc/merchant/pub_key.pem" MchPublicKeyPath: "etc/merchant/wxpay/1687993434_20260210_cert/pub_key.pem"
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781" MchPlatformRAS: "5630D013C88EA348BF66E642B6C39AA0180D4B15"
NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback" NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback"
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback" RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback"
Applepay: Applepay:
@@ -62,6 +68,7 @@ Ali:
Code: "d55b58829efb41c8aa8e86769cba4844" Code: "d55b58829efb41c8aa8e86769cba4844"
SystemConfig: SystemConfig:
ThreeVerify: false ThreeVerify: false
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
WechatH5: WechatH5:
AppID: "wxa581992dc74d860e" AppID: "wxa581992dc74d860e"
AppSecret: "4de1fbf521712247542d49907fcd5dbf" AppSecret: "4de1fbf521712247542d49907fcd5dbf"
@@ -88,3 +95,4 @@ Tianyuanapi:
Timeout: 60 Timeout: 60
Authorization: Authorization:
FileBaseURL: "https://www.tianyuandb.com/api/v1/auth-docs" # 授权书文件访问基础URL FileBaseURL: "https://www.tianyuandb.com/api/v1/auth-docs" # 授权书文件访问基础URL
ExtensionTime: 24 # 佣金解冻延迟时间单位24小时

View File

@@ -19,6 +19,14 @@ 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:
@@ -42,13 +50,13 @@ Alipay:
ReturnURL: "https://www.tianyuandb.com/payment/result" ReturnURL: "https://www.tianyuandb.com/payment/result"
Wxpay: Wxpay:
AppID: "wxa581992dc74d860e" AppID: "wxa581992dc74d860e"
MchID: "1682635136" MchID: "1687993434"
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61" MchCertificateSerialNumber: "241E4BCF5B69AAAC48451DB2C7ED794EF8B3A3D3"
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f" MchApiv3Key: "aB3cD5eF7gH9iJ1kL2mN4oP6qR8sT0uV"
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem" MchPrivateKeyPath: "etc/merchant/wxpay/1687993434_20260210_cert/apiclient_key.pem"
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601" MchPublicKeyID: "PUB_KEY_ID_0116879934342025120200181745004208"
MchPublicKeyPath: "etc/merchant/pub_key.pem" MchPublicKeyPath: "etc/merchant/wxpay/1687993434_20260210_cert/pub_key.pem"
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781" MchPlatformRAS: "5630D013C88EA348BF66E642B6C39AA0180D4B15"
NotifyUrl: "https://www.tianyuandb.com/api/v1/pay/wechat/callback" NotifyUrl: "https://www.tianyuandb.com/api/v1/pay/wechat/callback"
RefundNotifyUrl: "https://www.tianyuandb.com/api/v1/wechat/refund_callback" RefundNotifyUrl: "https://www.tianyuandb.com/api/v1/wechat/refund_callback"
Applepay: Applepay:
@@ -63,6 +71,7 @@ Ali:
Code: "d55b58829efb41c8aa8e86769cba4844" Code: "d55b58829efb41c8aa8e86769cba4844"
SystemConfig: SystemConfig:
ThreeVerify: true ThreeVerify: true
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
WechatH5: WechatH5:
AppID: "wxa581992dc74d860e" AppID: "wxa581992dc74d860e"
AppSecret: "4de1fbf521712247542d49907fcd5dbf" AppSecret: "4de1fbf521712247542d49907fcd5dbf"
@@ -89,3 +98,4 @@ Tianyuanapi:
Timeout: 60 Timeout: 60
Authorization: Authorization:
FileBaseURL: "https://www.tianyuandb.com/api/v1/auth-docs" # 授权书文件访问基础URL FileBaseURL: "https://www.tianyuandb.com/api/v1/auth-docs" # 授权书文件访问基础URL
ExtensionTime: 24 # 佣金解冻延迟时间单位24小时

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAh8YNl16EkVKW
IHDiPyx5Zz93osD4n2E7oJXPEOSGpumhsAMjXsRd32JulYDtD/B/phA/mxQiEf84
Um4VKC16pNAEtpEyrO7ZZRhrPk2AMck6Jm81nXLoppttuS0B3VkOE/1UuvAbIz1y
VliRLDTiIbuSM8p1rMfpsJGo4CoLaerJgzXL6MHZA0+Fhn4PQLkLIt57jC0Jh2Dg
ayH7Ru/wgwgq6upXb7rXj0ZzMer5kkA446mLis9P6Nz1GiTUMyUy+tnORK1EpWLg
tUifsfXsKBLlUcOrNyG1+TyeMOuY2p592Au8J2eZbwyXKRDvK2oJzsDQW04pGyJc
oW1vObmLAgMBAAECggEAPc0XgRNezrULSo99TNK0hv/iepeu09/tSUOh8wbcJHD9
u94RE9B+vhdPtGmfKfmc3IzE2HYCP3GBeGXVWks8VgsDjw+/igHC5duyu/IS1Jym
mFjwB8jTsuSQLedsEBYqWP+HqSQcoMluFv6qjWcgTpo/aI3hZmahAV2hVBEozeKR
Va+EssjI46k894Kr6s9rb9nk8hCORuLuqDXfWJdxT+UixMeYftrgmHXk6CCUb2Ct
EjMuxi66KyfVu9w5wS0DuE583mDIgTKmD+deJWxcVyJJMJDCULY4fotWhQb2ro9L
qndaCgBC+sOAB/PrO31E40hZhjgdToSq5SvUWgjUCQKBgQD6/zSzEGJYzjS3544l
PWF92WT3aFJxT3+Mgd/BrTWaY2LykbDoioM/Kp+5D1bB446k534xO6uwr3LuDCOE
jZGy/6+HQeDHSLfDZ+LgWQdEbakbniR57HXG293x3Mp5jTlZOXc8ryGURXaCP8Sy
xwIiZPUgpo4xA0Myt/CnjW9OhwKBgQDEXjkc4tyVTsgcVevxBjT4o3g2ihYouCdt
ClDr6iZ8Mi5A0vCcuy1A3uI5BZnax11AIwqapgjOdWgWEtyjQJ84bhesyI7km/Ya
AeaelsgSf+mAfFgTarWb+KpD5l0jxJAlX/1PAQU6vXuUPdA4PtBbKyUKHLY0kMXr
wE4vbPpZ3QKBgQDGvwpFt/WICFAqR0qBJmdqNZgDaDHP03lWEwjQ3YySYZxaIw8I
M5XVkLTE3uZ9xOiQn1WHOo6q62KAKFB/h3IVYOzmlz2bz3LjYgF+UEC26HZ9je2o
NZrVCghmmcQiF7ePdTd7b9mRBKfgXwor3fVMstB/OCNjoAe3w3rl0dKPRQKBgQC2
oIbvdZH/DqkPqV1o6QPk5muMFbrsHfEU+U4vSrKGOUlJIqWCrpY0ydWNgMcJcPcq
Ciz3jUmNciXeuaYX8qbiHYnJOTGkLeShZXktrz/d7Lamt35WeJz0tTztL1caR9pj
2DVG/8T0T3uacC6x0MGIuMSW9gMDOk3Ipy5P70OaxQKBgQDFzgdJxU9Ra6AzbyBL
hcaKb+moNEo+peKCImwY1n3FeRM6XZ59qQaGV1lYu5KPUj9T/SVaJm6DCYFKqM0r
T1prq2LeR69nB7Dpsr2TKp57L86DoCqbxOBnWxZ/6Em65hoRYe7CAtn3yFQGKm9T
+EdUfn1gf7AWjQAgo3bis3TaMQ==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4el5skKyhhV+lFP/lx2x
MIQ14WFzoywBmy7Jd/UnKp5i8g85rwsFKvkMD9QqdQgoUPdnKKpvKiJeQqwUeXUC
ogVZxedg+wCj4FuOmctHTJVwWaqZ07uom78nvDJhgRCsgWR7UBRq8v9MymbPC4p7
IkGuq+lLkYPyJFMGpk33fAua4NkKxBseyLHbB9t3vSlUFh8x2JlIYxC531362qbS
+L2m0B2stMyQEdYxpYCtS3nsEG+ib2Du3GiT+5pAmXxZ6DGyr4jAlAWDnljwJZEf
xJPECXSlcAsHdI6ugkC+9DwPjLs1mrEQ/BevmTT2o0wPigCRNi9xZf178L1sptYy
DwIDAQAB
-----END PUBLIC KEY-----

View File

@@ -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
@@ -25,6 +26,7 @@ type Config struct {
AdminConfig AdminConfig AdminConfig AdminConfig
AdminPromotion AdminPromotion AdminPromotion AdminPromotion
TaxConfig TaxConfig TaxConfig TaxConfig
ExtensionTime int64
} }
// JwtAuth 用于 JWT 鉴权配置 // JwtAuth 用于 JWT 鉴权配置
@@ -41,6 +43,14 @@ type VerifyCode struct {
TemplateCode string TemplateCode string
ValidTime int ValidTime int
} }
type CaptchaConfig struct {
AccessKeyID string
AccessKeySecret string
EndpointURL string
SceneID string
EKey string
}
type Encrypt struct { type Encrypt struct {
SecretKey string SecretKey string
} }
@@ -91,7 +101,8 @@ type YushanConfig struct {
Url string Url string
} }
type SystemConfig struct { type SystemConfig struct {
ThreeVerify bool ThreeVerify bool // 是否开启三级实名认证
CommissionSafeMode bool // 佣金安全防御模式true-冻结模式(status=1,进入frozen_balance)false-直接结算(status=0,进入balance)
} }
type WechatH5Config struct { type WechatH5Config struct {
AppID string AppID string

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminBatchUnfreezeAgentCommissionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminBatchUnfreezeAgentCommissionReq
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 := admin_agent.NewAdminBatchUnfreezeAgentCommissionLogic(r.Context(), svcCtx)
resp, err := l.AdminBatchUnfreezeAgentCommission(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminGetAgentWalletHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetAgentWalletReq
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 := admin_agent.NewAdminGetAgentWalletLogic(r.Context(), svcCtx)
resp, err := l.AdminGetAgentWallet(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,29 @@
package admin_agent
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
)
func AdminGetAgentWalletTransactionListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetAgentWalletTransactionListReq
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 := admin_agent.NewAdminGetAgentWalletTransactionListLogic(r.Context(), svcCtx)
resp, err := l.AdminGetAgentWalletTransactionList(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,17 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/common/result"
)
func AdminGetSystemConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := admin_agent.NewAdminGetSystemConfigLogic(r.Context(), svcCtx)
resp, err := l.AdminGetSystemConfig()
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminUpdateAgentCommissionStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUpdateAgentCommissionStatusReq
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 := admin_agent.NewAdminUpdateAgentCommissionStatusLogic(r.Context(), svcCtx)
resp, err := l.AdminUpdateAgentCommissionStatus(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminUpdateAgentWalletBalanceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUpdateAgentWalletBalanceReq
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 := admin_agent.NewAdminUpdateAgentWalletBalanceLogic(r.Context(), svcCtx)
resp, err := l.AdminUpdateAgentWalletBalance(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_agent"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/common/result"
"tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx"
)
func AdminUpdateSystemConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUpdateSystemConfigReq
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 := admin_agent.NewAdminUpdateSystemConfigLogic(r.Context(), svcCtx)
resp, err := l.AdminUpdateSystemConfig(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

@@ -1,12 +1,14 @@
package auth package auth
import ( import (
"context"
"net/http" "net/http"
"tydata-server/app/main/api/internal/logic/auth" "tydata-server/app/main/api/internal/logic/auth"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/common/result" "tydata-server/common/result"
"tydata-server/pkg/captcha"
"tydata-server/pkg/lzkit/validator" "tydata-server/pkg/lzkit/validator"
"github.com/zeromicro/go-zero/rest/httpx" "github.com/zeromicro/go-zero/rest/httpx"
@@ -23,7 +25,9 @@ func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
result.ParamValidateErrorResult(r, w, err) result.ParamValidateErrorResult(r, w, err)
return return
} }
l := auth.NewSendSmsLogic(r.Context(), svcCtx) // 将 request 注入 context供 captcha 包判断微信等环境时跳过图形验证
ctx := context.WithValue(r.Context(), captcha.HTTPRequestContextKey, r)
l := auth.NewSendSmsLogic(ctx, svcCtx)
err := l.SendSms(&req) err := l.SendSms(&req)
result.HttpResult(r, w, nil, err) result.HttpResult(r, w, nil, err)
} }

View File

@@ -42,11 +42,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/agent-commission-deduction/list", Path: "/agent-commission-deduction/list",
Handler: admin_agent.AdminGetAgentCommissionDeductionListHandler(serverCtx), Handler: admin_agent.AdminGetAgentCommissionDeductionListHandler(serverCtx),
}, },
{
Method: http.MethodPost,
Path: "/agent-commission/batch-unfreeze",
Handler: admin_agent.AdminBatchUnfreezeAgentCommissionHandler(serverCtx),
},
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/agent-commission/list", Path: "/agent-commission/list",
Handler: admin_agent.AdminGetAgentCommissionListHandler(serverCtx), Handler: admin_agent.AdminGetAgentCommissionListHandler(serverCtx),
}, },
{
Method: http.MethodPost,
Path: "/agent-commission/update-status",
Handler: admin_agent.AdminUpdateAgentCommissionStatusHandler(serverCtx),
},
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/agent-link/list", Path: "/agent-link/list",
@@ -122,6 +132,31 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/statistics", Path: "/statistics",
Handler: admin_agent.AdminGetAgentStatisticsHandler(serverCtx), Handler: admin_agent.AdminGetAgentStatisticsHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/system-config",
Handler: admin_agent.AdminGetSystemConfigHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/system-config",
Handler: admin_agent.AdminUpdateSystemConfigHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/wallet-transaction/list",
Handler: admin_agent.AdminGetAgentWalletTransactionListHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/wallet/:agent_id",
Handler: admin_agent.AdminGetAgentWalletHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/wallet/update-balance",
Handler: admin_agent.AdminUpdateAgentWalletBalanceHandler(serverCtx),
},
}..., }...,
), ),
rest.WithPrefix("/api/v1/admin/agent"), rest.WithPrefix("/api/v1/admin/agent"),
@@ -568,6 +603,14 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
rest.WithPrefix("/api/v1/admin/query"), rest.WithPrefix("/api/v1/admin/query"),
) )
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AdminAuthInterceptor},
[]rest.Route{}...,
),
rest.WithPrefix("/api/v1"),
)
server.AddRoutes( server.AddRoutes(
rest.WithMiddlewares( rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AdminAuthInterceptor}, []rest.Middleware{serverCtx.AdminAuthInterceptor},

View File

@@ -0,0 +1,149 @@
package admin_agent
import (
"context"
"errors"
"fmt"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model"
"tydata-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminBatchUnfreezeAgentCommissionLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminBatchUnfreezeAgentCommissionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminBatchUnfreezeAgentCommissionLogic {
return &AdminBatchUnfreezeAgentCommissionLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminBatchUnfreezeAgentCommissionLogic) AdminBatchUnfreezeAgentCommission(req *types.AdminBatchUnfreezeAgentCommissionReq) (resp *types.AdminBatchUnfreezeAgentCommissionResp, err error) {
// 构建查询条件状态为1冻结中
builder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.Eq{"status": 1})
// 如果指定了代理商ID则只查询该代理商的冻结佣金
if req.AgentId != nil && *req.AgentId > 0 {
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
}
// 查询所有冻结中的佣金记录
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, err
}
// 如果没有冻结的佣金,直接返回
if len(commissions) == 0 {
resp = &types.AdminBatchUnfreezeAgentCommissionResp{
Success: true,
Count: 0,
Amount: 0,
}
return
}
// 计算总金额
var totalAmount float64
for _, commission := range commissions {
totalAmount += commission.Amount
}
// 开始事务
err = l.svcCtx.AgentCommissionModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 按代理商分组更新钱包余额
agentWalletMap := make(map[int64]*model.AgentWallet)
// 遍历所有冻结的佣金,更新状态
for _, commission := range commissions {
// 更新佣金状态为已结算
commission.Status = 0
err := l.svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
if err != nil {
// 如果是版本冲突错误,重新查询最新的数据后重试
if errors.Is(err, model.ErrNoRowsUpdate) {
latestCommission, findErr := l.svcCtx.AgentCommissionModel.FindOne(ctx, commission.Id)
if findErr != nil {
return findErr
}
// 检查状态是否已被其他操作修改
if latestCommission.Status != 1 {
return xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, fmt.Sprintf("佣金 %d 的状态已被其他操作修改,当前状态: %d", commission.Id, latestCommission.Status))
}
// 重新更新状态
latestCommission.Status = 0
updateErr := l.svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, latestCommission)
if updateErr != nil {
return updateErr
}
// 更新引用,使用最新的数据
commission.Version = latestCommission.Version
} else {
return err
}
}
// 累加到对应代理商的钱包数据
if wallet, exists := agentWalletMap[commission.AgentId]; exists {
wallet.Balance += commission.Amount
wallet.FrozenBalance -= commission.Amount
} else {
// 查询该代理商的钱包
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId)
if err != nil {
return err
}
wallet.Balance += commission.Amount
wallet.FrozenBalance -= commission.Amount
agentWalletMap[commission.AgentId] = wallet
}
}
// 更新所有受影响代理商的钱包
for _, wallet := range agentWalletMap {
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
if err != nil {
// 如果是版本冲突错误,重新查询最新的数据后重试
if errors.Is(err, model.ErrNoRowsUpdate) {
latestWallet, findErr := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, wallet.AgentId)
if findErr != nil {
return findErr
}
// 重新累加金额
latestWallet.Balance = wallet.Balance
latestWallet.FrozenBalance = wallet.FrozenBalance
updateErr := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, latestWallet)
if updateErr != nil {
return updateErr
}
} else {
return err
}
}
}
return nil
})
if err != nil {
return nil, xerr.NewErrMsg("批量解冻失败: " + err.Error())
}
resp = &types.AdminBatchUnfreezeAgentCommissionResp{
Success: true,
Count: int64(len(commissions)),
Amount: totalAmount,
}
return
}

View File

@@ -2,6 +2,7 @@ package admin_agent
import ( import (
"context" "context"
"time"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
@@ -30,19 +31,39 @@ func (l *AdminGetAgentCommissionListLogic) AdminGetAgentCommissionList(req *type
if req.AgentId != nil { if req.AgentId != nil {
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId}) builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
} }
if req.OrderId != nil {
builder = builder.Where(squirrel.Eq{"order_id": *req.OrderId})
}
if req.Status != nil { if req.Status != nil {
builder = builder.Where(squirrel.Eq{"status": *req.Status}) builder = builder.Where(squirrel.Eq{"status": *req.Status})
} }
// 时间范围筛选
if req.CreateTimeStart != nil && *req.CreateTimeStart != "" {
startTime, err := time.Parse("2006-01-02 15:04:05", *req.CreateTimeStart)
if err == nil {
builder = builder.Where(squirrel.GtOrEq{"create_time": startTime})
}
}
if req.CreateTimeEnd != nil && *req.CreateTimeEnd != "" {
endTime, err := time.Parse("2006-01-02 15:04:05", *req.CreateTimeEnd)
if err == nil {
builder = builder.Where(squirrel.LtOrEq{"create_time": endTime})
}
}
// 先查出所有product_id对应的product_name如有product_name筛选需反查id // 先查出所有product_id对应的product_name如有product_name筛选需反查id
var productIdFilter int64
if req.ProductName != nil && *req.ProductName != "" { if req.ProductName != nil && *req.ProductName != "" {
// 支持精确匹配,如需模糊可扩展 // 支持模糊匹配产品名称
products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"product_name": *req.ProductName}), "") products, err := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Like{"product_name": "%" + *req.ProductName + "%"}), "")
if err != nil || len(products) == 0 { if err != nil || len(products) == 0 {
return &types.AdminGetAgentCommissionListResp{Total: 0, Items: []types.AgentCommissionListItem{}}, nil return &types.AdminGetAgentCommissionListResp{Total: 0, Items: []types.AgentCommissionListItem{}}, nil
} }
productIdFilter = products[0].Id productIds := make([]int64, 0, len(products))
builder = builder.Where("product_id = ?", productIdFilter) for _, p := range products {
productIds = append(productIds, p.Id)
}
builder = builder.Where(squirrel.Eq{"product_id": productIds})
} }
list, total, err := l.svcCtx.AgentCommissionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC") list, total, err := l.svcCtx.AgentCommissionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")

View File

@@ -0,0 +1,45 @@
package admin_agent
import (
"context"
"errors"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model"
"tydata-server/common/xerr"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentWalletLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentWalletLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentWalletLogic {
return &AdminGetAgentWalletLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentWalletLogic) AdminGetAgentWallet(req *types.AdminGetAgentWalletReq) (resp *types.AdminGetAgentWalletResp, err error) {
// 查询代理钱包信息
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, req.AgentId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, xerr.NewErrMsg("代理钱包不存在")
}
return nil, err
}
resp = &types.AdminGetAgentWalletResp{
Balance: wallet.Balance,
FrozenBalance: wallet.FrozenBalance,
TotalEarnings: wallet.TotalEarnings,
}
return
}

View File

@@ -0,0 +1,84 @@
package admin_agent
import (
"context"
"errors"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentWalletTransactionListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentWalletTransactionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentWalletTransactionListLogic {
return &AdminGetAgentWalletTransactionListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentWalletTransactionListLogic) AdminGetAgentWalletTransactionList(req *types.AdminGetAgentWalletTransactionListReq) (resp *types.AdminGetAgentWalletTransactionListResp, err error) {
builder := l.svcCtx.AgentWalletTransactionModel.SelectBuilder()
// 必须传入代理ID
if req.AgentId == 0 {
return nil, errors.New("代理ID不能为空")
}
builder = builder.Where(squirrel.Eq{"agent_id": req.AgentId})
// 可选条件
if req.TransactionType != nil && *req.TransactionType != "" {
builder = builder.Where(squirrel.Eq{"transaction_type": *req.TransactionType})
}
if req.CreateTimeStart != nil && *req.CreateTimeStart != "" {
builder = builder.Where(squirrel.GtOrEq{"create_time": *req.CreateTimeStart})
}
if req.CreateTimeEnd != nil && *req.CreateTimeEnd != "" {
builder = builder.Where(squirrel.LtOrEq{"create_time": *req.CreateTimeEnd})
}
list, total, err := l.svcCtx.AgentWalletTransactionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, err
}
items := make([]types.AgentWalletTransactionListItem, 0, len(list))
for _, v := range list {
item := types.AgentWalletTransactionListItem{
Id: v.Id,
AgentId: v.AgentId,
TransactionType: v.TransactionType,
Amount: v.Amount,
BalanceBefore: v.BalanceBefore,
BalanceAfter: v.BalanceAfter,
FrozenBalanceBefore: v.FrozenBalanceBefore,
FrozenBalanceAfter: v.FrozenBalanceAfter,
CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"),
}
if v.TransactionId.Valid {
item.TransactionId = &v.TransactionId.String
}
if v.RelatedUserId.Valid {
item.RelatedUserId = &v.RelatedUserId.Int64
}
if v.Remark.Valid {
item.Remark = &v.Remark.String
}
items = append(items, item)
}
resp = &types.AdminGetAgentWalletTransactionListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -36,6 +36,9 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
if req.WithdrawNo != nil && *req.WithdrawNo != "" { if req.WithdrawNo != nil && *req.WithdrawNo != "" {
builder = builder.Where(squirrel.Eq{"withdraw_no": *req.WithdrawNo}) builder = builder.Where(squirrel.Eq{"withdraw_no": *req.WithdrawNo})
} }
if req.WithdrawType != nil {
builder = builder.Where(squirrel.Eq{"withdraw_type": *req.WithdrawType})
}
list, total, err := l.svcCtx.AgentWithdrawalModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC") list, total, err := l.svcCtx.AgentWithdrawalModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -0,0 +1,31 @@
package admin_agent
import (
"context"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetSystemConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetSystemConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetSystemConfigLogic {
return &AdminGetSystemConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetSystemConfigLogic) AdminGetSystemConfig() (resp *types.AdminGetSystemConfigResp, err error) {
resp = &types.AdminGetSystemConfigResp{
CommissionSafeMode: l.svcCtx.Config.SystemConfig.CommissionSafeMode,
}
return
}

View File

@@ -3,6 +3,7 @@ package admin_agent
import ( import (
"context" "context"
"database/sql" "database/sql"
"fmt"
"time" "time"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/xerr" "tydata-server/common/xerr"
@@ -80,9 +81,9 @@ func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *
return errors.Wrapf(xerr.NewErrMsg("该提现记录已处理,无法重复操作"), "状态验证失败") return errors.Wrapf(xerr.NewErrMsg("该提现记录已处理,无法重复操作"), "状态验证失败")
} }
// 验证提现类型 // 验证提现类型(支持银行卡和支付宝提现)
if record.WithdrawType != WithdrawTypeBankCard { if record.WithdrawType != WithdrawTypeBankCard && record.WithdrawType != WithdrawTypeAlipay {
return errors.Wrapf(xerr.NewErrMsg("该记录不是银行卡提现,无法审核"), "提现类型验证失败") return errors.Wrapf(xerr.NewErrMsg("提现类型不正确"), "提现类型验证失败")
} }
if req.Action == ReviewActionApprove { if req.Action == ReviewActionApprove {
@@ -104,6 +105,49 @@ func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *
// 确认提现 // 确认提现
func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error { func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
// 根据提现类型执行不同的操作
if record.WithdrawType == WithdrawTypeAlipay {
// 支付宝提现:先调用支付宝转账接口
return l.approveAlipayWithdrawal(ctx, session, record)
} else {
// 银行卡提现:直接更新状态为成功(线下转账)
return l.approveBankCardWithdrawal(ctx, session, record)
}
}
// 确认支付宝提现
func (l *AdminReviewBankCardWithdrawalLogic) approveAlipayWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
// 同步调用支付宝转账
transferResp, err := l.svcCtx.AlipayService.AliTransfer(ctx, record.PayeeAccount, record.PayeeName.String, record.ActualAmount, "公司提现", record.WithdrawNo)
if err != nil {
l.Logger.Errorf("【支付宝转账失败】withdrawNo:%s error:%v", record.WithdrawNo, err)
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
}
switch {
case transferResp.Status == "SUCCESS":
// 立即处理成功状态
return l.completeWithdrawalSuccess(ctx, session, record)
case transferResp.Status == "FAIL" || transferResp.SubCode != "":
// 处理明确失败
errorMsg := l.mapAlipayError(transferResp.SubCode)
return l.completeWithdrawalFailure(ctx, session, record, errorMsg)
case transferResp.Status == "DEALING":
// 处理中状态,更新为处理中但不标记为最终状态
record.Remark = sql.NullString{String: "支付宝转账处理中", Valid: true}
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
}
l.Logger.Infof("支付宝提现审核通过,转账处理中 withdrawalId:%d withdrawNo:%s", record.Id, record.WithdrawNo)
return nil
default:
// 未知状态按失败处理
return l.completeWithdrawalFailure(ctx, session, record, "支付宝返回未知状态")
}
}
// 确认银行卡提现
func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
// 更新提现记录状态为成功 // 更新提现记录状态为成功
record.Status = StatusSuccess record.Status = StatusSuccess
record.Remark = sql.NullString{String: "管理员确认提现", Valid: true} record.Remark = sql.NullString{String: "管理员确认提现", Valid: true}
@@ -117,11 +161,34 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Conte
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
} }
// 记录变动前的冻结余额
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包(减少冻结余额)
wallet.FrozenBalance -= record.Amount wallet.FrozenBalance -= record.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
} }
// 记录交易流水(提现成功)
err = l.svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeWithdraw,
-record.Amount, // 变动金额(负数表示减少)
wallet.Balance, // 变动前余额(不变)
wallet.Balance, // 变动后余额(不变)
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
record.WithdrawNo, // 关联交易ID
0, // 关联用户ID
"提现审核通过", // 备注
)
if err != nil {
return err
}
// 更新扣税记录状态为成功 // 更新扣税记录状态为成功
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
if err != nil { if err != nil {
@@ -163,12 +230,36 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
} }
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包(余额增加,冻结余额减少)
wallet.Balance += record.Amount wallet.Balance += record.Amount
wallet.FrozenBalance -= record.Amount wallet.FrozenBalance -= record.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
} }
// 记录交易流水(解冻)
err = l.svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeUnfreeze,
record.Amount, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
wallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
record.WithdrawNo, // 关联交易ID
0, // 关联用户ID
"提现拒绝,解冻资金", // 备注
)
if err != nil {
return err
}
// 更新扣税记录状态为失败 // 更新扣税记录状态为失败
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id) taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
if err != nil { if err != nil {
@@ -185,3 +276,173 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex
l.Logger.Infof("银行卡提现拒绝 withdrawalId:%d amount:%f reason:%s", record.Id, record.Amount, remark) l.Logger.Infof("银行卡提现拒绝 withdrawalId:%d amount:%f reason:%s", record.Id, record.Amount, remark)
return nil return nil
} }
// 完成提现成功(支付宝转账成功后调用)
func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
// 更新提现记录状态为成功
record.Status = StatusSuccess
record.Remark = sql.NullString{String: "支付宝转账成功", Valid: true}
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
}
// 解冻资金并扣除FrozenBalance -= amount, Balance不变
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
}
// 记录变动前的冻结余额
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包(减少冻结余额)
wallet.FrozenBalance -= record.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
}
// 记录交易流水(提现成功)
err = l.svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeWithdraw,
-record.Amount, // 变动金额(负数表示减少)
wallet.Balance, // 变动前余额(不变)
wallet.Balance, // 变动后余额(不变)
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
record.WithdrawNo, // 关联交易ID
0, // 关联用户ID
"提现成功", // 备注
)
if err != nil {
return err
}
// 更新扣税记录状态为成功
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
}
if taxModel.TaxStatus == model.TaxStatusPending {
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
}
}
// 提现成功后,给上级代理发放提现奖励
withdrawRewardErr := l.svcCtx.AgentService.GiveWithdrawReward(ctx, record.AgentId, record.Amount, session)
if withdrawRewardErr != nil {
l.Logger.Errorf("发放提现奖励失败代理ID%d提现金额%f错误%+v", record.AgentId, record.Amount, withdrawRewardErr)
// 提现奖励失败不影响主流程,只记录日志
} else {
l.Logger.Infof("发放提现奖励成功代理ID%d提现金额%f", record.AgentId, record.Amount)
}
l.Logger.Infof("支付宝提现成功 withdrawalId:%d withdrawNo:%s", record.Id, record.WithdrawNo)
return nil
}
// 完成提现失败(支付宝转账失败后调用)
func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, errorMsg string) error {
// 更新提现记录状态为失败
record.Status = StatusFailed
record.Remark = sql.NullString{String: errorMsg, Valid: true}
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录失败: %v", err)
}
// 解冻资金FrozenBalance -= amount, Balance += amount
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败: %v", err)
}
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包(余额增加,冻结余额减少)
wallet.Balance += record.Amount
wallet.FrozenBalance -= record.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新钱包失败: %v", err)
}
// 记录交易流水(解冻)
err = l.svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeUnfreeze,
record.Amount, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
wallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
record.WithdrawNo, // 关联交易ID
0, // 关联用户ID
"提现失败,解冻资金", // 备注
)
if err != nil {
return err
}
// 更新扣税记录状态为失败
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
}
if taxModel.TaxStatus == model.TaxStatusPending {
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
}
}
l.Logger.Infof("支付宝提现失败 withdrawalId:%d withdrawNo:%s reason:%s", record.Id, record.WithdrawNo, errorMsg)
return nil
}
// 错误类型映射
func (l *AdminReviewBankCardWithdrawalLogic) mapAlipayError(code string) string {
errorMapping := map[string]string{
// 账户存在性错误
"PAYEE_ACCOUNT_NOT_EXSIT": "收款账户不存在,请检查账号是否正确",
"PAYEE_NOT_EXIST": "收款账户不存在或姓名有误,请核实信息",
"PAYEE_ACC_OCUPIED": "收款账号存在多个账户,无法确认唯一性",
"PAYEE_MID_CANNOT_SAME": "收款方和中间方不能是同一个人,请修改收款方或者中间方信息",
// 实名认证问题
"PAYEE_CERTIFY_LEVEL_LIMIT": "收款方未完成实名认证",
"PAYEE_NOT_RELNAME_CERTIFY": "收款方未完成实名认证",
"PAYEE_CERT_INFO_ERROR": "收款方证件信息不匹配",
// 账户状态异常
"PAYEE_ACCOUNT_STATUS_ERROR": "收款账户状态异常,请更换账号",
"PAYEE_USERINFO_STATUS_ERROR": "收款账户状态异常,无法收款",
"PERMIT_LIMIT_PAYEE": "收款账户异常,请更换账号",
"BLOCK_USER_FORBBIDEN_RECIEVE": "账户冻结无法收款",
"PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT": "收款方托管子户累计收款金额超限",
// 账户信息错误
"PAYEE_USERINFO_ERROR": "收款方姓名或信息不匹配",
"PAYEE_CARD_INFO_ERROR": "收款支付宝账号及户名不一致",
"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
"PAYEE_USER_IS_INST": "收款方为金融机构,不能使用提现功能,请更换收款账号",
"PAYEE_USER_TYPE_ERROR": "该支付宝账号类型不支持提现,请更换收款账号",
// 权限与限制
"PAYEE_RECEIVE_COUNT_EXCEED_LIMIT": "收款次数超限,请明日再试",
"PAYEE_OUT_PERMLIMIT_CHECK_FAILURE": "收款方权限校验不通过",
"PERMIT_NON_BANK_LIMIT_PAYEE": "收款方未完善身份信息,无法收款",
}
if msg, ok := errorMapping[code]; ok {
return msg
}
return fmt.Sprintf("系统错误,请联系客服,错误码: %s", code)
}

View File

@@ -0,0 +1,109 @@
package admin_agent
import (
"context"
"errors"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model"
"tydata-server/common/xerr"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminUpdateAgentCommissionStatusLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentCommissionStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentCommissionStatusLogic {
return &AdminUpdateAgentCommissionStatusLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentCommissionStatusLogic) AdminUpdateAgentCommissionStatus(req *types.AdminUpdateAgentCommissionStatusReq) (resp *types.AdminUpdateAgentCommissionStatusResp, err error) {
// 验证状态值不允许手动设置为2已取消只能设置为0或1
if req.Status != 0 && req.Status != 1 {
return nil, xerr.NewErrMsg("无效的状态值状态必须为0(已结算)或1(冻结中)")
}
commission, err := l.svcCtx.AgentCommissionModel.FindOne(l.ctx, req.Id)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, xerr.NewErrMsg("佣金记录不存在")
}
return nil, err
}
// 检查状态转换是否合法
// 0(已结算) <-> 1(冻结中):允许冻结和解冻相互转换
// 2(已取消):已取消的状态无法转换到其他状态(由订单退款自动触发)
if commission.Status == req.Status {
return nil, xerr.NewErrMsg("状态未发生变化")
}
// 已取消的状态不能再转换
if commission.Status == 2 {
return nil, xerr.NewErrMsg("已取消的佣金状态不能转换")
}
// 开始事务
err = l.svcCtx.AgentCommissionModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 保存原始状态用于判断
originalStatus := commission.Status
// 更新佣金状态
commission.Status = req.Status
err = l.svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
if err != nil {
return err
}
// 查询代理钱包
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId)
if err != nil {
return err
}
// 根据状态转换更新钱包
if originalStatus == 0 && req.Status == 1 {
// 已结算 -> 冻结中:增加冻结金额,减少钱包余额
// 检查钱包余额是否足够
if wallet.Balance < commission.Amount {
return xerr.NewErrMsg("钱包余额不足,无法冻结")
}
wallet.FrozenBalance += commission.Amount
wallet.Balance -= commission.Amount
} else if originalStatus == 1 && req.Status == 0 {
// 冻结中 -> 已结算:减少冻结金额,增加钱包余额
// 检查冻结余额是否足够
if wallet.FrozenBalance < commission.Amount {
return xerr.NewErrMsg("冻结余额不足,无法解冻")
}
wallet.FrozenBalance -= commission.Amount
wallet.Balance += commission.Amount
}
err = l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
resp = &types.AdminUpdateAgentCommissionStatusResp{
Success: true,
}
return
}

View File

@@ -0,0 +1,107 @@
package admin_agent
import (
"context"
"errors"
"fmt"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model"
"tydata-server/common/xerr"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminUpdateAgentWalletBalanceLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentWalletBalanceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentWalletBalanceLogic {
return &AdminUpdateAgentWalletBalanceLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentWalletBalanceLogic) AdminUpdateAgentWalletBalance(req *types.AdminUpdateAgentWalletBalanceReq) (resp *types.AdminUpdateAgentWalletBalanceResp, err error) {
// 参数校验
if req.AgentId <= 0 {
return nil, xerr.NewErrMsg("代理ID无效")
}
if req.Amount == 0 {
return nil, xerr.NewErrMsg("修改金额不能为0")
}
// 查询代理钱包信息
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, req.AgentId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, xerr.NewErrMsg("代理钱包不存在")
}
return nil, err
}
// 计算新余额
newBalance := wallet.Balance + req.Amount
// 校验余额不能为负数
if newBalance < 0 {
return nil, xerr.NewErrMsg(fmt.Sprintf("操作后余额不能为负数,当前余额: %.2f,操作金额: %.2f", wallet.Balance, req.Amount))
}
// 更新余额
updateErr := l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 使用版本号更新
wallet.Balance = newBalance
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
if err != nil {
return err
}
// 创建钱包交易流水记录(手动调整)
remark := fmt.Sprintf("管理员手动调整余额,金额: %.2f", req.Amount)
transErr := l.svcCtx.AgentService.CreateWalletTransaction(
transCtx,
session,
req.AgentId,
model.WalletTransactionTypeAdjust,
req.Amount, // 变动金额(正数表示增加,负数表示减少)
balanceBefore, // 变动前余额
wallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额(保持不变)
"", // 关联交易ID无关联
0, // 关联用户ID无关联
remark, // 备注
)
if transErr != nil {
l.Logger.Errorf("创建代理钱包流水记录失败: %+v", transErr)
return transErr
}
l.Logger.Infof("代理钱包余额变更 - AgentId: %d, 原余额: %.2f, 变更金额: %.2f, 新余额: %.2f",
req.AgentId, balanceBefore, req.Amount, wallet.Balance)
return nil
})
if updateErr != nil {
l.Logger.Errorf("更新代理钱包余额失败: %+v", updateErr)
return nil, xerr.NewErrMsg("更新余额失败")
}
resp = &types.AdminUpdateAgentWalletBalanceResp{
Success: true,
Balance: newBalance,
}
return
}

View File

@@ -0,0 +1,37 @@
package admin_agent
import (
"context"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateSystemConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateSystemConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateSystemConfigLogic {
return &AdminUpdateSystemConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateSystemConfigLogic) AdminUpdateSystemConfig(req *types.AdminUpdateSystemConfigReq) (resp *types.AdminUpdateSystemConfigResp, err error) {
// 更新佣金安全防御模式配置
if req.CommissionSafeMode != nil {
l.svcCtx.Config.SystemConfig.CommissionSafeMode = *req.CommissionSafeMode
logx.Infof("更新系统配置:佣金安全防御模式设置为 %v", *req.CommissionSafeMode)
}
resp = &types.AdminUpdateSystemConfigResp{
Success: true,
}
return
}

View File

@@ -2,6 +2,8 @@ package admin_order
import ( import (
"context" "context"
"encoding/hex"
"strings"
"sync" "sync"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
@@ -9,6 +11,7 @@ import (
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/globalkey" "tydata-server/common/globalkey"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -77,6 +80,19 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
builder = builder.Where("refund_time <= ?", req.RefundTimeEnd) builder = builder.Where("refund_time <= ?", req.RefundTimeEnd)
} }
// 按被查询人query_user_record 表)过滤:姓名、身份证、手机号(库中为密文,需解密后匹配)
if req.QueryName != "" || req.QueryIdCard != "" || req.QueryMobile != "" {
orderIds, filterErr := l.filterOrderIdsByQueryUserRecord(req.QueryName, req.QueryIdCard, req.QueryMobile)
if filterErr != nil {
return nil, filterErr
}
if len(orderIds) == 0 {
builder = builder.Where("1 = 0") // 无匹配时返回空
} else {
builder = builder.Where(squirrel.Eq{"id": orderIds})
}
}
// 并发获取总数和列表 // 并发获取总数和列表
var total int64 var total int64
var orders []*model.Order var orders []*model.Order
@@ -294,3 +310,71 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
return resp, nil return resp, nil
} }
// filterOrderIdsByQueryUserRecord 根据姓名、身份证、手机号(明文)从 query_user_record 解密后匹配,返回符合条件的 order_id 列表
func (l *AdminGetOrderListLogic) filterOrderIdsByQueryUserRecord(queryName, queryIdCard, queryMobile string) ([]int64, error) {
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, keyErr := hex.DecodeString(secretKey)
if keyErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "Encrypt.SecretKey 解析失败: %v", keyErr)
}
qb := l.svcCtx.QueryUserRecordModel.SelectBuilder().Where("order_id > ?", 0)
recs, err := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx, qb, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 query_user_record 失败: %v", err)
}
orderIds := make([]int64, 0, len(recs))
for _, rec := range recs {
match := true
if queryName != "" {
var decName string
if rec.Name != "" {
bs, e := crypto.AesEcbDecrypt(rec.Name, key)
if e != nil {
match = false
} else {
decName = string(bs)
}
}
if match && !strings.Contains(decName, queryName) {
match = false
}
}
if match && queryIdCard != "" {
var decIdCard string
if rec.IdCard != "" {
var e error
decIdCard, e = crypto.DecryptIDCard(rec.IdCard, key)
if e != nil {
match = false
}
}
if match && decIdCard != queryIdCard {
match = false
}
}
if match && queryMobile != "" {
var decMobile string
if rec.Mobile != "" {
var e error
decMobile, e = crypto.DecryptMobile(rec.Mobile, secretKey)
if e != nil {
match = false
}
}
if match && decMobile != queryMobile {
match = false
}
}
if match {
orderIds = append(orderIds, rec.OrderId)
}
}
return orderIds, nil
}

View File

@@ -89,9 +89,47 @@ func (l *AdminGetRevenueStatisticsLogic) AdminGetRevenueStatistics(req *types.Ad
// 总利润 = 总收入 - 所有订单成本 // 总利润 = 总收入 - 所有订单成本
totalProfitAmount := deductionAmount + nonDeductionAmount - orderCostAmount totalProfitAmount := deductionAmount + nonDeductionAmount - orderCostAmount
// 今日利润与总利润计算方式相同(这里简化为直接使用总利润) // 计算今日利润 = 今日代理订单金额 + 今日非代理订单金额 - 今日订单成本总和
// 如需精确计算今日利润,可参考总利润的计算方式,添加时间条件
todayProfitAmount := totalProfitAmount // 1. 查询今日代理订单金额
todayDeductionBuilder := l.svcCtx.AgentPlatformDeductionModel.SelectBuilder().
Where("create_time >= ? AND create_time < ?", startOfDay, endOfDay)
todayDeductionAmount, err := l.svcCtx.AgentPlatformDeductionModel.FindSum(
l.ctx,
todayDeductionBuilder,
"amount")
if err != nil {
logx.Errorf("查询今日代理订单金额失败: %v", err)
return nil, fmt.Errorf("查询今日代理订单金额失败: %w", err)
}
// 2. 查询今日非代理订单金额
todayNonDeductionBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND pay_time >= ? AND pay_time < ? AND id NOT IN (SELECT order_id FROM agent_platform_deduction WHERE order_id IS NOT NULL)",
"paid", startOfDay, endOfDay)
todayNonDeductionAmount, err := l.svcCtx.OrderModel.FindSum(
l.ctx,
todayNonDeductionBuilder,
"amount")
if err != nil {
logx.Errorf("查询今日非代理订单金额失败: %v", err)
return nil, fmt.Errorf("查询今日非代理订单金额失败: %w", err)
}
// 3. 查询今日订单成本总和
todayOrderCostBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND pay_time >= ? AND pay_time < ?", "paid", startOfDay, endOfDay)
todayOrderCostAmount, err := l.svcCtx.OrderModel.FindSum(
l.ctx,
todayOrderCostBuilder,
"sales_cost")
if err != nil {
logx.Errorf("查询今日订单成本总和失败: %v", err)
return nil, fmt.Errorf("查询今日订单成本总和失败: %w", err)
}
// 4. 计算今日利润 = 今日代理订单金额 + 今日非代理订单金额 - 今日订单成本总和
todayProfitAmount := todayDeductionAmount + todayNonDeductionAmount - todayOrderCostAmount
// 构建响应 // 构建响应
resp = &types.AdminGetRevenueStatisticsResp{ resp = &types.AdminGetRevenueStatisticsResp{

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
paylogic "tydata-server/app/main/api/internal/logic/pay"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model" "tydata-server/app/main/model"
@@ -91,6 +92,10 @@ func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *type
return nil, err return nil, err
} }
// 退款成功后,按本次退款金额更新代理佣金状态并扣除钱包金额
// 注意refundAmount 为本次实际退款金额,可以是部分退款
_ = paylogic.HandleCommissionAndWalletDeduction(l.ctx, l.svcCtx, nil, order, req.RefundAmount)
return &types.AdminRefundOrderResp{ return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded, Status: model.OrderStatusRefunded,
RefundNo: refundNo, RefundNo: refundNo,
@@ -115,6 +120,7 @@ func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *type
} }
// 微信退款是异步的创建pending状态的退款记录 // 微信退款是异步的创建pending状态的退款记录
// 注意:代理佣金扣除将在微信退款回调成功后再执行,不在此处提前扣除
refundNo := l.generateRefundNo(order.OrderNo) refundNo := l.generateRefundNo(order.OrderNo)
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", model.OrderStatusRefunding, model.OrderRefundStatusPending) err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", model.OrderStatusRefunding, model.OrderRefundStatusPending)
if err != nil { if err != nil {

View File

@@ -43,6 +43,7 @@ func (l *AdminGetPlatformUserDetailLogic) AdminGetPlatformUserDetail(req *types.
Nickname: "", Nickname: "",
Info: user.Info, Info: user.Info,
Inside: user.Inside, Inside: user.Inside,
Disable: user.Disable,
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"), CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"), UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
} }

View File

@@ -30,8 +30,14 @@ func NewAdminGetPlatformUserListLogic(ctx context.Context, svcCtx *svc.ServiceCo
func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.AdminGetPlatformUserListReq) (resp *types.AdminGetPlatformUserListResp, err error) { func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.AdminGetPlatformUserListReq) (resp *types.AdminGetPlatformUserListResp, err error) {
builder := l.svcCtx.UserModel.SelectBuilder() builder := l.svcCtx.UserModel.SelectBuilder()
secretKey := l.svcCtx.Config.Encrypt.SecretKey
if req.Mobile != "" { if req.Mobile != "" {
builder = builder.Where("mobile = ?", req.Mobile) // 数据库存密文,搜索时把明文手机号加密后再查询
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机号加密失败: %v", err)
}
builder = builder.Where("mobile = ?", encryptedMobile)
} }
if req.Nickname != "" { if req.Nickname != "" {
builder = builder.Where("nickname = ?", req.Nickname) builder = builder.Where("nickname = ?", req.Nickname)
@@ -55,7 +61,6 @@ func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.Admi
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户分页失败: %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户分页失败: %v", err)
} }
var items []types.PlatformUserListItem var items []types.PlatformUserListItem
secretKey := l.svcCtx.Config.Encrypt.SecretKey
for _, user := range users { for _, user := range users {
mobile := user.Mobile mobile := user.Mobile
@@ -72,6 +77,7 @@ func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.Admi
Nickname: "", Nickname: "",
Info: user.Info, Info: user.Info,
Inside: user.Inside, Inside: user.Inside,
Disable: user.Disable,
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"), CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"), UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
} }

View File

@@ -52,6 +52,12 @@ func (l *AdminUpdatePlatformUserLogic) AdminUpdatePlatformUser(req *types.AdminU
} }
user.Inside = *req.Inside user.Inside = *req.Inside
} }
if req.Disable != nil {
if *req.Disable != 1 && *req.Disable != 0 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "封禁状态错误: %d0-可用 1-禁用", *req.Disable)
}
user.Disable = *req.Disable
}
if req.Password != nil { if req.Password != nil {
user.Password = sql.NullString{String: *req.Password, Valid: *req.Password != ""} user.Password = sql.NullString{String: *req.Password, Valid: *req.Password != ""}
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"os"
"time" "time"
"tydata-server/app/main/api/internal/service" "tydata-server/app/main/api/internal/service"
@@ -43,6 +44,8 @@ func (l *AgentRealNameLogic) AgentRealName(req *types.AgentRealNameReq) (resp *t
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理实名, 加密手机号失败: %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理实名, 加密手机号失败: %v", err)
} }
// 开发环境下跳过验证码验证
if os.Getenv("ENV") != "development" {
// 检查手机号是否在一分钟内已发送过验证码 // 检查手机号是否在一分钟内已发送过验证码
redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile) redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey) cacheCode, err := l.svcCtx.Redis.Get(redisKey)
@@ -55,6 +58,7 @@ func (l *AgentRealNameLogic) AgentRealName(req *types.AgentRealNameReq) (resp *t
if cacheCode != req.Code { if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理实名, 验证码不正确: %s", encryptedMobile) return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理实名, 验证码不正确: %s", encryptedMobile)
} }
}
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID) agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息失败, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息失败, %v", err)

View File

@@ -53,6 +53,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
var ( var (
outBizNo string outBizNo string
withdrawRes = &types.WithdrawalResp{} withdrawRes = &types.WithdrawalResp{}
agentID int64
) )
var finalWithdrawAmount float64 // 实际到账金额 var finalWithdrawAmount float64 // 实际到账金额
// 使用事务处理核心操作 // 使用事务处理核心操作
@@ -67,6 +68,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
if err != nil { if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
} }
agentID = agentModel.Id // 保存agentId用于日志
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id) agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
if err != nil { if err != nil {
if errors.Is(err, model.ErrNotFound) { if errors.Is(err, model.ErrNotFound) {
@@ -116,7 +118,7 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费 finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
// 创建提现记录(初始状态为处理中) // 创建提现记录(初始状态为处理中)
withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.Amount, finalWithdrawAmount, taxAmount, outBizNo) withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.PayeeName, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
if err != nil { if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err) return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
} }
@@ -146,34 +148,12 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
return nil, err return nil, err
} }
// 同步调用支付宝转账 // 支付宝提现不再直接调用转账接口,改为先申请后审核
transferResp, err := l.svcCtx.AlipayService.AliTransfer(l.ctx, req.PayeeAccount, req.PayeeName, finalWithdrawAmount, "公司提现", outBizNo) // 直接返回申请中状态,等待管理员审核
if err != nil {
l.Logger.Errorf("【支付宝转账失败】outBizNo:%s error:%v", outBizNo, err)
l.handleTransferError(outBizNo, err, "支付宝接口调用失败")
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
}
switch {
case transferResp.Status == "SUCCESS":
// 立即处理成功状态
l.handleTransferSuccess(outBizNo, transferResp)
withdrawRes.Status = WithdrawStatusSuccess
case transferResp.Status == "FAIL" || transferResp.SubCode != "":
// 处理明确失败
errorMsg := l.mapAlipayError(transferResp.SubCode)
l.handleTransferFailure(outBizNo, transferResp)
withdrawRes.Status = WithdrawStatusFailed
withdrawRes.FailMsg = errorMsg
case transferResp.Status == "DEALING":
// 处理中状态,启动异步轮询
go l.startAsyncPolling(outBizNo)
withdrawRes.Status = WithdrawStatusProcessing withdrawRes.Status = WithdrawStatusProcessing
default: withdrawRes.FailMsg = ""
// 未知状态按失败处理
l.handleTransferError(outBizNo, fmt.Errorf("未知状态:%s", transferResp.Status), "支付宝返回未知状态") l.Logger.Infof("支付宝提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
}
return withdrawRes, nil return withdrawRes, nil
} }
@@ -213,16 +193,17 @@ func (l *AgentWithdrawalLogic) mapAlipayError(code string) string {
if msg, ok := errorMapping[code]; ok { if msg, ok := errorMapping[code]; ok {
return msg return msg
} }
return "系统错误,请联系客服" return fmt.Sprintf("系统错误,请联系客服,错误码: %s", code)
} }
// 创建提现记录(事务内操作) // 创建提现记录(事务内操作)
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) { func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, payeeName string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
record := &model.AgentWithdrawal{ record := &model.AgentWithdrawal{
AgentId: agentID, AgentId: agentID,
WithdrawType: 1, // 支付宝提现 WithdrawType: 1, // 支付宝提现
WithdrawNo: outBizNo, WithdrawNo: outBizNo,
PayeeAccount: payeeAccount, PayeeAccount: payeeAccount,
PayeeName: sql.NullString{String: payeeName, Valid: true}, // 设置收款人姓名
Amount: amount, Amount: amount,
ActualAmount: finalWithdrawAmount, ActualAmount: finalWithdrawAmount,
TaxAmount: taxAmount, TaxAmount: taxAmount,

View File

@@ -2,13 +2,14 @@ package agent
import ( import (
"context" "context"
"database/sql"
"fmt"
"os"
"time"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/ctxdata" "tydata-server/common/ctxdata"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto" "tydata-server/pkg/lzkit/crypto"
"database/sql"
"fmt"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
@@ -44,7 +45,8 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
} }
if req.Mobile != "18889793585" { // 开发环境下跳过验证码验证,或者特定手机号跳过(保留原有逻辑)
if os.Getenv("ENV") != "development" && req.Mobile != "18889793585" {
// 校验验证码 // 校验验证码
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile) redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey) cacheCode, err := l.svcCtx.Redis.Get(redisKey)
@@ -77,6 +79,10 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err) return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err)
} }
} else { } else {
// 被封禁用户禁止登录/申请
if user.Disable == 1 {
return errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
if claims != nil && claims.UserType == model.UserTypeTemp { if claims != nil && claims.UserType == model.UserTypeTemp {
// 临时用户,转为正式用户 // 临时用户,转为正式用户
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id) err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)

View File

@@ -189,11 +189,36 @@ func (l *BankCardWithdrawalLogic) createBankCardWithdrawalRecord(session sqlx.Se
// 冻结资金(事务内操作) // 冻结资金(事务内操作)
func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error { func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error {
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 更新钱包余额
wallet.Balance -= amount wallet.Balance -= amount
wallet.FrozenBalance += amount wallet.FrozenBalance += amount
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet) err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet)
if err != nil { if err != nil {
return err return err
} }
// 记录交易流水(冻结操作)
err = l.svcCtx.AgentService.CreateWalletTransaction(
l.ctx,
session,
wallet.AgentId,
model.WalletTransactionTypeFreeze,
amount, // 变动金额
balanceBefore, // 变动前余额
wallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
wallet.FrozenBalance, // 变动后冻结余额
"", // 关联交易ID暂时为空创建提现记录后可以更新
0, // 关联用户ID
"提现申请冻结资金", // 备注
)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -2,13 +2,13 @@ package agent
import ( import (
"context" "context"
"encoding/hex"
"encoding/json"
"strconv"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/ctxdata" "tydata-server/common/ctxdata"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto" "tydata-server/pkg/lzkit/crypto"
"encoding/hex"
"encoding/json"
"strconv"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"

View File

@@ -58,6 +58,15 @@ func (l *GetAgentCommissionLogic) GetAgentCommission(req *types.GetCommissionReq
if copyErr != nil { if copyErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理佣金列表, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理佣金列表, %v", err)
} }
// 显式设置 status 字段和已退款金额
commission.Status = agentCommissionModel.Status
commission.RefundedAmount = agentCommissionModel.RefundedAmount
// 计算净佣金金额(防御性处理,避免出现负数)
netAmount := agentCommissionModel.Amount - agentCommissionModel.RefundedAmount
if netAmount < 0 {
netAmount = 0
}
commission.NetAmount = netAmount
product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, agentCommissionModel.ProductId) product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, agentCommissionModel.ProductId)
if findProductErr != nil { if findProductErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理佣金列表, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理佣金列表, %v", err)

View File

@@ -2,10 +2,10 @@ package agent
import ( import (
"context" "context"
"time"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/ctxdata" "tydata-server/common/ctxdata"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"time"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -126,25 +126,36 @@ func calculateDirectPushReport(commissions []*model.AgentCommission, loc *time.L
// 转换时区 // 转换时区
createTime := c.CreateTime createTime := c.CreateTime
// 只统计未被全部退款的佣金
if c.Status == 2 {
continue
}
// 统计净佣金:原始金额减去已退款金额
netAmount := c.Amount - c.RefundedAmount
if netAmount <= 0 {
continue
}
// 统计总量 // 统计总量
report.TotalCommission += c.Amount report.TotalCommission += netAmount
report.TotalReport++ report.TotalReport++
// 近24小时滚动周期 // 近24小时滚动周期
if createTime.After(todayStart) { if createTime.After(todayStart) {
report.Today.Commission += c.Amount report.Today.Commission += netAmount
report.Today.Report++ report.Today.Report++
} }
// 近7天滚动周期 // 近7天滚动周期
if createTime.After(last7dStart) { if createTime.After(last7dStart) {
report.Last7D.Commission += c.Amount report.Last7D.Commission += netAmount
report.Last7D.Report++ report.Last7D.Report++
} }
// 近30天滚动周期 // 近30天滚动周期
if createTime.After(last30dStart) { if createTime.After(last30dStart) {
report.Last30D.Commission += c.Amount report.Last30D.Commission += netAmount
report.Last30D.Report++ report.Last30D.Report++
} }
} }

View File

@@ -2,16 +2,17 @@ package auth
import ( import (
"context" "context"
"tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto"
"fmt" "fmt"
"math/rand" "math/rand"
"time" "time"
"tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto"
"github.com/pkg/errors" "github.com/pkg/errors"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/pkg/captcha"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client" dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
@@ -35,13 +36,27 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo
} }
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error { func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
cfg := l.svcCtx.Config.Captcha
if err := captcha.VerifyWithRequest(captcha.Config{
AccessKeyID: cfg.AccessKeyID,
AccessKeySecret: cfg.AccessKeySecret,
EndpointURL: cfg.EndpointURL,
SceneID: cfg.SceneID,
}, req.CaptchaVerifyParam, l.ctx); err != nil {
return err
}
// 默认action类型当未传入时默认为login便于小程序环境兼容
action := req.ActionType
if action == "" {
action = "login"
}
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)
} }
// 检查手机号是否在一分钟内已发送过验证码 // 检查手机号是否在一分钟内已发送过验证码
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, encryptedMobile) limitCodeKey := fmt.Sprintf("limit:%s:%s", action, encryptedMobile)
exists, err := l.svcCtx.Redis.Exists(limitCodeKey) exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
if err != nil { if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", encryptedMobile) return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", encryptedMobile)
@@ -62,7 +77,7 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
if *smsResp.Body.Code != "OK" { if *smsResp.Body.Code != "OK" {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message) return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
} }
codeKey := fmt.Sprintf("%s:%s", req.ActionType, encryptedMobile) codeKey := fmt.Sprintf("%s:%s", action, encryptedMobile)
// 将验证码保存到 Redis设置过期时间 // 将验证码保存到 Redis设置过期时间
err = l.svcCtx.Redis.Setex(codeKey, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟 err = l.svcCtx.Redis.Setex(codeKey, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
if err != nil { if err != nil {

View File

@@ -93,7 +93,6 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not
logx.Errorf("支付宝支付回调,修改订单信息失败: %+v", updateErr) logx.Errorf("支付宝支付回调,修改订单信息失败: %+v", updateErr)
return nil return nil
} }
if order.Status == "paid" { if order.Status == "paid" {
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil { if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
logx.Errorf("异步任务调度失败: %v", asyncErr) logx.Errorf("异步任务调度失败: %v", asyncErr)

View File

@@ -2,9 +2,13 @@ package pay
import ( import (
"context" "context"
"database/sql"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"time"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model" "tydata-server/app/main/model"
@@ -27,6 +31,7 @@ type PaymentTypeResp struct {
amount float64 amount float64
outTradeNo string outTradeNo string
description string description string
orderID int64 // 仅 query 类型有值agent_vip 为 0
} }
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic { func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
@@ -40,9 +45,10 @@ func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLo
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) { func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
var paymentTypeResp *PaymentTypeResp var paymentTypeResp *PaymentTypeResp
var prepayData interface{} var prepayData interface{}
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 {
case "agent_vip": case "agent_vip", "agent_upgrade":
paymentTypeResp, err = l.AgentVipOrderPayment(req, session) paymentTypeResp, err = l.AgentVipOrderPayment(req, session)
if err != nil { if err != nil {
return err return err
@@ -53,8 +59,22 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
if err != nil { if err != nil {
return err return err
} }
default:
err = errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "不支持的支付类型: %s", req.PayType)
return err
} }
// 开发环境测试支付模式:仅当 pay_method=test 时跳过实际支付,直接返回 test_payment_success
// 支付宝/微信在开发环境下仍走真实支付流程(跳转沙箱),支付成功后由回调更新订单
// 注意:订单状态更新在事务外进行,避免在事务中查询不到订单的问题
isDevTestPayment := os.Getenv("ENV") == "development" && req.PayMethod == "test"
if isDevTestPayment && paymentTypeResp != nil && paymentTypeResp.orderID != 0 {
prepayData = "test_payment_success"
logx.Infof("开发环境测试支付模式:订单 %s (ID: %d) 将在事务提交后更新状态", paymentTypeResp.outTradeNo, paymentTypeResp.orderID)
return nil
}
// 仅 wechat/alipay/appleiap 调起真实支付test 仅在上面 isDevTestPayment 分支处理
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)
@@ -62,6 +82,13 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
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" {
prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo) prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
} else if req.PayMethod == "test" {
if os.Getenv("ENV") != "development" {
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "开发环境测试支付仅在开发环境可用")
}
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "开发环境测试支付仅支持 query 类型订单")
} else {
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "不支持的支付方式: %s", req.PayMethod)
} }
if createOrderErr != nil { if createOrderErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr) return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
@@ -71,6 +98,46 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 开发环境测试支付模式:事务提交后处理订单状态更新和后续流程(仅 pay_method=test 且 query 类型 orderID>0
isDevTestPayment := os.Getenv("ENV") == "development" && req.PayMethod == "test"
if isDevTestPayment && paymentTypeResp != nil && paymentTypeResp.orderID != 0 {
go func() {
time.Sleep(200 * time.Millisecond)
finalOrderID := paymentTypeResp.orderID
order, findOrderErr := l.svcCtx.OrderModel.FindOne(context.Background(), finalOrderID)
if findOrderErr != nil {
logx.Errorf("开发测试模式查找订单失败订单ID: %d, 错误: %v", finalOrderID, findOrderErr)
return
}
order.Status = "paid"
now := time.Now()
order.PayTime = sql.NullTime{Time: now, Valid: true}
// 空报告模式:在 PaymentPlatform 标记为 "test",在 paySuccessNotify.ProcessTask 中通过
// order.PaymentPlatform == "test" 识别isEmptyReportMode并生成空报告、跳过 API 调用
isEmptyReportMode := req.PayMethod == "test"
if isEmptyReportMode {
order.PaymentPlatform = "test"
logx.Infof("开发环境空报告模式:订单 %s (ID: %d) 已标记为空报告模式", paymentTypeResp.outTradeNo, finalOrderID)
}
updateErr := l.svcCtx.OrderModel.UpdateWithVersion(context.Background(), nil, order)
if updateErr != nil {
logx.Errorf("开发测试模式更新订单状态失败订单ID: %d, 错误: %v", finalOrderID, updateErr)
return
}
if enqErr := l.svcCtx.AsynqService.SendQueryTask(finalOrderID); enqErr != nil {
logx.Errorf("开发测试模式入队生成报告失败订单ID: %d, 错误: %v", finalOrderID, enqErr)
}
logx.Infof("开发测试模式订单状态已更新为已支付并已入队生成报告订单ID: %d", finalOrderID)
// 再次短暂延迟,确保订单状态更新已提交
time.Sleep(100 * time.Millisecond)
}()
}
switch v := prepayData.(type) { switch v := prepayData.(type) {
case string: case string:
// 如果 prepayData 是字符串类型,直接返回 // 如果 prepayData 是字符串类型,直接返回
@@ -124,6 +191,42 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
if user.Inside == 1 { if user.Inside == 1 {
amount = 0.01 amount = 0.01
} }
// 检查72小时内身份证查询次数限制
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取AES密钥失败: %+v", decodeErr)
}
// 解密缓存中的参数
decryptedParams, decryptErr := crypto.AesDecrypt(data.Params, key)
if decryptErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解密缓存参数失败: %v", decryptErr)
}
var params map[string]interface{}
if unmarshalErr := json.Unmarshal(decryptedParams, &params); unmarshalErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析解密参数失败: %v", unmarshalErr)
}
// 获取身份证号
idCard, ok := params["id_card"].(string)
if !ok || idCard == "" {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 获取身份证号失败")
}
// 加密身份证号用于查询
encryptedIdCard, encryptErr := crypto.EncryptIDCard(idCard, key)
if encryptErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 加密身份证号失败: %v", encryptErr)
}
// 查询72小时内的查询次数
queryCount, countErr := l.svcCtx.QueryUserRecordModel.CountByEncryptedIdCardIn72Hours(l.ctx, encryptedIdCard)
if countErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 查询记录失败: %v", countErr)
}
// 如果72小时内查询次数大于等于2次禁止支付当前这次是第3次
if queryCount >= 2 {
return nil, errors.Wrapf(xerr.NewErrMsg("查询受限通知检测到您72小时内已完成2次报告查询系统已自动暂停服务。如需紧急查询请联系客服申请临时额度。"), "生成订单, 查询次数超限: %d", queryCount)
}
var orderID int64 var orderID int64
order := model.Order{ order := model.Order{
OrderNo: outTradeNo, OrderNo: outTradeNo,
@@ -144,6 +247,12 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
} }
orderID = insertedOrderID orderID = insertedOrderID
// 更新查询用户记录表的 order_id便于通过查询信息追溯订单
if rec, findErr := l.svcCtx.QueryUserRecordModel.FindOneByQueryNo(l.ctx, outTradeNo); findErr == nil {
rec.OrderId = orderID
_, _ = l.svcCtx.QueryUserRecordModel.Update(l.ctx, session, rec)
}
if data.AgentIdentifier != "" { if data.AgentIdentifier != "" {
agent, parsingErr := l.agentParsing(data.AgentIdentifier) agent, parsingErr := l.agentParsing(data.AgentIdentifier)
if parsingErr != nil { if parsingErr != nil {
@@ -157,7 +266,7 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert)
} }
} }
return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName}, nil return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName, orderID: orderID}, nil
} }
func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) { func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx) userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)

View File

@@ -90,6 +90,11 @@ func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter,
logx.Errorf("微信支付回调,更新订单失败%+v", updateErr) logx.Errorf("微信支付回调,更新订单失败%+v", updateErr)
return nil return nil
} }
// 更新查询用户记录表的 platform_order_id
if rec, findErr := l.svcCtx.QueryUserRecordModel.FindOneByQueryNo(l.ctx, *notification.OutTradeNo); findErr == nil {
rec.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
_, _ = l.svcCtx.QueryUserRecordModel.Update(l.ctx, nil, rec)
}
if order.Status == "paid" { if order.Status == "paid" {
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil { if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {

View File

@@ -10,12 +10,158 @@ import (
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/globalkey" "tydata-server/common/globalkey"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic" "github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"
) )
// HandleCommissionAndWalletDeduction 处理退款后的佣金状态更新和钱包金额扣除
// refundAmount 为本次实际退款金额(单位:元),从代理侧总共需要承担的金额
// 该函数会优先冲减当前订单相关的佣金(基于 RefundedAmount不足部分再从钱包余额/冻结余额中扣除
func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.ServiceContext, session sqlx.Session, order *model.Order, refundAmount float64) error {
if refundAmount <= 0 {
return nil
}
// 查询当前订单关联的所有佣金记录(包括已结算和冻结),剔除已经完全退款的
commissionBuilder := svcCtx.AgentCommissionModel.SelectBuilder()
commissions, commissionsErr := svcCtx.AgentCommissionModel.FindAll(ctx, commissionBuilder.Where(squirrel.And{
squirrel.Eq{"order_id": order.Id},
squirrel.NotEq{"status": 2}, // 排除已全部退款的佣金
}), "")
if commissionsErr != nil {
logx.Errorf("查询代理佣金失败订单ID: %d, 错误: %v", order.Id, commissionsErr)
return nil // 返回 nil因为佣金更新失败不应影响退款流程
}
if len(commissions) == 0 {
return nil
}
// 剩余需要由佣金 + 钱包共同承担的退款金额
remainRefundAmount := refundAmount
// 记录每个代理本次需要从钱包扣除的金额,避免同一代理多条佣金时重复查钱包并产生多条流水
type walletAdjust struct {
agentId int64
amount float64 // 需要从该代理钱包扣除的金额(正数)
}
walletAdjustMap := make(map[int64]*walletAdjust)
// 1. 先在佣金记录上做冲减:增加 RefundedAmount必要时将状态置为已退款
for _, commission := range commissions {
available := commission.Amount - commission.RefundedAmount
if available <= 0 {
continue
}
if remainRefundAmount <= 0 {
break
}
// 当前这条佣金最多可冲减 available本次实际冲减 currentRefund
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
// 更新佣金的已退款金额
commission.RefundedAmount += currentRefund
// 如果这条佣金已经被完全冲减,则标记为已退款
if commission.RefundedAmount >= commission.Amount {
commission.Status = 2
}
// 更新佣金状态到数据库
var updateCommissionErr error
if session != nil {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
} else {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, nil, commission)
}
if updateCommissionErr != nil {
logx.Errorf("更新代理佣金状态失败佣金ID: %d, 订单ID: %d, 错误: %v", commission.Id, order.Id, updateCommissionErr)
continue // 如果佣金状态更新失败,就不继续计入本次冲减
}
// 记录该代理需要从钱包扣除的金额(可能后续还有其他佣金叠加)
wa, ok := walletAdjustMap[commission.AgentId]
if !ok {
wa = &walletAdjust{agentId: commission.AgentId}
walletAdjustMap[commission.AgentId] = wa
}
wa.amount += currentRefund
remainRefundAmount -= currentRefund
}
// 2. 再按代理维度,从钱包(冻结余额/可用余额)中扣除对应金额
for _, wa := range walletAdjustMap {
if wa.amount <= 0 {
continue
}
// 处理用户钱包的金额扣除
wallet, err := svcCtx.AgentWalletModel.FindOneByAgentId(ctx, wa.agentId)
if err != nil {
logx.Errorf("查询代理钱包失败代理ID: %d, 错误: %v", wa.agentId, err)
continue
}
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 优先从冻结余额中扣除(与原先“冻结佣金优先使用冻结余额”的设计一致)
deduct := wa.amount
if wallet.FrozenBalance >= deduct {
wallet.FrozenBalance -= deduct
} else {
remaining := deduct - wallet.FrozenBalance
wallet.FrozenBalance = 0
// 可用余额可以为负数,由业务承担风险
wallet.Balance -= remaining
}
// 变动后余额和冻结余额
balanceAfter := wallet.Balance
frozenBalanceAfter := wallet.FrozenBalance
// 更新钱包
var updateWalletErr error
if session != nil {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
} else {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, nil, wallet)
}
if updateWalletErr != nil {
logx.Errorf("更新代理钱包失败代理ID: %d, 错误: %v", wa.agentId, updateWalletErr)
continue
}
// 创建钱包交易流水记录(退款)
transErr := svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wa.agentId,
model.WalletTransactionTypeRefund,
-wa.amount*-1, // 钱包流水金额为负数
balanceBefore,
balanceAfter,
frozenBalanceBefore,
frozenBalanceAfter,
order.OrderNo,
0, // 这里不强绑到某一条具体佣金记录,按订单维度记录
"订单退款,佣金已扣除",
)
if transErr != nil {
logx.Errorf("创建代理钱包流水记录失败代理ID: %d, 错误: %v", wa.agentId, transErr)
continue
}
}
return nil
}
type WechatPayRefundCallbackLogic struct { type WechatPayRefundCallbackLogic struct {
logx.Logger logx.Logger
ctx context.Context ctx context.Context
@@ -31,7 +177,8 @@ func NewWechatPayRefundCallbackLogic(ctx context.Context, svcCtx *svc.ServiceCon
} }
// handleQueryOrderRefund 处理查询订单退款 // handleQueryOrderRefund 处理查询订单退款
func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, status refunddomestic.Status) error { // refundAmountYuan 表示微信本次实际退款金额(单位:元)
func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, status refunddomestic.Status, refundAmountYuan float64) error {
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo) order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
if err != nil { if err != nil {
return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo) return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo)
@@ -72,6 +219,9 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
if err := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); err != nil { if err := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); err != nil {
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo) return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
} }
// 退款成功时,按本次实际退款金额更新代理佣金状态并扣除钱包金额
_ = HandleCommissionAndWalletDeduction(ctx, l.svcCtx, session, order, refundAmountYuan)
} }
// 查找最新的pending状态的退款记录 // 查找最新的pending状态的退款记录
@@ -194,15 +344,22 @@ func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWr
var processErr error var processErr error
// 计算本次实际退款金额(单位:元),用于后续佣金和钱包扣减
var refundAmountYuan float64
if notification.Amount != nil && notification.Amount.Refund != nil {
// 微信退款金额单位为分,这里转换为元
refundAmountYuan = float64(*notification.Amount.Refund) / 100.0
}
// 4. 根据订单号前缀处理不同类型的订单 // 4. 根据订单号前缀处理不同类型的订单
switch { switch {
case strings.HasPrefix(orderNo, "Q_"): case strings.HasPrefix(orderNo, "Q_"):
processErr = l.handleQueryOrderRefund(orderNo, status) processErr = l.handleQueryOrderRefund(orderNo, status, refundAmountYuan)
case strings.HasPrefix(orderNo, "A_"): case strings.HasPrefix(orderNo, "A_"):
processErr = l.handleAgentOrderRefund(orderNo, status) processErr = l.handleAgentOrderRefund(orderNo, status)
default: default:
// 兼容旧订单,假设没有前缀的是查询订单 // 兼容旧订单,假设没有前缀的是查询订单
processErr = l.handleQueryOrderRefund(orderNo, status) processErr = l.handleQueryOrderRefund(orderNo, status, refundAmountYuan)
} }
// 5. 处理错误并响应 // 5. 处理错误并响应

View File

@@ -2,9 +2,11 @@ package query
import ( import (
"context" "context"
"database/sql"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"time" "time"
"tydata-server/app/main/api/internal/service" "tydata-server/app/main/api/internal/service"
"tydata-server/app/main/model" "tydata-server/app/main/model"
@@ -38,6 +40,7 @@ func NewQueryServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Quer
func (l *QueryServiceLogic) QueryService(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) { func (l *QueryServiceLogic) QueryService(req *types.QueryServiceReq) (resp *types.QueryServiceResp, err error) {
if req.AgentIdentifier != "" { if req.AgentIdentifier != "" {
l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier) l.ctx = context.WithValue(l.ctx, "agentIdentifier", req.AgentIdentifier)
} else if req.App { } else if req.App {
l.ctx = context.WithValue(l.ctx, "app", req.App) l.ctx = context.WithValue(l.ctx, "app", req.App)
@@ -106,6 +109,7 @@ func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*t
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "marriage", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -166,7 +170,7 @@ func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq)
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "homeservice", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -227,7 +231,7 @@ func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceRe
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "riskassessment", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -261,11 +265,11 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq)
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr) return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
} }
// 校验验证码 // 校验验证码CompanyInfo 暂不校验)
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code) // verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil { // if verifyCodeErr != nil {
return nil, verifyCodeErr // return nil, verifyCodeErr
} // }
// 校验三要素 // 校验三要素
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile) verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
@@ -287,7 +291,7 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq)
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "companyinfo", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -348,7 +352,7 @@ func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "rentalinfo", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -409,7 +413,7 @@ func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryS
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "preloanbackgroundcheck", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -469,7 +473,7 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "backgroundcheck", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -527,7 +531,7 @@ func (l *QueryServiceLogic) ProcessPersonalDataLogic(req *types.QueryServiceReq)
if cacheDataErr != nil { if cacheDataErr != nil {
return nil, cacheDataErr return nil, cacheDataErr
} }
l.recordQueryUserRecord(params, "personalData", userID, cacheNo)
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
@@ -558,6 +562,10 @@ func (l *QueryServiceLogic) DecryptData(data string) ([]byte, error) {
// 校验验证码 // 校验验证码
func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error { func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error {
// 开发环境下跳过验证码验证
if os.Getenv("ENV") == "development" {
return nil
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey) encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey)
if err != nil { if err != nil {
@@ -579,6 +587,12 @@ func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error {
// 二、三要素验证 // 二、三要素验证
func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) error { func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) error {
// 开发环境下跳过二/三要素验证避免未授权IP调用天元API失败
isDevelopment := os.Getenv("ENV") == "development"
if isDevelopment {
return nil
}
if !l.svcCtx.Config.SystemConfig.ThreeVerify { if !l.svcCtx.Config.SystemConfig.ThreeVerify {
twoVerification := service.TwoFactorVerificationRequest{ twoVerification := service.TwoFactorVerificationRequest{
Name: Name, Name: Name,
@@ -643,6 +657,76 @@ func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product str
return outTradeNo, nil return outTradeNo, nil
} }
// recordQueryUserRecord 写入查询用户记录表,用于通过姓名/身份证/手机号追溯订单
// 重要name、id_card、mobile 必须以 AES-ECB+Base64 密文入库,禁止写入明文
func (l *QueryServiceLogic) recordQueryUserRecord(params map[string]interface{}, product string, userID int64, queryNo string) {
getStr := func(k string) string {
if v, ok := params[k]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
if secretKey == "" {
l.Errorf("查询用户记录表加密失败, Encrypt.SecretKey 未配置,拒绝写入明文 queryNo=%s", queryNo)
return
}
key, keyErr := hex.DecodeString(secretKey)
if keyErr != nil {
l.Errorf("查询用户记录表加密失败, 密钥解析错误 queryNo=%s err=%v", queryNo, keyErr)
return
}
// 以下三字段仅使用加密后的值赋值,不得使用 getStr 的明文
encName := ""
if name := getStr("name"); name != "" {
if s, err := crypto.AesEcbEncrypt([]byte(name), key); err != nil {
l.Errorf("查询用户记录表姓名加密失败 queryNo=%s err=%v", queryNo, err)
return
} else {
encName = s
}
}
encIdCard := ""
if idCard := getStr("id_card"); idCard != "" {
if s, err := crypto.EncryptIDCard(idCard, key); err != nil {
l.Errorf("查询用户记录表身份证加密失败 queryNo=%s err=%v", queryNo, err)
return
} else {
encIdCard = s
}
}
encMobile := ""
if mobile := getStr("mobile"); mobile != "" {
if s, err := crypto.EncryptMobile(mobile, secretKey); err != nil {
l.Errorf("查询用户记录表手机号加密失败 queryNo=%s err=%v", queryNo, err)
return
} else {
encMobile = s
}
}
agentIdentifier := sql.NullString{}
if v, ok := l.ctx.Value("agentIdentifier").(string); ok && v != "" {
agentIdentifier = sql.NullString{String: v, Valid: true}
}
// rec 的 Name、IdCard、Mobile 仅使用密文 encName、encIdCard、encMobile
rec := &model.QueryUserRecord{
UserId: userID,
Name: encName,
IdCard: encIdCard,
Mobile: encMobile,
Product: product,
QueryNo: queryNo,
OrderId: 0,
PlatformOrderId: sql.NullString{},
AgentIdentifier: agentIdentifier,
}
if _, err := l.svcCtx.QueryUserRecordModel.Insert(l.ctx, nil, rec); err != nil {
l.Errorf("查询用户记录表写入失败 queryNo=%s err=%v", queryNo, err)
}
}
// GetOrCreateUser 获取或创建用户 // GetOrCreateUser 获取或创建用户
// 1. 如果上下文中已有用户ID直接返回 // 1. 如果上下文中已有用户ID直接返回
// 2. 如果是代理查询或APP请求创建新用户 // 2. 如果是代理查询或APP请求创建新用户

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"os"
"time" "time"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
@@ -42,7 +43,8 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 加密手机号失败: %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 加密手机号失败: %v", err)
} }
if req.Mobile != "18889793585" { // 开发环境下跳过验证码验证,或者特定手机号跳过(保留原有逻辑)
if os.Getenv("ENV") != "development" && req.Mobile != "18889793585" {
// 检查手机号是否在一分钟内已发送过验证码 // 检查手机号是否在一分钟内已发送过验证码
redisKey := fmt.Sprintf("%s:%s", "bindMobile", encryptedMobile) redisKey := fmt.Sprintf("%s:%s", "bindMobile", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey) cacheCode, err := l.svcCtx.Redis.Get(redisKey)
@@ -62,6 +64,10 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
} }
if user != nil { if user != nil {
// 被封禁用户禁止绑定/登录
if user.Disable == 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
// 进行平台绑定 // 进行平台绑定
if claims != nil { if claims != nil {
if req.Mobile != "18889793585" { if req.Mobile != "18889793585" {

View File

@@ -33,6 +33,14 @@ func (l *GetTokenLogic) GetToken() (resp *types.MobileCodeLoginResp, err error)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
} }
// 被封禁用户禁止刷新 token
user, err := l.svcCtx.UserModel.FindOne(l.ctx, claims.UserId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
if user.Disable == 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId, claims.UserType) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId, claims.UserType)
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)

View File

@@ -2,14 +2,15 @@ package user
import ( import (
"context" "context"
"database/sql"
"fmt"
"os"
"time"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"tydata-server/pkg/lzkit/crypto" "tydata-server/pkg/lzkit/crypto"
"database/sql"
"fmt"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
@@ -37,6 +38,8 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
if err != nil { if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err)
} }
// 开发环境下跳过验证码验证
if os.Getenv("ENV") != "development" {
// 检查手机号是否在一分钟内已发送过验证码 // 检查手机号是否在一分钟内已发送过验证码
redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile) redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey) cacheCode, err := l.svcCtx.Redis.Get(redisKey)
@@ -49,6 +52,7 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
if cacheCode != req.Code { if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile) return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile)
} }
}
var userID int64 var userID int64
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true}) user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
if findUserErr != nil && findUserErr != model.ErrNotFound { if findUserErr != nil && findUserErr != model.ErrNotFound {
@@ -60,6 +64,10 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 注册用户失败: %+v", err) return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 注册用户失败: %+v", err)
} }
} else { } else {
// 被封禁用户禁止登录
if user.Disable == 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
userID = user.Id userID = user.Id
} }
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal) token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)

View File

@@ -49,7 +49,14 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe
var userID int64 var userID int64
var userType int64 var userType int64
if userAuth != nil { if userAuth != nil {
// 已存在用户,直接登录 // 已存在用户,直接登录(被封禁用户禁止登录)
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败: %v", err)
}
if user.Disable == 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
userID = userAuth.UserId userID = userAuth.UserId
userType = model.UserTypeNormal userType = model.UserTypeNormal
} else { } else {

View File

@@ -48,7 +48,14 @@ func (l *WxMiniAuthLogic) WxMiniAuth(req *types.WXMiniAuthReq) (resp *types.WXMi
var userID int64 var userID int64
var userType int64 var userType int64
if userAuth != nil { if userAuth != nil {
// 已存在用户,直接登录 // 已存在用户,直接登录(被封禁用户禁止登录)
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败: %v", err)
}
if user.Disable == 1 {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
userID = userAuth.UserId userID = userAuth.UserId
userType = model.UserTypeNormal userType = model.UserTypeNormal
} else { } else {

View File

@@ -1,33 +1,60 @@
package middleware package middleware
import ( import (
"net/http"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/ctxdata" "tydata-server/common/ctxdata"
"tydata-server/common/result"
"tydata-server/common/xerr" "tydata-server/common/xerr"
"net/http"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/rest/httpx" "github.com/zeromicro/go-zero/rest/httpx"
) )
// 用户封禁状态0 可用1 禁用
const userDisableStatus = 1
type UserAuthInterceptorMiddleware struct { type UserAuthInterceptorMiddleware struct {
UserModel model.UserModel
} }
func NewUserAuthInterceptorMiddleware() *UserAuthInterceptorMiddleware { func NewUserAuthInterceptorMiddleware(userModel model.UserModel) *UserAuthInterceptorMiddleware {
return &UserAuthInterceptorMiddleware{} return &UserAuthInterceptorMiddleware{UserModel: userModel}
} }
func (m *UserAuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { func (m *UserAuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
claims, err := ctxdata.GetClaimsFromCtx(r.Context()) claims, err := ctxdata.GetClaimsFromCtx(r.Context())
if err != nil { if err != nil {
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err)) m.writeErrorResponse(w, http.StatusUnauthorized, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err))
return return
} }
if claims.UserType == model.UserTypeTemp { if claims.UserType == model.UserTypeTemp {
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(xerr.USER_NEED_BIND_MOBILE), "token解析失败: %v", err)) m.writeErrorResponse(w, http.StatusUnauthorized, errors.Wrapf(xerr.NewErrCode(xerr.USER_NEED_BIND_MOBILE), "请先绑定手机号"))
return
}
// 封禁校验:用户已被禁用则直接拒绝
user, err := m.UserModel.FindOne(r.Context(), claims.UserId)
if err != nil {
m.writeErrorResponse(w, http.StatusUnauthorized, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err))
return
}
if user.Disable == userDisableStatus {
m.writeErrorResponse(w, http.StatusForbidden, xerr.NewErrCode(xerr.USER_DISABLED))
return return
} }
next(w, r) next(w, r)
} }
} }
// writeErrorResponse 统一返回 code + msg便于前端展示提示信息
func (m *UserAuthInterceptorMiddleware) writeErrorResponse(w http.ResponseWriter, statusCode int, err error) {
errcode := xerr.SERVER_COMMON_ERROR
errmsg := xerr.MapErrMsg(errcode)
if e, ok := errors.Cause(err).(*xerr.CodeError); ok {
errcode = e.GetErrCode()
errmsg = e.GetErrMsg()
}
httpx.WriteJson(w, statusCode, result.Error(errcode, errmsg))
}

View File

@@ -10,6 +10,8 @@ import (
"path" "path"
"regexp" "regexp"
"strings" "strings"
paylogic "tydata-server/app/main/api/internal/logic/pay"
"tydata-server/app/main/api/internal/service"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model" "tydata-server/app/main/model"
@@ -44,12 +46,13 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
if err != nil { if err != nil {
return fmt.Errorf("无效的订单ID: %d, %v", payload.OrderID, err) return fmt.Errorf("无效的订单ID: %d, %v", payload.OrderID, err)
} }
env := os.Getenv("ENV") // 必须已支付才处理:仅支付宝/微信/苹果回调或 pay_method=test 的异步流程会将订单标为 paid此处不再按 ENV 放宽
if order.Status != "paid" && env != "development" { if order.Status != "paid" {
err = fmt.Errorf("无效的订单: %d", payload.OrderID) err = fmt.Errorf("无效的订单状态(非已支付): orderID=%d, status=%s", payload.OrderID, order.Status)
logx.Errorf("处理任务失败,原因: %v", err) logx.Errorf("处理任务失败,原因: %v", err)
return asynq.SkipRetry return asynq.SkipRetry
} }
env := os.Getenv("ENV")
product, err := l.svcCtx.ProductModel.FindOne(ctx, order.ProductId) product, err := l.svcCtx.ProductModel.FindOne(ctx, order.ProductId)
if err != nil { if err != nil {
return fmt.Errorf("找不到相关产品: orderID: %d, productID: %d", payload.OrderID, order.ProductId) return fmt.Errorf("找不到相关产品: orderID: %d, productID: %d", payload.OrderID, order.ProductId)
@@ -140,11 +143,20 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
decryptData = updatedDecryptData decryptData = updatedDecryptData
} }
} }
// 调用API请求服务开发环境下不调用其它产品使用默认空报告
// 调用API请求服务 var responseData []service.APIResponseData
responseData, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id) if env == "development" {
if err != nil { // 开发环境:生成仅包含基本信息的默认空报告,不调用外部 API
return l.handleError(ctx, err, order, query) // 空报告模式生成空的报告数据跳过API调用
logx.Infof("空报告模式:订单 %s (ID: %s) 跳过API调用生成空报告", order.OrderNo, order.Id)
// 空数组,表示没有数据;与 json.Marshal 配合得到 []
responseData = []service.APIResponseData{}
} else {
var processErr error
responseData, processErr = l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
if processErr != nil {
return l.handleError(ctx, processErr, order, query)
}
} }
// 计算成功模块的总成本价 // 计算成功模块的总成本价
@@ -246,19 +258,23 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
// 退款 // 退款
if order.PaymentPlatform == "wechat" { if order.PaymentPlatform == "wechat" {
// 微信退款为异步结果,这里只发起退款申请,订单状态与佣金/钱包扣减交由退款回调统一处理
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount) refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
if refundErr != nil { if refundErr != nil {
logx.Error(refundErr) logx.Error(refundErr)
return asynq.SkipRetry return asynq.SkipRetry
} }
logx.Infof("已发起微信退款申请, orderID: %d, amount: %f", order.Id, order.Amount)
return asynq.SkipRetry
} else { } else {
// 支付宝退款为同步结果,这里直接根据返回结果更新订单和佣金/钱包
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount) refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
if refundErr != nil { if refundErr != nil {
logx.Error(refundErr) logx.Error(refundErr)
return asynq.SkipRetry return asynq.SkipRetry
} }
if refund.IsSuccess() { if refund.IsSuccess() {
logx.Errorf("支付宝退款成功, orderID: %d", order.Id) logx.Infof("支付宝退款成功, orderID: %d", order.Id)
// 更新订单状态为退款 // 更新订单状态为退款
order.Status = "refunded" order.Status = "refunded"
updateOrderErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, nil, order) updateOrderErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, nil, order)
@@ -266,6 +282,10 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
logx.Errorf("更新订单状态失败订单ID: %d, 错误: %v", order.Id, updateOrderErr) logx.Errorf("更新订单状态失败订单ID: %d, 错误: %v", order.Id, updateOrderErr)
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr) return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
} }
// 使用公共函数按本次退款金额处理佣金和钱包扣除
_ = paylogic.HandleCommissionAndWalletDeduction(ctx, l.svcCtx, nil, order, order.Amount)
return asynq.SkipRetry return asynq.SkipRetry
} else { } else {
logx.Errorf("支付宝退款失败:%v", refundErr) logx.Errorf("支付宝退款失败:%v", refundErr)

View File

@@ -2,9 +2,9 @@ package queue
import ( import (
"context" "context"
"fmt"
"tydata-server/app/main/api/internal/svc" "tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"fmt"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
) )
@@ -24,6 +24,8 @@ func NewCronJob(ctx context.Context, svcCtx *svc.ServiceContext) *CronJob {
func (l *CronJob) Register() *asynq.ServeMux { func (l *CronJob) Register() *asynq.ServeMux {
redisClientOpt := asynq.RedisClientOpt{Addr: l.svcCtx.Config.CacheRedis[0].Host, Password: l.svcCtx.Config.CacheRedis[0].Pass} redisClientOpt := asynq.RedisClientOpt{Addr: l.svcCtx.Config.CacheRedis[0].Host, Password: l.svcCtx.Config.CacheRedis[0].Pass}
scheduler := asynq.NewScheduler(redisClientOpt, nil) scheduler := asynq.NewScheduler(redisClientOpt, nil)
// 注册清理查询数据任务
task := asynq.NewTask(types.MsgCleanQueryData, nil, nil) task := asynq.NewTask(types.MsgCleanQueryData, nil, nil)
_, err := scheduler.Register(TASKTIME, task) _, err := scheduler.Register(TASKTIME, task)
if err != nil { if err != nil {
@@ -35,6 +37,7 @@ func (l *CronJob) Register() *asynq.ServeMux {
mux := asynq.NewServeMux() mux := asynq.NewServeMux()
mux.Handle(types.MsgPaySuccessQuery, NewPaySuccessNotifyUserHandler(l.svcCtx)) mux.Handle(types.MsgPaySuccessQuery, NewPaySuccessNotifyUserHandler(l.svcCtx))
mux.Handle(types.MsgCleanQueryData, NewCleanQueryDataHandler(l.svcCtx)) mux.Handle(types.MsgCleanQueryData, NewCleanQueryDataHandler(l.svcCtx))
mux.Handle(types.MsgUnfreezeCommission, NewUnfreezeCommissionHandler(l.svcCtx))
return mux return mux
} }

View File

@@ -0,0 +1,159 @@
package queue
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"tydata-server/app/main/model"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
// 定义佣金状态常量
const (
CommissionStatusReleased = 0 // 已发放
CommissionStatusFrozen = 1 // 冻结佣金
)
type UnfreezeCommissionHandler struct {
svcCtx *svc.ServiceContext
}
func NewUnfreezeCommissionHandler(svcCtx *svc.ServiceContext) *UnfreezeCommissionHandler {
return &UnfreezeCommissionHandler{
svcCtx: svcCtx,
}
}
func (l *UnfreezeCommissionHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
now := time.Now()
logx.Infof("%s - 开始执行佣金解冻任务", now.Format("2006-01-02 15:04:05"))
// 解析任务payload获取佣金ID
var payload types.MsgUnfreezeCommissionPayload
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
logx.Errorf("解析佣金解冻任务payload失败: %v", err)
return err
}
commissionID := payload.CommissionID
if commissionID <= 0 {
logx.Errorf("无效的佣金ID: %d", commissionID)
return fmt.Errorf("无效的佣金ID: %d", commissionID)
}
// 根据佣金ID查询特定佣金记录
commission, err := l.svcCtx.AgentCommissionModel.FindOne(ctx, commissionID)
if err != nil {
logx.Errorf("查询佣金记录ID %d 失败: %v", commissionID, err)
return err
}
// 检查佣金状态是否为冻结状态
if commission.Status != CommissionStatusFrozen {
logx.Infof("佣金记录ID %d 状态不是冻结状态,当前状态: %d无需处理", commissionID, commission.Status)
return nil
}
// 使用事务处理解冻操作
err = l.svcCtx.AgentCommissionModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 获取代理钱包记录
agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId)
if err != nil {
logx.Errorf("查询代理ID %d 的钱包记录失败: %v", commission.AgentId, err)
return err
}
// 计算当前佣金在发生退款后的“净佣金金额”
commissionAmount := commission.Amount - commission.RefundedAmount
if commissionAmount <= 0 {
logx.Infof("佣金记录ID %d 已被全部退款或无可解冻金额,跳过解冻", commissionID)
return nil
}
// 更新钱包余额:增加净佣金金额到 balance减少相应的 frozen_balance
agentWallet.Balance += commissionAmount
agentWallet.FrozenBalance -= commissionAmount
agentWallet.UpdateTime = now
// 更新钱包数据库(使用 UpdateWithVersion 保持乐观锁)
updateWalletErr := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, agentWallet)
if updateWalletErr != nil {
// 如果是版本冲突错误,重新查询最新的数据后重试
if errors.Is(updateWalletErr, model.ErrNoRowsUpdate) {
logx.Infof("代理ID %d 的钱包版本冲突,重新查询最新数据重试", commission.AgentId)
latestWallet, findErr := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, commission.AgentId)
if findErr != nil {
logx.Errorf("重新查询代理ID %d 的钱包记录失败: %v", commission.AgentId, findErr)
return findErr
}
// 重新累加金额
latestWallet.Balance += commissionAmount
latestWallet.FrozenBalance -= commissionAmount
latestWallet.UpdateTime = now
retryUpdateErr := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, latestWallet)
if retryUpdateErr != nil {
logx.Errorf("重试更新代理ID %d 的钱包记录失败: %v", commission.AgentId, retryUpdateErr)
return retryUpdateErr
}
logx.Infof("重试成功已更新代理ID %d 的钱包记录", commission.AgentId)
} else {
logx.Errorf("更新代理ID %d 的钱包记录失败: %v", commission.AgentId, updateWalletErr)
return updateWalletErr
}
}
// 钱包更新成功后,再更新佣金状态为已发放
commission.Status = CommissionStatusReleased
commission.UpdateTime = now
// 更新佣金数据库(使用 UpdateWithVersion 保持乐观锁)
err = l.svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
if err != nil {
// 如果是版本冲突错误,重新查询最新的数据后重试
if errors.Is(err, model.ErrNoRowsUpdate) {
logx.Infof("佣金记录ID %d 版本冲突,重新查询最新数据重试", commissionID)
latestCommission, findErr := l.svcCtx.AgentCommissionModel.FindOne(ctx, commissionID)
if findErr != nil {
logx.Errorf("重新查询佣金记录ID %d 失败: %v", commissionID, findErr)
return findErr
}
// 检查状态是否已被其他操作修改
if latestCommission.Status != CommissionStatusFrozen {
logx.Errorf("佣金记录ID %d 的状态已被其他操作修改,当前状态: %d", commissionID, latestCommission.Status)
return fmt.Errorf("佣金记录状态已被修改")
}
// 重新更新状态
latestCommission.Status = CommissionStatusReleased
latestCommission.UpdateTime = now
retryUpdateErr := l.svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, latestCommission)
if retryUpdateErr != nil {
logx.Errorf("重试更新佣金记录ID %d 失败: %v", commissionID, retryUpdateErr)
return retryUpdateErr
}
logx.Infof("重试成功已更新佣金记录ID %d", commissionID)
} else {
logx.Errorf("更新佣金记录ID %d 失败: %v", commissionID, err)
return err
}
}
logx.Infof("成功解冻佣金记录ID %d代理ID %d佣金金额 %.2f,已将佣金金额从冻结余额转移到可用余额",
commissionID, commission.AgentId, commissionAmount)
return nil
})
if err != nil {
logx.Errorf("%s - 佣金解冻任务失败: %v", now.Format("2006-01-02 15:04:05"), err)
return err
}
logx.Infof("%s - 佣金解冻任务完成佣金ID: %d", now.Format("2006-01-02 15:04:05"), commissionID)
return nil
}

View File

@@ -2,11 +2,15 @@ package service
import ( import (
"context" "context"
"database/sql"
"fmt"
"tydata-server/app/main/api/internal/config" "tydata-server/app/main/api/internal/config"
"tydata-server/app/main/model" "tydata-server/app/main/model"
"tydata-server/common/globalkey"
"tydata-server/pkg/lzkit/lzUtils" "tydata-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"
) )
@@ -29,6 +33,8 @@ type AgentService struct {
AgentPlatformDeductionModel model.AgentPlatformDeductionModel AgentPlatformDeductionModel model.AgentPlatformDeductionModel
AgentActiveStatModel model.AgentActiveStatModel AgentActiveStatModel model.AgentActiveStatModel
AgentWithdrawalModel model.AgentWithdrawalModel AgentWithdrawalModel model.AgentWithdrawalModel
AgentWalletTransactionModel model.AgentWalletTransactionModel
AsynqService *AsynqService
} }
func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel model.AgentModel, agentAuditModel model.AgentAuditModel, func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel model.AgentModel, agentAuditModel model.AgentAuditModel,
@@ -38,7 +44,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel, agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel,
agentMembershipUserConfigModel model.AgentMembershipUserConfigModel, agentMembershipUserConfigModel model.AgentMembershipUserConfigModel,
agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel, agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel,
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel) *AgentService { agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, asynqService *AsynqService) *AgentService {
return &AgentService{ return &AgentService{
config: c, config: c,
@@ -59,6 +65,8 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
AgentPlatformDeductionModel: agentPlatformDeductionModel, AgentPlatformDeductionModel: agentPlatformDeductionModel,
AgentActiveStatModel: agentActiveStatModel, AgentActiveStatModel: agentActiveStatModel,
AgentWithdrawalModel: agentWithdrawalModel, AgentWithdrawalModel: agentWithdrawalModel,
AgentWalletTransactionModel: agentWalletTransactionModel,
AsynqService: asynqService,
} }
} }
@@ -113,38 +121,94 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return findAgentMembersipConfigModelErr return findAgentMembersipConfigModelErr
} }
// 定价 // 定价
commissionCost, commissionCostErr := l.CommissionCost(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, session) commissionCost, commissionCostErr := l.CommissionCost(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, order.Id, session)
if commissionCostErr != nil { if commissionCostErr != nil {
return commissionCostErr return commissionCostErr
} }
// 提价 // 提价
commissionPricing, commissionPricingErr := l.CommissionPricing(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, order.Amount, session) commissionPricing, commissionPricingErr := l.CommissionPricing(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, order.Amount, order.Id, session)
if commissionPricingErr != nil { if commissionPricingErr != nil {
return commissionPricingErr return commissionPricingErr
} }
// 上级克扣的成本 // 上级克扣的成本
descendantDeductedAmount = commissionCost + commissionPricing descendantDeductedAmount = commissionCost + commissionPricing
// 奖励
// 佣金
ancestorCommissionReward, ancestorCommissionErr := l.AncestorCommission(transCtx, agentID, AncestorId, session) ancestorCommissionReward, ancestorCommissionErr := l.AncestorCommission(transCtx, agentID, AncestorId, session)
if ancestorCommissionErr != nil { if ancestorCommissionErr != nil {
return ancestorCommissionErr return ancestorCommissionErr
} }
// 给上级成本以及佣金 // 给上级成本以及佣金
ancestorCommissionAmount := commissionCost + commissionPricing + ancestorCommissionReward ancestorCommissionAmount := commissionCost + commissionPricing
ancestorWallet, findAgentWalletModelErr := l.AgentWalletModel.FindOneByAgentId(transCtx, AncestorId) ancestorWallet, findAgentWalletModelErr := l.AgentWalletModel.FindOneByAgentId(transCtx, AncestorId)
if findAgentWalletModelErr != nil { if findAgentWalletModelErr != nil {
return findAgentWalletModelErr return findAgentWalletModelErr
} }
// 记录变动前的余额
balanceBefore := ancestorWallet.Balance
frozenBalanceBefore := ancestorWallet.FrozenBalance
// 奖励不冻结直接进入balance
ancestorWallet.Balance += ancestorCommissionReward
// 根据安全防御模式配置决定佣金处理方式
var commissionStatus int64
if l.config.SystemConfig.CommissionSafeMode {
// 安全防御模式佣金冻结在frozen_balance中
ancestorWallet.FrozenBalance += ancestorCommissionAmount
commissionStatus = 1 // 冻结状态
} else {
// 非安全防御模式佣金直接进入balance
ancestorWallet.Balance += ancestorCommissionAmount ancestorWallet.Balance += ancestorCommissionAmount
ancestorWallet.TotalEarnings += ancestorCommissionAmount commissionStatus = 0 // 已结算状态
}
// 为上级创建佣金记录
ancestorCommissionRecord := model.AgentCommission{
AgentId: AncestorId,
OrderId: order.Id,
Amount: ancestorCommissionAmount,
ProductId: order.ProductId,
Status: commissionStatus,
}
insertResult, insertAncestorCommissionErr := l.AgentCommissionModel.Insert(transCtx, session, &ancestorCommissionRecord)
if insertAncestorCommissionErr != nil {
return insertAncestorCommissionErr
}
ancestorWallet.TotalEarnings += ancestorCommissionAmount + ancestorCommissionReward
updateErr := l.AgentWalletModel.UpdateWithVersion(transCtx, session, ancestorWallet) updateErr := l.AgentWalletModel.UpdateWithVersion(transCtx, session, ancestorWallet)
if updateErr != nil { if updateErr != nil {
return updateErr return updateErr
} }
// 获取新插入的佣金记录ID
commissionID, err := insertResult.LastInsertId()
if err != nil {
return err
}
commissionIDStr := fmt.Sprintf("%d", commissionID) // 转换为字符串
// 记录交易流水(佣金收入)
transErr := l.CreateWalletTransaction(
transCtx,
session,
AncestorId,
model.WalletTransactionTypeCommission,
ancestorCommissionAmount, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
ancestorWallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
ancestorWallet.FrozenBalance, // 变动后冻结余额
order.OrderNo, // 关联交易ID订单号
commissionID, // 关联佣金记录ID
fmt.Sprintf("订单佣金收入佣金记录ID: %s", commissionIDStr), // 备注包含佣金记录ID
)
if transErr != nil {
return transErr
}
} }
} }
@@ -161,6 +225,39 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return transErr return transErr
} }
// 在事务提交后,仅在安全防御模式下触发解冻任务
// 注意:这里发送的是任务,实际解冻将在指定时间后由队列处理
if l.AsynqService != nil && l.config.SystemConfig.CommissionSafeMode {
// 仅在安全防御模式下,才需要发送解冻任务
// 获取刚创建的佣金记录ID
// 由于我们需要佣金记录ID来触发解冻任务但事务中无法获取我们可以在事务后查询
builder := l.AgentCommissionModel.SelectBuilder().
Where("order_id = ?", order.Id).
Where("status = ?", 1). // 只查询状态为冻结的佣金
Where("del_state = ?", globalkey.DelStateNo)
commissions, findErr := l.AgentCommissionModel.FindAll(ctx, builder, "")
if findErr != nil {
logx.Errorf("查询刚创建的佣金记录失败订单ID: %d, 错误: %v", order.Id, findErr)
return findErr
}
if len(commissions) > 0 {
// 为所有新创建的冻结佣金记录触发解冻任务
for _, commission := range commissions {
// 发送解冻任务将在10小时后执行
sendTaskErr := l.AsynqService.SendUnfreezeCommissionTask(commission.Id)
if sendTaskErr != nil {
logx.Errorf("发送佣金解冻任务失败佣金ID: %d, 错误: %v", commission.Id, sendTaskErr)
// 不返回错误,因为佣金记录已创建成功,只是解冻任务失败
} else {
logx.Infof("已发送佣金解冻任务佣金ID: %d, 代理ID: %d, 金额: %.2f",
commission.Id, commission.AgentId, commission.Amount)
}
}
}
}
return nil return nil
} }
@@ -172,24 +269,71 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
} }
// 推广人最终获得代理佣金 // 推广人最终获得代理佣金
finalCommission := order.Amount - deductedAmount finalCommission := order.Amount - deductedAmount
// 记录变动前的余额
balanceBefore := agentWalletModel.Balance
frozenBalanceBefore := agentWalletModel.FrozenBalance
// 根据安全防御模式配置决定佣金状态和钱包操作
if l.config.SystemConfig.CommissionSafeMode {
// 安全防御模式佣金冻结在frozen_balance中
agentWalletModel.FrozenBalance += finalCommission
} else {
// 非安全防御模式佣金直接进入balance
agentWalletModel.Balance += finalCommission agentWalletModel.Balance += finalCommission
}
agentWalletModel.TotalEarnings += finalCommission agentWalletModel.TotalEarnings += finalCommission
// 根据安全防御模式配置决定佣金状态
commissionStatus := int64(1) // 默认为冻结状态
if !l.config.SystemConfig.CommissionSafeMode {
commissionStatus = 0 // 非安全模式直接设置为已结算
}
agentCommission := model.AgentCommission{ agentCommission := model.AgentCommission{
AgentId: agentID, AgentId: agentID,
OrderId: order.Id, OrderId: order.Id,
Amount: finalCommission, Amount: finalCommission,
ProductId: order.ProductId, ProductId: order.ProductId,
Status: commissionStatus,
} }
_, insertAgentCommissionErr := l.AgentCommissionModel.Insert(ctx, session, &agentCommission) insertResult, insertAgentCommissionErr := l.AgentCommissionModel.Insert(ctx, session, &agentCommission)
if insertAgentCommissionErr != nil { if insertAgentCommissionErr != nil {
return insertAgentCommissionErr return insertAgentCommissionErr
} }
// 获取新插入的佣金记录ID用于日志记录
commissionID, err := insertResult.LastInsertId()
if err != nil {
return err
}
_ = commissionID // 暂时忽略该变量,因为我们使用其他方式获取佣金记录
// 更新钱包
updateAgentWalletErr := l.AgentWalletModel.UpdateWithVersion(ctx, session, agentWalletModel) updateAgentWalletErr := l.AgentWalletModel.UpdateWithVersion(ctx, session, agentWalletModel)
if updateAgentWalletErr != nil { if updateAgentWalletErr != nil {
return updateAgentWalletErr return updateAgentWalletErr
} }
// 记录交易流水(佣金收入)
transErr := l.CreateWalletTransaction(
ctx,
session,
agentID,
model.WalletTransactionTypeCommission,
finalCommission, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
agentWalletModel.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
agentWalletModel.FrozenBalance, // 变动后冻结余额
order.OrderNo, // 关联交易ID订单号
0, // 关联用户ID
"订单佣金收入", // 备注
)
if transErr != nil {
return transErr
}
return nil return nil
} }
@@ -270,7 +414,7 @@ func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, order
} }
// CommissionCost 上级底价成本 // CommissionCost 上级底价成本
func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, session sqlx.Session) (float64, error) { func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, orderId int64, session sqlx.Session) (float64, error) {
if agentMembershipConfigModel.PriceIncreaseAmount.Valid { if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
// 拥有则查看该上级设定的成本 // 拥有则查看该上级设定的成本
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID) agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
@@ -290,6 +434,7 @@ func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, A
Amount: deductCostAmount, Amount: deductCostAmount,
Type: model.AgentDeductionTypeCost, Type: model.AgentDeductionTypeCost,
ProductId: productID, ProductId: productID,
OrderId: sql.NullInt64{Int64: orderId, Valid: true},
} }
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel) _, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
@@ -303,7 +448,7 @@ func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, A
} }
// CommissionPricing 上级提价成本 // CommissionPricing 上级提价成本
func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, pricing float64, session sqlx.Session) (float64, error) { func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, pricing float64, orderId int64, session sqlx.Session) (float64, error) {
//看上级代理等级否有拥有定价标准收益功能 //看上级代理等级否有拥有定价标准收益功能
if agentMembershipConfigModel.PriceIncreaseMax.Valid && agentMembershipConfigModel.PriceRatio.Valid { if agentMembershipConfigModel.PriceIncreaseMax.Valid && agentMembershipConfigModel.PriceRatio.Valid {
// 拥有则查看该上级设定的成本 // 拥有则查看该上级设定的成本
@@ -334,6 +479,7 @@ func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64
Amount: deductCostAmount, Amount: deductCostAmount,
Type: model.AgentDeductionTypePricing, Type: model.AgentDeductionTypePricing,
ProductId: productID, ProductId: productID,
OrderId: sql.NullInt64{Int64: orderId, Valid: true},
} }
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel) _, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
if insertAgentCommissionDeductionModelErr != nil { if insertAgentCommissionDeductionModelErr != nil {
@@ -432,7 +578,7 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old
Type: rewardType, Type: rewardType,
} }
_, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards) insertResult, err := l.AgentRewardsModel.Insert(ctx, session, &agentRewards)
if err != nil { if err != nil {
return err return err
} }
@@ -443,12 +589,42 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old
return err return err
} }
// 记录变动前的余额
balanceBefore := ancestorWallet.Balance
frozenBalanceBefore := ancestorWallet.FrozenBalance
ancestorWallet.Balance += rewardAmount ancestorWallet.Balance += rewardAmount
ancestorWallet.TotalEarnings += rewardAmount ancestorWallet.TotalEarnings += rewardAmount
err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet) err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet)
if err != nil { if err != nil {
return err return err
} }
// 获取新插入的奖励记录ID
rewardID, err := insertResult.LastInsertId()
if err != nil {
return err
}
rewardIDStr := fmt.Sprintf("%d", rewardID) // 转换为字符串
// 记录交易流水(奖励收入)
transErr := l.CreateWalletTransaction(
ctx,
session,
ancestorID,
model.WalletTransactionTypeReward,
rewardAmount, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
ancestorWallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
ancestorWallet.FrozenBalance, // 变动后冻结余额
rewardIDStr, // 关联交易ID奖励记录ID
agentID, // 关联用户ID下级代理ID
"下级升级奖励", // 备注
)
if transErr != nil {
return transErr
}
} }
return nil return nil
@@ -512,7 +688,7 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi
Type: model.AgentRewardsTypeDescendantWithdraw, Type: model.AgentRewardsTypeDescendantWithdraw,
} }
_, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards) insertResult, err := l.AgentRewardsModel.Insert(ctx, session, &agentRewards)
if err != nil { if err != nil {
return err return err
} }
@@ -523,12 +699,42 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi
return err return err
} }
// 记录变动前的余额
balanceBefore := ancestorWallet.Balance
frozenBalanceBefore := ancestorWallet.FrozenBalance
ancestorWallet.Balance += rewardAmount ancestorWallet.Balance += rewardAmount
ancestorWallet.TotalEarnings += rewardAmount ancestorWallet.TotalEarnings += rewardAmount
err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet) err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet)
if err != nil { if err != nil {
return err return err
} }
// 获取新插入的奖励记录ID
rewardID, err := insertResult.LastInsertId()
if err != nil {
return err
}
rewardIDStr := fmt.Sprintf("%d", rewardID) // 转换为字符串
// 记录交易流水(奖励收入)
transErr := l.CreateWalletTransaction(
ctx,
session,
ancestorID,
model.WalletTransactionTypeReward,
rewardAmount, // 变动金额(正数表示增加)
balanceBefore, // 变动前余额
ancestorWallet.Balance, // 变动后余额
frozenBalanceBefore, // 变动前冻结余额
ancestorWallet.FrozenBalance, // 变动后冻结余额
rewardIDStr, // 关联交易ID奖励记录ID
agentID, // 关联用户ID下级代理ID
"下级提现奖励", // 备注
)
if transErr != nil {
return transErr
}
} }
} }
@@ -588,3 +794,57 @@ func (l *AgentService) RetryAgentProcess(ctx context.Context, orderID int64) err
// 执行代理处理 // 执行代理处理
return l.AgentProcess(ctx, order) return l.AgentProcess(ctx, order)
} }
// CreateWalletTransaction 创建代理钱包流水记录
// ctx: 上下文
// session: 数据库会话(事务)
// agentID: 代理ID
// transactionType: 交易类型 (commission/withdraw/freeze/unfreeze/reward/refund/adjust)
// amount: 变动金额(正数为增加,负数为减少)
// balanceBefore: 变动前余额
// balanceAfter: 变动后余额
// frozenBalanceBefore: 变动前冻结余额
// frozenBalanceAfter: 变动后冻结余额
// transactionID: 关联交易ID订单号、提现申请号等
// relatedUserID: 关联用户ID如佣金来源用户
// remark: 备注说明
func (l *AgentService) CreateWalletTransaction(ctx context.Context, session sqlx.Session,
agentID int64, transactionType string, amount float64,
balanceBefore, balanceAfter, frozenBalanceBefore, frozenBalanceAfter float64,
transactionID string, relatedUserID int64, remark string) error {
// 处理可空字段
var transactionIDField sql.NullString
if transactionID != "" {
transactionIDField = sql.NullString{String: transactionID, Valid: true}
}
var relatedUserIDField sql.NullInt64
if relatedUserID > 0 {
relatedUserIDField = sql.NullInt64{Int64: relatedUserID, Valid: true}
}
var remarkField sql.NullString
if remark != "" {
remarkField = sql.NullString{String: remark, Valid: true}
}
transaction := &model.AgentWalletTransaction{
AgentId: agentID,
TransactionType: transactionType,
Amount: amount,
BalanceBefore: balanceBefore,
BalanceAfter: balanceAfter,
FrozenBalanceBefore: frozenBalanceBefore,
FrozenBalanceAfter: frozenBalanceAfter,
TransactionId: transactionIDField,
RelatedUserId: relatedUserIDField,
Remark: remarkField,
}
_, err := l.AgentWalletTransactionModel.Insert(ctx, session, transaction)
if err != nil {
return errors.Wrapf(err, "创建代理钱包流水记录失败agentID: %d, type: %s, amount: %.2f", agentID, transactionType, amount)
}
return nil
}

View File

@@ -1434,6 +1434,7 @@ func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error
return convertTianyuanResponse(resp) return convertTianyuanResponse(resp)
} }
// ProcessFLXG7E8FRequest 个人涉诉 // ProcessFLXG7E8FRequest 个人涉诉
func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error) { func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card") idCard := gjson.GetBytes(params, "id_card")

View File

@@ -3,9 +3,11 @@
package service package service
import ( import (
"encoding/json"
"time"
"tydata-server/app/main/api/internal/config" "tydata-server/app/main/api/internal/config"
"tydata-server/app/main/api/internal/types" "tydata-server/app/main/api/internal/types"
"encoding/json"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
@@ -58,3 +60,33 @@ func (s *AsynqService) SendQueryTask(orderID int64) error {
logx.Infof("发送异步任务成功任务ID: %s, 队列: %s, 订单号: %d", info.ID, info.Queue, orderID) logx.Infof("发送异步任务成功任务ID: %s, 队列: %s, 订单号: %d", info.ID, info.Queue, orderID)
return nil return nil
} }
// SendUnfreezeCommissionTask 发送佣金解冻任务
func (s *AsynqService) SendUnfreezeCommissionTask(commissionID int64) error {
// 准备任务的 payload
payload := types.MsgUnfreezeCommissionPayload{
CommissionID: commissionID,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
logx.Errorf("发送佣金解冻任务失败 (无法编码 payload): %v, 佣金ID: %d", err, commissionID)
return err
}
options := []asynq.Option{
asynq.ProcessIn(time.Duration(s.config.ExtensionTime) * time.Hour), // 10小时后执行
asynq.MaxRetry(5), // 设置最大重试次数
}
task := asynq.NewTask(types.MsgUnfreezeCommission, payloadBytes, options...)
// 将任务加入队列并获取任务信息
info, err := s.client.Enqueue(task)
if err != nil {
logx.Errorf("发送佣金解冻任务失败 (加入队列失败): %+v, 佣金ID: %d", err, commissionID)
return err
}
// 记录成功日志,带上任务 ID 和队列信息
logx.Infof("发送佣金解冻任务成功任务ID: %s, 队列: %s, 佣金ID: %d", info.ID, info.Queue, commissionID)
return nil
}

View File

@@ -206,8 +206,35 @@ func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amo
if err != nil { if err != nil {
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode) return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
} }
// 返回预支付交易会话标识 // 显式转为 map确保小程序 uni.requestPayment 能正确解析(避免指针序列化问题)
return resp, nil return jsapiRespToMap(resp), nil
}
// jsapiRespToMap 将 PrepayWithRequestPaymentResponse 转为 map供小程序/JSAPI 调起支付
func jsapiRespToMap(resp *jsapi.PrepayWithRequestPaymentResponse) map[string]string {
m := make(map[string]string)
if resp == nil {
return m
}
if resp.Appid != nil {
m["appId"] = *resp.Appid
}
if resp.TimeStamp != nil {
m["timeStamp"] = *resp.TimeStamp
}
if resp.NonceStr != nil {
m["nonceStr"] = *resp.NonceStr
}
if resp.Package != nil {
m["package"] = *resp.Package
}
if resp.SignType != nil {
m["signType"] = *resp.SignType
}
if resp.PaySign != nil {
m["paySign"] = *resp.PaySign
}
return m
} }
// CreateWechatH5Order 创建微信H5支付订单 // CreateWechatH5Order 创建微信H5支付订单
@@ -237,8 +264,7 @@ func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float
if err != nil { if err != nil {
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode) return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
} }
// 返回预支付交易会话标识 return jsapiRespToMap(resp), nil
return resp, nil
} }
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序 // CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序

View File

@@ -39,6 +39,7 @@ type ServiceContext struct {
OrderModel model.OrderModel OrderModel model.OrderModel
OrderRefundModel model.OrderRefundModel OrderRefundModel model.OrderRefundModel
QueryModel model.QueryModel QueryModel model.QueryModel
QueryUserRecordModel model.QueryUserRecordModel
QueryCleanupLogModel model.QueryCleanupLogModel QueryCleanupLogModel model.QueryCleanupLogModel
QueryCleanupDetailModel model.QueryCleanupDetailModel QueryCleanupDetailModel model.QueryCleanupDetailModel
QueryCleanupConfigModel model.QueryCleanupConfigModel QueryCleanupConfigModel model.QueryCleanupConfigModel
@@ -63,6 +64,7 @@ type ServiceContext struct {
AgentRealNameModel model.AgentRealNameModel AgentRealNameModel model.AgentRealNameModel
AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel
AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel
AgentWalletTransactionModel model.AgentWalletTransactionModel
// 管理后台相关模型 // 管理后台相关模型
AdminApiModel model.AdminApiModel AdminApiModel model.AdminApiModel
@@ -127,6 +129,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
// ============================== 订单相关模型 ============================== // ============================== 订单相关模型 ==============================
orderModel := model.NewOrderModel(db, cacheConf) orderModel := model.NewOrderModel(db, cacheConf)
queryModel := model.NewQueryModel(db, cacheConf) queryModel := model.NewQueryModel(db, cacheConf)
queryUserRecordModel := model.NewQueryUserRecordModel(db, cacheConf)
orderRefundModel := model.NewOrderRefundModel(db, cacheConf) orderRefundModel := model.NewOrderRefundModel(db, cacheConf)
queryCleanupLogModel := model.NewQueryCleanupLogModel(db, cacheConf) queryCleanupLogModel := model.NewQueryCleanupLogModel(db, cacheConf)
queryCleanupDetailModel := model.NewQueryCleanupDetailModel(db, cacheConf) queryCleanupDetailModel := model.NewQueryCleanupDetailModel(db, cacheConf)
@@ -152,6 +155,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
agentRealNameModel := model.NewAgentRealNameModel(db, cacheConf) agentRealNameModel := model.NewAgentRealNameModel(db, cacheConf)
agentWithdrawalTaxModel := model.NewAgentWithdrawalTaxModel(db, cacheConf) agentWithdrawalTaxModel := model.NewAgentWithdrawalTaxModel(db, cacheConf)
agentWithdrawalTaxExemptionModel := model.NewAgentWithdrawalTaxExemptionModel(db, cacheConf) agentWithdrawalTaxExemptionModel := model.NewAgentWithdrawalTaxExemptionModel(db, cacheConf)
agentWalletTransactionModel := model.NewAgentWalletTransactionModel(db, cacheConf)
// ============================== 管理后台相关模型 ============================== // ============================== 管理后台相关模型 ==============================
adminApiModel := model.NewAdminApiModel(db, cacheConf) adminApiModel := model.NewAdminApiModel(db, cacheConf)
adminMenuModel := model.NewAdminMenuModel(db, cacheConf) adminMenuModel := model.NewAdminMenuModel(db, cacheConf)
@@ -194,7 +198,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
agentCommissionModel, agentCommissionDeductionModel, agentWalletModel, agentLinkModel, agentCommissionModel, agentCommissionDeductionModel, agentWalletModel, agentLinkModel,
agentOrderModel, agentRewardsModel, agentMembershipConfigModel, agentMembershipRechargeOrderModel, agentOrderModel, agentRewardsModel, agentMembershipConfigModel, agentMembershipRechargeOrderModel,
agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel, agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel,
agentActiveStatModel, agentWithdrawalModel) agentActiveStatModel, agentWithdrawalModel, agentWalletTransactionModel, asynqService)
userService := service.NewUserService(&c, userModel, userAuthModel, userTempModel, agentModel) userService := service.NewUserService(&c, userModel, userAuthModel, userTempModel, agentModel)
dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel) dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel)
adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel, adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel,
@@ -219,7 +223,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
Config: c, Config: c,
Redis: redisClient, Redis: redisClient,
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle, AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
UserAuthInterceptor: middleware.NewUserAuthInterceptorMiddleware().Handle, UserAuthInterceptor: middleware.NewUserAuthInterceptorMiddleware(userModel).Handle,
AdminAuthInterceptor: middleware.NewAdminAuthInterceptorMiddleware(c, AdminAuthInterceptor: middleware.NewAdminAuthInterceptorMiddleware(c,
adminUserModel, adminUserRoleModel, adminRoleModel, adminApiModel, adminRoleApiModel).Handle, adminUserModel, adminUserRoleModel, adminRoleModel, adminApiModel, adminRoleApiModel).Handle,
@@ -236,6 +240,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
// 订单相关模型 // 订单相关模型
OrderModel: orderModel, OrderModel: orderModel,
QueryModel: queryModel, QueryModel: queryModel,
QueryUserRecordModel: queryUserRecordModel,
OrderRefundModel: orderRefundModel, OrderRefundModel: orderRefundModel,
QueryCleanupLogModel: queryCleanupLogModel, QueryCleanupLogModel: queryCleanupLogModel,
QueryCleanupDetailModel: queryCleanupDetailModel, QueryCleanupDetailModel: queryCleanupDetailModel,
@@ -261,6 +266,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
AgentRealNameModel: agentRealNameModel, AgentRealNameModel: agentRealNameModel,
AgentWithdrawalTaxModel: agentWithdrawalTaxModel, AgentWithdrawalTaxModel: agentWithdrawalTaxModel,
AgentWithdrawalTaxExemptionModel: agentWithdrawalTaxExemptionModel, AgentWithdrawalTaxExemptionModel: agentWithdrawalTaxExemptionModel,
AgentWalletTransactionModel: agentWalletTransactionModel,
// 管理后台相关模型 // 管理后台相关模型
AdminApiModel: adminApiModel, AdminApiModel: adminApiModel,

View File

@@ -3,3 +3,7 @@ package types
type MsgPaySuccessQueryPayload struct { type MsgPaySuccessQueryPayload struct {
OrderID int64 `json:"order_id"` OrderID int64 `json:"order_id"`
} }
type MsgUnfreezeCommissionPayload struct {
CommissionID int64 `json:"commission_id"`
}

View File

@@ -26,7 +26,7 @@ type CompanyInfoReq struct {
Name string `json:"name" validate:"required,name"` Name string `json:"name" validate:"required,name"`
IDCard string `json:"id_card" validate:"required,idCard"` IDCard string `json:"id_card" validate:"required,idCard"`
Mobile string `json:"mobile" validate:"required,mobile"` Mobile string `json:"mobile" validate:"required,mobile"`
Code string `json:"code" validate:"required"` // Code string `json:"code" validate:"required"` // 暂不校验验证码
} }
// RentalInfo 查询请求结构 // RentalInfo 查询请求结构

View File

@@ -2,3 +2,4 @@ package types
const MsgPaySuccessQuery = "msg:pay_success:query" const MsgPaySuccessQuery = "msg:pay_success:query"
const MsgCleanQueryData = "msg:clean_query_data" const MsgCleanQueryData = "msg:clean_query_data"
const MsgUnfreezeCommission = "msg:unfreeze_commission"

View File

@@ -36,6 +36,16 @@ type AdminAssignRoleApiResp struct {
Success bool `json:"success"` Success bool `json:"success"`
} }
type AdminBatchUnfreezeAgentCommissionReq struct {
AgentId *int64 `json:"agent_id,optional"` // 代理ID可选。如果不传则解冻所有冻结中的佣金
}
type AdminBatchUnfreezeAgentCommissionResp struct {
Success bool `json:"success"` // 是否成功
Count int64 `json:"count"` // 解冻的数量
Amount float64 `json:"amount"` // 解冻的总金额
}
type AdminBatchUpdateApiStatusReq struct { type AdminBatchUpdateApiStatusReq struct {
Ids []int64 `json:"ids"` Ids []int64 `json:"ids"`
Status int64 `json:"status"` Status int64 `json:"status"`
@@ -217,8 +227,11 @@ type AdminGetAgentCommissionListReq struct {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选 AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
OrderId *int64 `form:"order_id,optional"` // 订单ID可选
ProductName *string `form:"product_name,optional"` // 产品名(可选) ProductName *string `form:"product_name,optional"` // 产品名(可选)
Status *int64 `form:"status,optional"` // 状态(可选) Status *int64 `form:"status,optional"` // 状态(可选)
CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选)
CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选)
} }
type AdminGetAgentCommissionListResp struct { type AdminGetAgentCommissionListResp struct {
@@ -340,12 +353,37 @@ type AdminGetAgentStatisticsResp struct {
TodayAgentCount int64 `json:"today_agent_count"` // 今日新增代理数 TodayAgentCount int64 `json:"today_agent_count"` // 今日新增代理数
} }
type AdminGetAgentWalletReq struct {
AgentId int64 `path:"agent_id"` // 代理ID
}
type AdminGetAgentWalletResp struct {
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 总收益
}
type AdminGetAgentWalletTransactionListReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量
AgentId int64 `form:"agent_id"` // 代理ID
TransactionType *string `form:"transaction_type,optional"` // 交易类型(可选)
CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选)
CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选)
}
type AdminGetAgentWalletTransactionListResp struct {
Total int64 `json:"total"` // 总数
Items []AgentWalletTransactionListItem `json:"items"` // 列表数据
}
type AdminGetAgentWithdrawalListReq struct { type AdminGetAgentWithdrawalListReq struct {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选 AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
Status *int64 `form:"status,optional"` // 状态(可选) Status *int64 `form:"status,optional"` // 状态(可选)
WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选) WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选)
WithdrawType *int64 `form:"withdraw_type,optional"` // 提现类型可选1-支付宝,2-银行卡
} }
type AdminGetAgentWithdrawalListResp struct { type AdminGetAgentWithdrawalListResp struct {
@@ -495,6 +533,9 @@ type AdminGetOrderListReq struct {
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始 RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束 RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
SalesCost float64 `form:"sales_cost,optional"` // 成本价 SalesCost float64 `form:"sales_cost,optional"` // 成本价
QueryName string `form:"query_name,optional"` // 被查询人姓名(通过 query_user_record 表追溯订单)
QueryIdCard string `form:"query_id_card,optional"` // 被查询人身份证(通过 query_user_record 表追溯订单)
QueryMobile string `form:"query_mobile,optional"` // 被查询人手机号(通过 query_user_record 表追溯订单)
} }
type AdminGetOrderListResp struct { type AdminGetOrderListResp struct {
@@ -527,6 +568,7 @@ type AdminGetPlatformUserDetailResp struct {
Nickname string `json:"nickname"` // 昵称 Nickname string `json:"nickname"` // 昵称
Info string `json:"info"` // 备注信息 Info string `json:"info"` // 备注信息
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否 Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
Disable int64 `json:"disable"` // 封禁状态 0-可用 1-禁用
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间 UpdateTime string `json:"update_time"` // 更新时间
} }
@@ -667,6 +709,10 @@ type AdminGetRoleApiListResp struct {
Items []AdminRoleApiInfo `json:"items"` Items []AdminRoleApiInfo `json:"items"`
} }
type AdminGetSystemConfigResp struct {
CommissionSafeMode bool `json:"commission_safe_mode"` // 佣金安全防御模式
}
type AdminGetUserDetailReq struct { type AdminGetUserDetailReq struct {
Id int64 `path:"id"` // 用户ID Id int64 `path:"id"` // 用户ID
} }
@@ -784,6 +830,15 @@ type AdminRoleApiInfo struct {
Description string `json:"description"` Description string `json:"description"`
} }
type AdminUpdateAgentCommissionStatusReq struct {
Id int64 `json:"id"` // 佣金记录ID
Status int64 `json:"status"` // 状态:0-已结算,1-冻结中,2-已取消
}
type AdminUpdateAgentCommissionStatusResp struct {
Success bool `json:"success"` // 是否成功
}
type AdminUpdateAgentMembershipConfigReq struct { type AdminUpdateAgentMembershipConfigReq struct {
Id int64 `json:"id"` // 主键 Id int64 `json:"id"` // 主键
LevelName string `json:"level_name"` // 会员级别名称 LevelName string `json:"level_name"` // 会员级别名称
@@ -819,6 +874,16 @@ type AdminUpdateAgentProductionConfigResp struct {
Success bool `json:"success"` // 是否成功 Success bool `json:"success"` // 是否成功
} }
type AdminUpdateAgentWalletBalanceReq struct {
AgentId int64 `json:"agent_id"` // 代理ID
Amount float64 `json:"amount"` // 修改金额(正数增加,负数减少)
}
type AdminUpdateAgentWalletBalanceResp struct {
Success bool `json:"success"` // 是否成功
Balance float64 `json:"balance"` // 修改后的余额
}
type AdminUpdateApiReq struct { type AdminUpdateApiReq struct {
Id int64 `path:"id"` Id int64 `path:"id"`
ApiName string `json:"api_name"` ApiName string `json:"api_name"`
@@ -885,6 +950,7 @@ type AdminUpdatePlatformUserReq struct {
Nickname *string `json:"nickname,optional"` // 昵称 Nickname *string `json:"nickname,optional"` // 昵称
Info *string `json:"info,optional"` // 备注信息 Info *string `json:"info,optional"` // 备注信息
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否 Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
Disable *int64 `json:"disable,optional"` // 封禁状态 0-可用 1-禁用
} }
type AdminUpdatePlatformUserResp struct { type AdminUpdatePlatformUserResp struct {
@@ -933,6 +999,14 @@ type AdminUpdateRoleApiResp struct {
Success bool `json:"success"` Success bool `json:"success"`
} }
type AdminUpdateSystemConfigReq struct {
CommissionSafeMode *bool `json:"commission_safe_mode,optional"` // 佣金安全防御模式true-冻结模式false-直接结算模式
}
type AdminUpdateSystemConfigResp struct {
Success bool `json:"success"` // 是否成功
}
type AdminUpdateUserReq struct { type AdminUpdateUserReq struct {
Id int64 `path:"id"` // 用户ID Id int64 `path:"id"` // 用户ID
Username *string `json:"username,optional"` // 用户名 Username *string `json:"username,optional"` // 用户名
@@ -1206,6 +1280,21 @@ type AgentSubordinateList struct {
TotalContribution float64 `json:"total_contribution"` // 总贡献 TotalContribution float64 `json:"total_contribution"` // 总贡献
} }
type AgentWalletTransactionListItem struct {
Id int64 `json:"id"` // 主键
AgentId int64 `json:"agent_id"` // 代理ID
TransactionType string `json:"transaction_type"` // 交易类型
Amount float64 `json:"amount"` // 变动金额
BalanceBefore float64 `json:"balance_before"` // 变动前余额
BalanceAfter float64 `json:"balance_after"` // 变动后余额
FrozenBalanceBefore float64 `json:"frozen_balance_before"` // 变动前冻结余额
FrozenBalanceAfter float64 `json:"frozen_balance_after"` // 变动后冻结余额
TransactionId *string `json:"transaction_id"` // 关联交易ID
RelatedUserId *int64 `json:"related_user_id"` // 关联用户ID
Remark *string `json:"remark"` // 备注说明
CreateTime string `json:"create_time"` // 创建时间
}
type AgentWithdrawalListItem struct { type AgentWithdrawalListItem struct {
Id int64 `json:"id"` // 主键 Id int64 `json:"id"` // 主键
AgentId int64 `json:"agent_id"` // 代理ID AgentId int64 `json:"agent_id"` // 代理ID
@@ -1256,7 +1345,10 @@ type BindMobileResp struct {
type Commission struct { type Commission struct {
OrderId string `json:"order_id"` // 订单号 OrderId string `json:"order_id"` // 订单号
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
Amount float64 `json:"amount"` Amount float64 `json:"amount"` // 原始佣金金额
RefundedAmount float64 `json:"refunded_amount"` // 已退款佣金金额
NetAmount float64 `json:"net_amount"` // 剩余净佣金金额 = amount - refunded_amount
Status int64 `json:"status"` // 状态0-已结算1-冻结中2-已退款
CreateTime string `json:"create_time"` CreateTime string `json:"create_time"`
QueryParams map[string]interface{} `json:"query_params,omitempty"` QueryParams map[string]interface{} `json:"query_params,omitempty"`
} }
@@ -1749,7 +1841,7 @@ type PaymentCheckResp struct {
type PaymentReq struct { type PaymentReq struct {
Id string `json:"id"` Id string `json:"id"`
PayMethod string `json:"pay_method"` PayMethod string `json:"pay_method"`
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"` PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
} }
type PaymentResp struct { type PaymentResp struct {
@@ -1764,6 +1856,7 @@ type PlatformUserListItem struct {
Nickname string `json:"nickname"` // 昵称 Nickname string `json:"nickname"` // 昵称
Info string `json:"info"` // 备注信息 Info string `json:"info"` // 备注信息
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否 Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
Disable int64 `json:"disable"` // 封禁状态 0-可用 1-禁用
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间 UpdateTime string `json:"update_time"` // 更新时间
} }
@@ -2128,5 +2221,6 @@ type GetAppVersionResp struct {
type SendSmsReq struct { type SendSmsReq struct {
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"` CaptchaVerifyParam string `json:"captchaVerifyParam"`
ActionType string `json:"actionType" validate:"omitempty,oneof=login register query agentApply realName bindMobile"`
} }

View File

@@ -2,8 +2,8 @@ package model
import ( import (
"context" "context"
"tydata-server/common/globalkey"
"fmt" "fmt"
"tydata-server/common/globalkey"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/cache"

View File

@@ -27,7 +27,7 @@ var (
agentCommissionDeductionRowsExpectAutoSet = strings.Join(stringx.Remove(agentCommissionDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), ",") agentCommissionDeductionRowsExpectAutoSet = strings.Join(stringx.Remove(agentCommissionDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
agentCommissionDeductionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentCommissionDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" agentCommissionDeductionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentCommissionDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmAgentCommissionDeductionIdPrefix = "cache:tydata:agentCommissionDeduction:id:" cacheTydataAgentCommissionDeductionIdPrefix = "cache:tydata:agentCommissionDeduction:id:"
) )
type ( type (
@@ -60,6 +60,7 @@ type (
DeductedAgentId int64 `db:"deducted_agent_id"` // 被抽佣代理ID DeductedAgentId int64 `db:"deducted_agent_id"` // 被抽佣代理ID
Amount float64 `db:"amount"` Amount float64 `db:"amount"`
ProductId int64 `db:"product_id"` // 产品ID ProductId int64 `db:"product_id"` // 产品ID
OrderId sql.NullInt64 `db:"order_id"` // 关联订单ID
Type string `db:"type"` Type string `db:"type"`
Status int64 `db:"status"` // 状态 Status int64 `db:"status"` // 状态
CreateTime time.Time `db:"create_time"` CreateTime time.Time `db:"create_time"`
@@ -79,20 +80,20 @@ func newAgentCommissionDeductionModel(conn sqlx.SqlConn, c cache.CacheConf) *def
func (m *defaultAgentCommissionDeductionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) (sql.Result, error) { func (m *defaultAgentCommissionDeductionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, data.Id) tydataAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, data.Id)
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, agentCommissionDeductionRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentCommissionDeductionRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version) return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version) return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version)
}, hmAgentCommissionDeductionIdKey) }, tydataAgentCommissionDeductionIdKey)
} }
func (m *defaultAgentCommissionDeductionModel) FindOne(ctx context.Context, id int64) (*AgentCommissionDeduction, error) { func (m *defaultAgentCommissionDeductionModel) FindOne(ctx context.Context, id int64) (*AgentCommissionDeduction, error) {
hmAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, id) tydataAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, id)
var resp AgentCommissionDeduction var resp AgentCommissionDeduction
err := m.QueryRowCtx(ctx, &resp, hmAgentCommissionDeductionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataAgentCommissionDeductionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionDeductionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionDeductionRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -107,14 +108,14 @@ func (m *defaultAgentCommissionDeductionModel) FindOne(ctx context.Context, id i
} }
func (m *defaultAgentCommissionDeductionModel) Update(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) (sql.Result, error) { func (m *defaultAgentCommissionDeductionModel) Update(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) (sql.Result, error) {
hmAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, data.Id) tydataAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, data.Id)
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, agentCommissionDeductionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentCommissionDeductionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
}, hmAgentCommissionDeductionIdKey) }, tydataAgentCommissionDeductionIdKey)
} }
func (m *defaultAgentCommissionDeductionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) error { func (m *defaultAgentCommissionDeductionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentCommissionDeduction) error {
@@ -125,14 +126,14 @@ func (m *defaultAgentCommissionDeductionModel) UpdateWithVersion(ctx context.Con
var sqlResult sql.Result var sqlResult sql.Result
var err error var err error
hmAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, data.Id) tydataAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, data.Id)
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, agentCommissionDeductionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentCommissionDeductionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return session.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return conn.ExecCtx(ctx, query, data.AgentId, data.DeductedAgentId, data.Amount, data.ProductId, data.OrderId, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
}, hmAgentCommissionDeductionIdKey) }, tydataAgentCommissionDeductionIdKey)
if err != nil { if err != nil {
return err return err
} }
@@ -350,18 +351,18 @@ func (m *defaultAgentCommissionDeductionModel) SelectBuilder() squirrel.SelectBu
return squirrel.Select().From(m.table) return squirrel.Select().From(m.table)
} }
func (m *defaultAgentCommissionDeductionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { func (m *defaultAgentCommissionDeductionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
hmAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, id) tydataAgentCommissionDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, id)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table) query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, id) return session.ExecCtx(ctx, query, id)
} }
return conn.ExecCtx(ctx, query, id) return conn.ExecCtx(ctx, query, id)
}, hmAgentCommissionDeductionIdKey) }, tydataAgentCommissionDeductionIdKey)
return err return err
} }
func (m *defaultAgentCommissionDeductionModel) formatPrimary(primary interface{}) string { func (m *defaultAgentCommissionDeductionModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmAgentCommissionDeductionIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataAgentCommissionDeductionIdPrefix, primary)
} }
func (m *defaultAgentCommissionDeductionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultAgentCommissionDeductionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionDeductionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionDeductionRows, m.table)

View File

@@ -10,8 +10,6 @@ import (
"time" "time"
"tydata-server/common/globalkey"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/builder" "github.com/zeromicro/go-zero/core/stores/builder"
@@ -19,6 +17,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"
"tydata-server/common/globalkey"
) )
var ( var (
@@ -27,7 +26,7 @@ var (
agentCommissionRowsExpectAutoSet = strings.Join(stringx.Remove(agentCommissionFieldNames, "`id`", "`create_time`", "`update_time`"), ",") agentCommissionRowsExpectAutoSet = strings.Join(stringx.Remove(agentCommissionFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
agentCommissionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentCommissionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" agentCommissionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentCommissionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmAgentCommissionIdPrefix = "cache:tydata:agentCommission:id:" cacheTydataAgentCommissionIdPrefix = "cache:tydata:agentCommission:id:"
) )
type ( type (
@@ -59,6 +58,7 @@ type (
AgentId int64 `db:"agent_id"` AgentId int64 `db:"agent_id"`
OrderId int64 `db:"order_id"` OrderId int64 `db:"order_id"`
Amount float64 `db:"amount"` Amount float64 `db:"amount"`
RefundedAmount float64 `db:"refunded_amount"` // 已退款佣金金额
ProductId int64 `db:"product_id"` // 产品ID ProductId int64 `db:"product_id"` // 产品ID
Status int64 `db:"status"` Status int64 `db:"status"`
CreateTime time.Time `db:"create_time"` CreateTime time.Time `db:"create_time"`
@@ -78,20 +78,20 @@ func newAgentCommissionModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgent
func (m *defaultAgentCommissionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentCommission) (sql.Result, error) { func (m *defaultAgentCommissionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentCommission) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, data.Id) tydataAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, data.Id)
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, agentCommissionRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentCommissionRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version) return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version) return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version)
}, hmAgentCommissionIdKey) }, tydataAgentCommissionIdKey)
} }
func (m *defaultAgentCommissionModel) FindOne(ctx context.Context, id int64) (*AgentCommission, error) { func (m *defaultAgentCommissionModel) FindOne(ctx context.Context, id int64) (*AgentCommission, error) {
hmAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, id) tydataAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, id)
var resp AgentCommission var resp AgentCommission
err := m.QueryRowCtx(ctx, &resp, hmAgentCommissionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataAgentCommissionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -106,14 +106,14 @@ func (m *defaultAgentCommissionModel) FindOne(ctx context.Context, id int64) (*A
} }
func (m *defaultAgentCommissionModel) Update(ctx context.Context, session sqlx.Session, data *AgentCommission) (sql.Result, error) { func (m *defaultAgentCommissionModel) Update(ctx context.Context, session sqlx.Session, data *AgentCommission) (sql.Result, error) {
hmAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, data.Id) tydataAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, data.Id)
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, agentCommissionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentCommissionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
}, hmAgentCommissionIdKey) }, tydataAgentCommissionIdKey)
} }
func (m *defaultAgentCommissionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentCommission) error { func (m *defaultAgentCommissionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentCommission) error {
@@ -124,14 +124,14 @@ func (m *defaultAgentCommissionModel) UpdateWithVersion(ctx context.Context, ses
var sqlResult sql.Result var sqlResult sql.Result
var err error var err error
hmAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, data.Id) tydataAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, data.Id)
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, agentCommissionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentCommissionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return session.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return conn.ExecCtx(ctx, query, data.AgentId, data.OrderId, data.Amount, data.RefundedAmount, data.ProductId, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
}, hmAgentCommissionIdKey) }, tydataAgentCommissionIdKey)
if err != nil { if err != nil {
return err return err
} }
@@ -349,18 +349,18 @@ func (m *defaultAgentCommissionModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table) return squirrel.Select().From(m.table)
} }
func (m *defaultAgentCommissionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { func (m *defaultAgentCommissionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
hmAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, id) tydataAgentCommissionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, id)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table) query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, id) return session.ExecCtx(ctx, query, id)
} }
return conn.ExecCtx(ctx, query, id) return conn.ExecCtx(ctx, query, id)
}, hmAgentCommissionIdKey) }, tydataAgentCommissionIdKey)
return err return err
} }
func (m *defaultAgentCommissionModel) formatPrimary(primary interface{}) string { func (m *defaultAgentCommissionModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmAgentCommissionIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataAgentCommissionIdPrefix, primary)
} }
func (m *defaultAgentCommissionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultAgentCommissionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentCommissionRows, m.table)

View File

@@ -0,0 +1,27 @@
package model
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ AgentWalletTransactionModel = (*customAgentWalletTransactionModel)(nil)
type (
// AgentWalletTransactionModel is an interface to be customized, add more methods here,
// and implement the added methods in customAgentWalletTransactionModel.
AgentWalletTransactionModel interface {
agentWalletTransactionModel
}
customAgentWalletTransactionModel struct {
*defaultAgentWalletTransactionModel
}
)
// NewAgentWalletTransactionModel returns a model for the database table.
func NewAgentWalletTransactionModel(conn sqlx.SqlConn, c cache.CacheConf) AgentWalletTransactionModel {
return &customAgentWalletTransactionModel{
defaultAgentWalletTransactionModel: newAgentWalletTransactionModel(conn, c),
}
}

View File

@@ -0,0 +1,377 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"tydata-server/common/globalkey"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
agentWalletTransactionFieldNames = builder.RawFieldNames(&AgentWalletTransaction{})
agentWalletTransactionRows = strings.Join(agentWalletTransactionFieldNames, ",")
agentWalletTransactionRowsExpectAutoSet = strings.Join(stringx.Remove(agentWalletTransactionFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
agentWalletTransactionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentWalletTransactionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheTydataAgentWalletTransactionIdPrefix = "cache:tydata:agentWalletTransaction:id:"
)
type (
agentWalletTransactionModel interface {
Insert(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*AgentWalletTransaction, error)
Update(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error
Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
SelectBuilder() squirrel.SelectBuilder
DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error
FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error)
FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error)
FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*AgentWalletTransaction, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWalletTransaction, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWalletTransaction, error)
Delete(ctx context.Context, session sqlx.Session, id int64) error
}
defaultAgentWalletTransactionModel struct {
sqlc.CachedConn
table string
}
AgentWalletTransaction struct {
Id int64 `db:"id"`
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"`
Version int64 `db:"version"` // 版本号
AgentId int64 `db:"agent_id"` // 代理ID
TransactionType string `db:"transaction_type"` // 交易类型:commission(佣金收入)、withdraw(提现)、freeze(冻结)、unfreeze(解冻)、reward(奖励)、refund(退款)、adjust(调整)等
Amount float64 `db:"amount"` // 变动金额(正数为增加,负数为减少)
BalanceBefore float64 `db:"balance_before"` // 变动前余额
BalanceAfter float64 `db:"balance_after"` // 变动后余额
FrozenBalanceBefore float64 `db:"frozen_balance_before"` // 变动前冻结余额
FrozenBalanceAfter float64 `db:"frozen_balance_after"` // 变动后冻结余额
TransactionId sql.NullString `db:"transaction_id"` // 关联交易ID(订单号、提现申请号等)
RelatedUserId sql.NullInt64 `db:"related_user_id"` // 关联用户ID(如佣金来源用户)
Remark sql.NullString `db:"remark"` // 备注说明
}
)
func newAgentWalletTransactionModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentWalletTransactionModel {
return &defaultAgentWalletTransactionModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`agent_wallet_transaction`",
}
}
func (m *defaultAgentWalletTransactionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id)
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, agentWalletTransactionRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark)
}, tydataAgentWalletTransactionIdKey)
}
func (m *defaultAgentWalletTransactionModel) FindOne(ctx context.Context, id int64) (*AgentWalletTransaction, error) {
tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, id)
var resp AgentWalletTransaction
err := m.QueryRowCtx(ctx, &resp, tydataAgentWalletTransactionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWalletTransactionRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultAgentWalletTransactionModel) Update(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) (sql.Result, error) {
tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id)
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, agentWalletTransactionRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id)
}, tydataAgentWalletTransactionIdKey)
}
func (m *defaultAgentWalletTransactionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, data.Id)
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, agentWalletTransactionRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.AgentId, data.TransactionType, data.Amount, data.BalanceBefore, data.BalanceAfter, data.FrozenBalanceBefore, data.FrozenBalanceAfter, data.TransactionId, data.RelatedUserId, data.Remark, data.Id, oldVersion)
}, tydataAgentWalletTransactionIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultAgentWalletTransactionModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWalletTransaction) error {
data.DelState = globalkey.DelStateYes
data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
return errors.Wrapf(errors.New("delete soft failed "), "AgentWalletTransactionModel delete err : %+v", err)
}
return nil
}
func (m *defaultAgentWalletTransactionModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field")
}
builder = builder.Columns("IFNULL(SUM(" + field + "),0)")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp float64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultAgentWalletTransactionModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field")
}
builder = builder.Columns("COUNT(" + field + ")")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp int64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultAgentWalletTransactionModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AgentWalletTransaction, error) {
builder = builder.Columns(agentWalletTransactionRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return nil, err
}
var resp []*AgentWalletTransaction
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAgentWalletTransactionModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, error) {
builder = builder.Columns(agentWalletTransactionRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*AgentWalletTransaction
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAgentWalletTransactionModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWalletTransaction, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(agentWalletTransactionRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, total, err
}
var resp []*AgentWalletTransaction
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultAgentWalletTransactionModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWalletTransaction, error) {
builder = builder.Columns(agentWalletTransactionRows)
if preMinId > 0 {
builder = builder.Where(" id < ? ", preMinId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*AgentWalletTransaction
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAgentWalletTransactionModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWalletTransaction, error) {
builder = builder.Columns(agentWalletTransactionRows)
if preMaxId > 0 {
builder = builder.Where(" id > ? ", preMaxId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*AgentWalletTransaction
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAgentWalletTransactionModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
return fn(ctx, session)
})
}
func (m *defaultAgentWalletTransactionModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultAgentWalletTransactionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
tydataAgentWalletTransactionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, id)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil {
return session.ExecCtx(ctx, query, id)
}
return conn.ExecCtx(ctx, query, id)
}, tydataAgentWalletTransactionIdKey)
return err
}
func (m *defaultAgentWalletTransactionModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheTydataAgentWalletTransactionIdPrefix, primary)
}
func (m *defaultAgentWalletTransactionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWalletTransactionRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultAgentWalletTransactionModel) tableName() string {
return m.table
}

View File

@@ -10,8 +10,6 @@ import (
"time" "time"
"tydata-server/common/globalkey"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/builder" "github.com/zeromicro/go-zero/core/stores/builder"
@@ -19,6 +17,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"
"tydata-server/common/globalkey"
) )
var ( var (

View File

@@ -2,9 +2,9 @@ package model
import ( import (
"context" "context"
"tydata-server/common/globalkey"
"fmt" "fmt"
"time" "time"
"tydata-server/common/globalkey"
"github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/stores/sqlx"

View File

@@ -0,0 +1,76 @@
package model
import (
"context"
"fmt"
"time"
"tydata-server/common/globalkey"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ QueryUserRecordModel = (*customQueryUserRecordModel)(nil)
type (
// QueryUserRecordModel is an interface to be customized, add more methods here,
// and implement the added methods in customQueryUserRecordModel.
QueryUserRecordModel interface {
queryUserRecordModel
FindOneByQueryNo(ctx context.Context, queryNo string) (*QueryUserRecord, error)
CountByEncryptedIdCardIn72Hours(ctx context.Context, encryptedIdCard string) (int64, error)
}
customQueryUserRecordModel struct {
*defaultQueryUserRecordModel
}
)
// FindOneByQueryNo 根据 query_no 查询一条记录query_no 与 order.order_no 一致)
func (m *customQueryUserRecordModel) FindOneByQueryNo(ctx context.Context, queryNo string) (*QueryUserRecord, error) {
query := fmt.Sprintf("select %s from %s where `query_no` = ? and del_state = ? limit 1", queryUserRecordRows, m.table)
var resp QueryUserRecord
err := m.QueryRowNoCacheCtx(ctx, &resp, query, queryNo, globalkey.DelStateNo)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
// CountByEncryptedIdCardIn72Hours 查询72小时内某个加密身份证号的已支付查询次数
func (m *customQueryUserRecordModel) CountByEncryptedIdCardIn72Hours(ctx context.Context, encryptedIdCard string) (int64, error) {
// 计算72小时前的时间
seventyTwoHoursAgo := time.Now().Add(-72 * time.Hour)
// 关联 order 表,只统计已支付的订单
query := fmt.Sprintf(`
select count(*)
from %s qur
inner join `+"`order`"+` o on qur.order_id = o.id
where qur.id_card = ?
and qur.create_time >= ?
and qur.del_state = ?
and qur.order_id > 0
and o.status = 'paid'
and o.del_state = ?
`, m.table)
var count int64
err := m.QueryRowNoCacheCtx(ctx, &count, query, encryptedIdCard, seventyTwoHoursAgo, globalkey.DelStateNo, globalkey.DelStateNo)
if err != nil {
return 0, err
}
return count, nil
}
// NewQueryUserRecordModel returns a model for the database table.
func NewQueryUserRecordModel(conn sqlx.SqlConn, c cache.CacheConf) QueryUserRecordModel {
return &customQueryUserRecordModel{
defaultQueryUserRecordModel: newQueryUserRecordModel(conn, c),
}
}

View File

@@ -0,0 +1,376 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"tydata-server/common/globalkey"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
queryUserRecordFieldNames = builder.RawFieldNames(&QueryUserRecord{})
queryUserRecordRows = strings.Join(queryUserRecordFieldNames, ",")
queryUserRecordRowsExpectAutoSet = strings.Join(stringx.Remove(queryUserRecordFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
queryUserRecordRowsWithPlaceHolder = strings.Join(stringx.Remove(queryUserRecordFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheTydataQueryUserRecordIdPrefix = "cache:tydata:queryUserRecord:id:"
)
type (
queryUserRecordModel interface {
Insert(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*QueryUserRecord, error)
Update(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error
Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
SelectBuilder() squirrel.SelectBuilder
DeleteSoft(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error
FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error)
FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error)
FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*QueryUserRecord, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*QueryUserRecord, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*QueryUserRecord, error)
Delete(ctx context.Context, session sqlx.Session, id int64) error
}
defaultQueryUserRecordModel struct {
sqlc.CachedConn
table string
}
QueryUserRecord struct {
Id int64 `db:"id"`
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"`
Version int64 `db:"version"` // 版本号
UserId int64 `db:"user_id"` // 用户ID
Name string `db:"name"` // 姓名密文AES-ECB+Base64
IdCard string `db:"id_card"` // 身份证号密文AES-ECB+Base64
Mobile string `db:"mobile"` // 手机号密文AES-ECB+Base64
Product string `db:"product"` // 产品类型,如 marriage/homeservice/riskassessment 等
QueryNo string `db:"query_no"` // 查询单号(与 order.order_no 一致,如 Q_xxx用户提交查询时生成
OrderId int64 `db:"order_id"` // 订单ID关联 order 表,用户发起支付并创建订单后写入
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号(支付宝/微信),支付成功后由回调写入
AgentIdentifier sql.NullString `db:"agent_identifier"` // 代理标识,代理渠道时有值
}
)
func newQueryUserRecordModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultQueryUserRecordModel {
return &defaultQueryUserRecordModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`query_user_record`",
}
}
func (m *defaultQueryUserRecordModel) Insert(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
tydataQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, data.Id)
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, queryUserRecordRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier)
}, tydataQueryUserRecordIdKey)
}
func (m *defaultQueryUserRecordModel) FindOne(ctx context.Context, id int64) (*QueryUserRecord, error) {
tydataQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, id)
var resp QueryUserRecord
err := m.QueryRowCtx(ctx, &resp, tydataQueryUserRecordIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", queryUserRecordRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultQueryUserRecordModel) Update(ctx context.Context, session sqlx.Session, data *QueryUserRecord) (sql.Result, error) {
tydataQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, data.Id)
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, queryUserRecordRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id)
}, tydataQueryUserRecordIdKey)
}
func (m *defaultQueryUserRecordModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
tydataQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, data.Id)
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, queryUserRecordRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.Name, data.IdCard, data.Mobile, data.Product, data.QueryNo, data.OrderId, data.PlatformOrderId, data.AgentIdentifier, data.Id, oldVersion)
}, tydataQueryUserRecordIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultQueryUserRecordModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *QueryUserRecord) error {
data.DelState = globalkey.DelStateYes
data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true}
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
return errors.Wrapf(errors.New("delete soft failed "), "QueryUserRecordModel delete err : %+v", err)
}
return nil
}
func (m *defaultQueryUserRecordModel) FindSum(ctx context.Context, builder squirrel.SelectBuilder, field string) (float64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindSum Least One Field"), "FindSum Least One Field")
}
builder = builder.Columns("IFNULL(SUM(" + field + "),0)")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp float64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultQueryUserRecordModel) FindCount(ctx context.Context, builder squirrel.SelectBuilder, field string) (int64, error) {
if len(field) == 0 {
return 0, errors.Wrapf(errors.New("FindCount Least One Field"), "FindCount Least One Field")
}
builder = builder.Columns("COUNT(" + field + ")")
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return 0, err
}
var resp int64
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return 0, err
}
}
func (m *defaultQueryUserRecordModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*QueryUserRecord, error) {
builder = builder.Columns(queryUserRecordRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).ToSql()
if err != nil {
return nil, err
}
var resp []*QueryUserRecord
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultQueryUserRecordModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, error) {
builder = builder.Columns(queryUserRecordRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*QueryUserRecord
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultQueryUserRecordModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*QueryUserRecord, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(queryUserRecordRows)
if orderBy == "" {
builder = builder.OrderBy("id DESC")
} else {
builder = builder.OrderBy(orderBy)
}
if page < 1 {
page = 1
}
offset := (page - 1) * pageSize
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, total, err
}
var resp []*QueryUserRecord
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultQueryUserRecordModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*QueryUserRecord, error) {
builder = builder.Columns(queryUserRecordRows)
if preMinId > 0 {
builder = builder.Where(" id < ? ", preMinId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id DESC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*QueryUserRecord
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultQueryUserRecordModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*QueryUserRecord, error) {
builder = builder.Columns(queryUserRecordRows)
if preMaxId > 0 {
builder = builder.Where(" id > ? ", preMaxId)
}
query, values, err := builder.Where("del_state = ?", globalkey.DelStateNo).OrderBy("id ASC").Limit(uint64(pageSize)).ToSql()
if err != nil {
return nil, err
}
var resp []*QueryUserRecord
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultQueryUserRecordModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
return fn(ctx, session)
})
}
func (m *defaultQueryUserRecordModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultQueryUserRecordModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
tydataQueryUserRecordIdKey := fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, id)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil {
return session.ExecCtx(ctx, query, id)
}
return conn.ExecCtx(ctx, query, id)
}, tydataQueryUserRecordIdKey)
return err
}
func (m *defaultQueryUserRecordModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheTydataQueryUserRecordIdPrefix, primary)
}
func (m *defaultQueryUserRecordModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", queryUserRecordRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultQueryUserRecordModel) tableName() string {
return m.table
}

View File

@@ -27,8 +27,8 @@ var (
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmUserIdPrefix = "cache:tydata:user:id:" cacheTydataUserIdPrefix = "cache:tydata:user:id:"
cacheHmUserMobilePrefix = "cache:tydata:user:mobile:" cacheTydataUserMobilePrefix = "cache:tydata:user:mobile:"
) )
type ( type (
@@ -68,6 +68,7 @@ type (
Nickname sql.NullString `db:"nickname"` Nickname sql.NullString `db:"nickname"`
Info string `db:"info"` Info string `db:"info"`
Inside int64 `db:"inside"` Inside int64 `db:"inside"`
Disable int64 `db:"disable"` // 0可用 1禁用
} }
) )
@@ -80,21 +81,21 @@ func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel {
func (m *defaultUserModel) Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) { func (m *defaultUserModel) Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmUserIdKey := fmt.Sprintf("%s%v", cacheHmUserIdPrefix, data.Id) tydataUserIdKey := fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, data.Id)
hmUserMobileKey := fmt.Sprintf("%s%v", cacheHmUserMobilePrefix, data.Mobile) tydataUserMobileKey := fmt.Sprintf("%s%v", cacheTydataUserMobilePrefix, data.Mobile)
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, userRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside) return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside, data.Disable)
} }
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside) return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside, data.Disable)
}, hmUserIdKey, hmUserMobileKey) }, tydataUserIdKey, tydataUserMobileKey)
} }
func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) {
hmUserIdKey := fmt.Sprintf("%s%v", cacheHmUserIdPrefix, id) tydataUserIdKey := fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, id)
var resp User var resp User
err := m.QueryRowCtx(ctx, &resp, hmUserIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataUserIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -109,9 +110,9 @@ func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error)
} }
func (m *defaultUserModel) FindOneByMobile(ctx context.Context, mobile sql.NullString) (*User, error) { func (m *defaultUserModel) FindOneByMobile(ctx context.Context, mobile sql.NullString) (*User, error) {
hmUserMobileKey := fmt.Sprintf("%s%v", cacheHmUserMobilePrefix, mobile) tydataUserMobileKey := fmt.Sprintf("%s%v", cacheTydataUserMobilePrefix, mobile)
var resp User var resp User
err := m.QueryRowIndexCtx(ctx, &resp, hmUserMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndexCtx(ctx, &resp, tydataUserMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `mobile` = ? and del_state = ? limit 1", userRows, m.table) query := fmt.Sprintf("select %s from %s where `mobile` = ? and del_state = ? limit 1", userRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, mobile, globalkey.DelStateNo); err != nil { if err := conn.QueryRowCtx(ctx, &resp, query, mobile, globalkey.DelStateNo); err != nil {
return nil, err return nil, err
@@ -133,15 +134,15 @@ func (m *defaultUserModel) Update(ctx context.Context, session sqlx.Session, new
if err != nil { if err != nil {
return nil, err return nil, err
} }
hmUserIdKey := fmt.Sprintf("%s%v", cacheHmUserIdPrefix, data.Id) tydataUserIdKey := fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, data.Id)
hmUserMobileKey := fmt.Sprintf("%s%v", cacheHmUserMobilePrefix, data.Mobile) tydataUserMobileKey := fmt.Sprintf("%s%v", cacheTydataUserMobilePrefix, data.Mobile)
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, userRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id) return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id)
} }
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id) return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id)
}, hmUserIdKey, hmUserMobileKey) }, tydataUserIdKey, tydataUserMobileKey)
} }
func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *User) error { func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *User) error {
@@ -156,15 +157,15 @@ func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.S
if err != nil { if err != nil {
return err return err
} }
hmUserIdKey := fmt.Sprintf("%s%v", cacheHmUserIdPrefix, data.Id) tydataUserIdKey := fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, data.Id)
hmUserMobileKey := fmt.Sprintf("%s%v", cacheHmUserMobilePrefix, data.Mobile) tydataUserMobileKey := fmt.Sprintf("%s%v", cacheTydataUserMobilePrefix, data.Mobile)
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, userRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id, oldVersion) return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id, oldVersion) return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id, oldVersion)
}, hmUserIdKey, hmUserMobileKey) }, tydataUserIdKey, tydataUserMobileKey)
if err != nil { if err != nil {
return err return err
} }
@@ -387,19 +388,19 @@ func (m *defaultUserModel) Delete(ctx context.Context, session sqlx.Session, id
return err return err
} }
hmUserIdKey := fmt.Sprintf("%s%v", cacheHmUserIdPrefix, id) tydataUserIdKey := fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, id)
hmUserMobileKey := fmt.Sprintf("%s%v", cacheHmUserMobilePrefix, data.Mobile) tydataUserMobileKey := fmt.Sprintf("%s%v", cacheTydataUserMobilePrefix, data.Mobile)
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table) query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, id) return session.ExecCtx(ctx, query, id)
} }
return conn.ExecCtx(ctx, query, id) return conn.ExecCtx(ctx, query, id)
}, hmUserIdKey, hmUserMobileKey) }, tydataUserIdKey, tydataUserMobileKey)
return err return err
} }
func (m *defaultUserModel) formatPrimary(primary interface{}) string { func (m *defaultUserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmUserIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataUserIdPrefix, primary)
} }
func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table)

View File

@@ -108,3 +108,14 @@ const (
TaxStatusExempt = 2 // 免税 TaxStatusExempt = 2 // 免税
TaxStatusFailed = 3 // 扣税失败 TaxStatusFailed = 3 // 扣税失败
) )
// 钱包交易类型
const (
WalletTransactionTypeCommission = "commission" // 佣金收入
WalletTransactionTypeWithdraw = "withdraw" // 提现
WalletTransactionTypeFreeze = "freeze" // 冻结
WalletTransactionTypeUnfreeze = "unfreeze" // 解冻
WalletTransactionTypeReward = "reward" // 奖励
WalletTransactionTypeRefund = "refund" // 退款
WalletTransactionTypeAdjust = "adjust" // 调整
)

View File

@@ -16,6 +16,7 @@ const PARAM_VERIFICATION_ERROR uint32 = 100007
const CUSTOM_ERROR uint32 = 100008 const CUSTOM_ERROR uint32 = 100008
const USER_NOT_FOUND uint32 = 100009 const USER_NOT_FOUND uint32 = 100009
const USER_NEED_BIND_MOBILE uint32 = 100010 const USER_NEED_BIND_MOBILE uint32 = 100010
const USER_DISABLED uint32 = 100011 // 账号已被封禁
const LOGIN_FAILED uint32 = 200001 const LOGIN_FAILED uint32 = 200001
const LOGIC_QUERY_WAIT uint32 = 200002 const LOGIC_QUERY_WAIT uint32 = 200002

View File

@@ -11,6 +11,9 @@ func init() {
message[TOKEN_GENERATE_ERROR] = "生成token失败" message[TOKEN_GENERATE_ERROR] = "生成token失败"
message[DB_ERROR] = "系统维护升级中,请稍后再试" message[DB_ERROR] = "系统维护升级中,请稍后再试"
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0" message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
message[USER_NOT_FOUND] = "用户不存在"
message[USER_NEED_BIND_MOBILE] = "请先绑定手机号"
message[USER_DISABLED] = "账号已被封禁"
} }
func MapErrMsg(errcode uint32) string { func MapErrMsg(errcode uint32) string {

View File

@@ -11,14 +11,15 @@ $tables = @(
# "agent_active_stat", # "agent_active_stat",
# "agent_audit", # "agent_audit",
# "agent_closure", # "agent_closure",
# "agent_commission", # "agent_commission"
# "agent_commission_deduction", # "agent_wallet_transaction"
# "agent_commission_deduction"
# "agent_link", # "agent_link",
# "agent_membership_config", # "agent_membership_config",
# "agent_membership_recharge_order" # "agent_membership_recharge_order"
# "agent_membership_user_config", # "agent_membership_user_config",
# "agent_order", # "agent_order",
"agent_platform_deduction" # "agent_platform_deduction"
# "agent_product_config", # "agent_product_config",
# "agent_rewards", # "agent_rewards",
# "agent_wallet", # "agent_wallet",
@@ -33,10 +34,11 @@ $tables = @(
# "product", # "product",
# "product_feature" # "product_feature"
# "query", # "query",
# "query_user_record" # 查询用户记录表:姓名、身份证、手机号、支付订单号等
# "query_cleanup_log" # "query_cleanup_log"
# "query_cleanup_detail" # "query_cleanup_detail"
# "query_cleanup_config" # "query_cleanup_config"
# "user" "user"
# "user_auth" # "user_auth"
# "user_temp" # "user_temp"
# "example" # "example"

View File

@@ -0,0 +1,24 @@
-- ============================================================
-- agent_withdrawal 表缺失字段修复脚本
-- 解决 Error 1054: Unknown column 'withdraw_type'/'bank_card_no'/'bank_name'/'payee_name' in 'field list'
-- 执行方式:在 MySQL 客户端或 Navicat 等工具中逐条执行
-- 若某字段已存在会报 Duplicate column name跳过该条继续执行下一条即可
-- ============================================================
-- 1. 添加提现类型
ALTER TABLE `agent_withdrawal` ADD COLUMN `withdraw_type` TINYINT NOT NULL DEFAULT 1 COMMENT '提现类型:1-支付宝,2-银行卡' AFTER `agent_id`;
-- 2. 添加银行卡号
ALTER TABLE `agent_withdrawal` ADD COLUMN `bank_card_no` VARCHAR(50) DEFAULT NULL COMMENT '银行卡号' AFTER `payeeAccount`;
-- 3. 添加开户支行
ALTER TABLE `agent_withdrawal` ADD COLUMN `bank_name` VARCHAR(100) DEFAULT NULL COMMENT '开户支行' AFTER `bank_card_no`;
-- 4. 添加收款人姓名
ALTER TABLE `agent_withdrawal` ADD COLUMN `payee_name` VARCHAR(50) DEFAULT NULL COMMENT '收款人姓名' AFTER `bank_name`;
-- 5. 添加索引(若已存在可跳过)
ALTER TABLE `agent_withdrawal` ADD INDEX `idx_withdraw_type` (`withdraw_type`);
-- 6. 更新现有记录
UPDATE `agent_withdrawal` SET `withdraw_type` = 1 WHERE `withdraw_type` IS NULL OR `withdraw_type` = 0;

View File

@@ -0,0 +1,42 @@
-- 查询用户记录表
-- 用途:记录用户查询时输入的姓名、身份证、手机号,以及支付订单号等,用于通过查询条件追溯订单信息
-- 执行说明:在目标数据库执行此脚本创建表
--
-- 使用说明(需在业务代码中接入):
-- 1. 用户提交查询时queryservicelogic.CacheData 之后INSERT 记录 name, id_card, mobile已 AES-ECB+Base64 加密), product, query_no, user_id, agent_identifier
-- 2. 用户发起支付并创建 order 时paymentlogic.QueryOrderPayment 中 Insert order 之后UPDATE 本表 SET order_id WHERE query_no=outTradeNo
-- 3. 支付回调成功更新 order 后alipaycallbacklogic/wechatpaycallbacklogicUPDATE 本表 SET platform_order_id WHERE query_no=orderNo
--
-- 敏感字段加密name、id_card、mobile 使用 pkg/lzkit/crypto 的 AES-ECB+Base64 加密后入库,密钥为 config.Encrypt.SecretKeyhex
-- 解密:姓名 crypto.AesEcbDecrypt(rec.Name, key);身份证 crypto.DecryptIDCard(rec.IdCard, key);手机 crypto.DecryptMobile(rec.Mobile, secretKey)。
CREATE TABLE `query_user_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
`del_state` tinyint NOT NULL DEFAULT '0',
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
/* 业务字段 - 用户查询输入name、id_card、mobile 为 AES-ECB+Base64 密文) */
`user_id` bigint NOT NULL DEFAULT '0' COMMENT '用户ID',
`name` varchar(256) NOT NULL DEFAULT '' COMMENT '姓名密文AES-ECB+Base64',
`id_card` varchar(128) NOT NULL DEFAULT '' COMMENT '身份证号密文AES-ECB+Base64',
`mobile` varchar(128) NOT NULL DEFAULT '' COMMENT '手机号密文AES-ECB+Base64',
`product` varchar(50) NOT NULL DEFAULT '' COMMENT '产品类型,如 marriage/homeservice/riskassessment 等',
/* 关联字段 - 查询单号与订单 */
`query_no` varchar(64) NOT NULL DEFAULT '' COMMENT '查询单号(与 order.order_no 一致,如 Q_xxx用户提交查询时生成',
`order_id` bigint NOT NULL DEFAULT '0' COMMENT '订单ID关联 order 表,用户发起支付并创建订单后写入',
`platform_order_id` varchar(64) DEFAULT NULL COMMENT '支付平台订单号(支付宝/微信),支付成功后由回调写入',
`agent_identifier` varchar(255) DEFAULT NULL COMMENT '代理标识,代理渠道时有值',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_id_card` (`id_card`),
KEY `idx_mobile` (`mobile`),
KEY `idx_query_no` (`query_no`),
KEY `idx_order_id` (`order_id`),
KEY `idx_platform_order_id` (`platform_order_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='查询用户记录表:姓名、身份证、手机号、支付订单号等,用于通过查询信息追溯订单';

View File

@@ -0,0 +1,2 @@
-- 为用户表添加 disable 字段0 可用1 禁用,默认 0
ALTER TABLE `user` ADD COLUMN `disable` tinyint NOT NULL DEFAULT 0 COMMENT '0可用 1禁用' AFTER `inside`;

40
go.mod
View File

@@ -6,31 +6,32 @@ 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/cenkalti/backoff/v4 v4.3.0 github.com/cenkalti/backoff/v4 v4.3.0
github.com/fogleman/gg v1.3.0 github.com/fogleman/gg v1.3.0
github.com/go-playground/validator/v10 v10.22.1 github.com/go-playground/validator/v10 v10.22.1
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.0 github.com/hibiken/asynq v0.25.0
github.com/jinzhu/copier v0.4.0 github.com/jinzhu/copier v0.4.0
github.com/jung-kurt/gofpdf v1.16.2 github.com/jung-kurt/gofpdf v1.16.2
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/redis/go-redis/v9 v9.7.0 github.com/redis/go-redis/v9 v9.17.2
github.com/samber/lo v1.50.0 github.com/samber/lo v1.50.0
github.com/shopspring/decimal v1.4.0 github.com/shopspring/decimal v1.4.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/smartwalle/alipay/v3 v3.2.23 github.com/smartwalle/alipay/v3 v3.2.23
github.com/sony/sonyflake v1.2.0 github.com/sony/sonyflake v1.2.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.11.1
github.com/tidwall/gjson v1.18.0 github.com/tidwall/gjson v1.18.0
github.com/wechatpay-apiv3/wechatpay-go v0.2.20 github.com/wechatpay-apiv3/wechatpay-go v0.2.20
github.com/zeromicro/go-zero v1.7.3 github.com/zeromicro/go-zero v1.9.4
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.33.0
google.golang.org/grpc v1.67.1 google.golang.org/grpc v1.67.1
) )
@@ -41,26 +42,27 @@ 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/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/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // 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.17.0 // indirect github.com/fatih/color v1.18.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-sql-driver/mysql v1.9.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/grafana/pyroscope-go v1.2.7 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
@@ -73,9 +75,9 @@ require (
github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/smartwalle/ncrypto v1.0.4 // indirect github.com/smartwalle/ncrypto v1.0.4 // indirect
@@ -102,13 +104,13 @@ require (
go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/image v0.28.0 // indirect golang.org/x/image v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.26.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.7.0 // indirect golang.org/x/time v0.10.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

108
go.sum
View File

@@ -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,28 +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/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
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=
@@ -83,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=
@@ -98,8 +97,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -119,10 +118,10 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -147,6 +146,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
@@ -162,8 +165,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc= github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0= github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@@ -210,17 +213,17 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -266,8 +269,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -287,8 +290,8 @@ github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zeromicro/go-zero v1.7.3 h1:yDUQF2DXDhUHc77/NZF6mzsoRPMBfldjPmG2O/ZSzss= github.com/zeromicro/go-zero v1.9.4 h1:aRLFoISqAYijABtkbliQC5SsI5TbizJpQvoHc9xup8k=
github.com/zeromicro/go-zero v1.7.3/go.mod h1:9JIW3gHBGuc9LzvjZnNwINIq9QdiKu3AigajLtkJamQ= github.com/zeromicro/go-zero v1.9.4/go.mod h1:a17JOTch25SWxBcUgJZYps60hygK3pIYdw7nGwlcS38=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
@@ -324,12 +327,15 @@ 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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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/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=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
@@ -340,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=
@@ -354,12 +363,15 @@ 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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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/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=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -368,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=
@@ -382,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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.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/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=
@@ -404,10 +426,12 @@ 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.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -418,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=
@@ -441,8 +467,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

81
pkg/captcha/aliyun.go Normal file
View File

@@ -0,0 +1,81 @@
package captcha
import (
"context"
"net/http"
"os"
"strings"
"tydata-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"
"github.com/pkg/errors"
)
// contextKey 用于在 context 中存储 *http.Request供 VerifyWithRequest 判断微信等环境
type contextKey struct{}
// HTTPRequestContextKey 为 context 中 http.Request 的 keyhandler 可将 r 注入后传入 logic
var HTTPRequestContextKey = &contextKey{}
// isWeChatClient 根据 User-Agent 判断是否为微信内置浏览器/小程序环境(此类环境可不传 captchaVerifyParam默认通过
func isWeChatClient(r *http.Request) bool {
if r == nil {
return false
}
ua := strings.ToLower(r.Header.Get("User-Agent"))
return strings.Contains(ua, "micromessenger") || strings.Contains(ua, "miniprogram")
}
type Config struct {
AccessKeyID string
AccessKeySecret string
EndpointURL string
SceneID string
}
// VerifyWithRequest 在 Verify 基础上支持按请求头判断:微信/小程序环境下可不传 captchaVerifyParam默认通过。
// 若 ctx 中带有 HTTPRequestContextKey 的 *http.Request且 User-Agent 为微信环境,则不再校验 captchaVerifyParam。
func VerifyWithRequest(cfg Config, captchaVerifyParam string, ctx context.Context) error {
if ctx != nil {
if req, ok := ctx.Value(HTTPRequestContextKey).(*http.Request); ok && isWeChatClient(req) {
return nil
}
}
return Verify(cfg, captchaVerifyParam)
}
func Verify(cfg Config, captchaVerifyParam string) error {
if os.Getenv("ENV") == "development" {
return nil
}
if captchaVerifyParam == "" {
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败captchaVerifyParam 不能为空"), "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 errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "new client error: %+v", err)
}
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
SceneId: tea.String(cfg.SceneID),
CaptchaVerifyParam: tea.String(captchaVerifyParam),
}
resp, err := client.VerifyIntelligentCaptcha(req)
if err != nil {
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "verify request error: %+v", err)
}
if tea.BoolValue(resp.Body.Result.VerifyResult) {
return nil
}
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "verify result false")
}