Compare commits

...

43 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
8e136d3ea7 Merge branch 'main' of http://1.117.67.95:3000/team/hm-server 2025-12-30 11:31:30 +08:00
dc24500067 fix 2025-12-30 11:31:27 +08:00
cb2474b27f Merge branch 'main' of http://1.117.67.95:3000/team/hm-server 2025-12-29 19:37:09 +08:00
b19933fcee add FLXG7E8F 2025-12-29 19:37:06 +08:00
293dc46343 fix 2025-12-29 18:29:24 +08:00
d2d3e589f0 fix add 2025-12-29 16:13:55 +08:00
8e1319ac39 fixadd 2025-12-27 16:04:43 +08:00
41ab1e1665 fix 2025-12-27 14:48:32 +08:00
cc1680cf01 fix analysis 2025-12-27 13:51:26 +08:00
112 changed files with 5302 additions and 500 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)
@@ -65,7 +81,36 @@ service main {
// 银行卡提现审核(确认/拒绝) // 银行卡提现审核(确认/拒绝)
@handler AdminReviewBankCardWithdrawal @handler AdminReviewBankCardWithdrawal
post /agent-withdrawal/bank-card/review (AdminReviewBankCardWithdrawalReq) returns (AdminReviewBankCardWithdrawalResp) post /agent-withdrawal/bank-card/review (AdminReviewBankCardWithdrawalReq) returns (AdminReviewBankCardWithdrawalResp)
}
// 获取提现统计数据
@handler AdminGetWithdrawalStatistics
get /agent-withdrawal/statistics (AdminGetWithdrawalStatisticsReq) returns (AdminGetWithdrawalStatisticsResp)
// 获取代理订单统计数据
@handler AdminGetAgentOrderStatistics
get /agent-order/statistics (AdminGetAgentOrderStatisticsReq) returns (AdminGetAgentOrderStatisticsResp)
// 获取代理统计数据
@handler AdminGetAgentStatistics
get /statistics (AdminGetAgentStatisticsReq) returns (AdminGetAgentStatisticsResp)
// 获取代理链接产品统计
@handler AdminGetAgentLinkProductStatistics
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 (
// 代理分页查询请求 // 代理分页查询请求
@@ -129,11 +174,14 @@ type (
// 代理佣金分页查询请求 // 代理佣金分页查询请求
AdminGetAgentCommissionListReq { AdminGetAgentCommissionListReq {
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可选
ProductName *string `form:"product_name,optional"` // 产品名(可选) OrderId *int64 `form:"order_id,optional"` // 订单ID(可选)
Status *int64 `form:"status,optional"` // 状态(可选) ProductName *string `form:"product_name,optional"` // 产品名(可选)
Status *int64 `form:"status,optional"` // 状态(可选)
CreateTimeStart *string `form:"create_time_start,optional"` // 创建时间开始(可选)
CreateTimeEnd *string `form:"create_time_end,optional"` // 创建时间结束(可选)
} }
// 代理佣金列表项 // 代理佣金列表项
@@ -153,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"` // 页码
@@ -180,11 +251,12 @@ type (
// 代理提现分页查询请求 // 代理提现分页查询请求
AdminGetAgentWithdrawalListReq { AdminGetAgentWithdrawalListReq {
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-银行卡
} }
// 代理提现列表项 // 代理提现列表项
@@ -405,4 +477,123 @@ type (
AdminReviewBankCardWithdrawalResp { AdminReviewBankCardWithdrawalResp {
Success bool `json:"success"` // 是否成功 Success bool `json:"success"` // 是否成功
} }
// 获取提现统计数据请求
AdminGetWithdrawalStatisticsReq {
}
// 获取提现统计数据响应
AdminGetWithdrawalStatisticsResp {
TotalWithdrawalAmount float64 `json:"total_withdrawal_amount"` // 总提现金额
TodayWithdrawalAmount float64 `json:"today_withdrawal_amount"` // 今日提现金额
TotalActualAmount float64 `json:"total_actual_amount"` // 总实际到账金额
TotalTaxAmount float64 `json:"total_tax_amount"` // 总扣税金额
}
// 获取代理订单统计数据请求
AdminGetAgentOrderStatisticsReq {
}
// 获取代理订单统计数据响应
AdminGetAgentOrderStatisticsResp {
TotalAgentOrderCount int64 `json:"total_agent_order_count"` // 总代理订单数
TodayAgentOrderCount int64 `json:"today_agent_order_count"` // 今日代理订单数
}
// 获取代理统计数据请求
AdminGetAgentStatisticsReq {
}
// 获取代理统计数据响应
AdminGetAgentStatisticsResp {
TotalAgentCount int64 `json:"total_agent_count"` // 总代理数
TodayAgentCount int64 `json:"today_agent_count"` // 今日新增代理数
}
// 获取代理链接产品统计请求
AdminGetAgentLinkProductStatisticsReq {
}
// 代理链接产品统计列表项
AgentLinkProductStatisticsItem {
ProductName string `json:"product_name"` // 产品名称
LinkCount int64 `json:"link_count"` // 推广链接数量
}
// 获取代理链接产品统计响应
AdminGetAgentLinkProductStatisticsResp {
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

@@ -45,8 +45,9 @@ service main {
type ( type (
// 创建功能请求 // 创建功能请求
AdminCreateFeatureReq { AdminCreateFeatureReq {
ApiId string `json:"api_id"` // API标识 ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述 Name string `json:"name"` // 描述
CostPrice float64 `json:"cost_price"` // 成本价
} }
// 创建功能响应 // 创建功能响应
AdminCreateFeatureResp { AdminCreateFeatureResp {
@@ -54,9 +55,10 @@ type (
} }
// 更新功能请求 // 更新功能请求
AdminUpdateFeatureReq { AdminUpdateFeatureReq {
Id int64 `path:"id"` // 功能ID Id int64 `path:"id"` // 功能ID
ApiId *string `json:"api_id,optional"` // API标识 ApiId *string `json:"api_id,optional"` // API标识
Name *string `json:"name,optional"` // 描述 Name *string `json:"name,optional"` // 描述
CostPrice *float64 `json:"cost_price,optional"` // 成本价
} }
// 更新功能响应 // 更新功能响应
AdminUpdateFeatureResp { AdminUpdateFeatureResp {
@@ -79,11 +81,12 @@ type (
} }
// 功能列表项 // 功能列表项
FeatureListItem { FeatureListItem {
Id int64 `json:"id"` // 功能ID Id int64 `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识 ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述 Name string `json:"name"` // 描述
CreateTime string `json:"create_time"` // 创建时间 CostPrice float64 `json:"cost_price"` // 成本价
UpdateTime string `json:"update_time"` // 更新时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
} }
// 获取功能列表响应 // 获取功能列表响应
AdminGetFeatureListResp { AdminGetFeatureListResp {
@@ -96,11 +99,12 @@ type (
} }
// 获取功能详情响应 // 获取功能详情响应
AdminGetFeatureDetailResp { AdminGetFeatureDetailResp {
Id int64 `json:"id"` // 功能ID Id int64 `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识 ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述 Name string `json:"name"` // 描述
CreateTime string `json:"create_time"` // 创建时间 CostPrice float64 `json:"cost_price"` // 成本价
UpdateTime string `json:"update_time"` // 更新时间 CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
} }
// 配置功能示例数据请求 // 配置功能示例数据请求
AdminConfigFeatureExampleReq { AdminConfigFeatureExampleReq {

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

@@ -39,6 +39,22 @@ service main {
@doc "重新执行代理处理" @doc "重新执行代理处理"
@handler AdminRetryAgentProcess @handler AdminRetryAgentProcess
post /retry-agent-process/:id (AdminRetryAgentProcessReq) returns (AdminRetryAgentProcessResp) post /retry-agent-process/:id (AdminRetryAgentProcessReq) returns (AdminRetryAgentProcessResp)
@doc "获取退款统计数据"
@handler AdminGetRefundStatistics
get /refund-statistics (AdminGetRefundStatisticsReq) returns (AdminGetRefundStatisticsResp)
@doc "获取收入和利润统计数据"
@handler AdminGetRevenueStatistics
get /revenue-statistics (AdminGetRevenueStatisticsReq) returns (AdminGetRevenueStatisticsResp)
@doc "获取订单来源统计数据"
@handler AdminGetOrderSourceStatistics
get /source-statistics (AdminGetOrderSourceStatisticsReq) returns (AdminGetOrderSourceStatisticsResp)
@doc "获取订单统计数据"
@handler AdminGetOrderStatistics
get /statistics (AdminGetOrderStatisticsReq) returns (AdminGetOrderStatisticsResp)
} }
type ( type (
@@ -60,6 +76,10 @@ type (
PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束 PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束
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"` // 成本价
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 {
@@ -75,6 +95,7 @@ type (
PaymentPlatform string `json:"payment_platform"` // 支付方式 PaymentPlatform string `json:"payment_platform"` // 支付方式
PaymentScene string `json:"payment_scene"` // 支付平台 PaymentScene string `json:"payment_scene"` // 支付平台
Amount float64 `json:"amount"` // 金额 Amount float64 `json:"amount"` // 金额
SalesCost float64 `json:"sales_cost"` // 成本价
Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败 Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中 QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
@@ -83,6 +104,7 @@ type (
IsPromotion int64 `json:"is_promotion"` // 是否推广订单0-否1-是 IsPromotion int64 `json:"is_promotion"` // 是否推广订单0-否1-是
IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单 IsAgentOrder bool `json:"is_agent_order"` // 是否是代理订单
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态not_agent-非代理订单success-处理成功failed-处理失败pending-待处理 AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态not_agent-非代理订单success-处理成功failed-处理失败pending-待处理
} }
// 详情请求 // 详情请求
AdminGetOrderDetailReq { AdminGetOrderDetailReq {
@@ -97,6 +119,7 @@ type (
PaymentPlatform string `json:"payment_platform"` // 支付方式 PaymentPlatform string `json:"payment_platform"` // 支付方式
PaymentScene string `json:"payment_scene"` // 支付平台 PaymentScene string `json:"payment_scene"` // 支付平台
Amount float64 `json:"amount"` // 金额 Amount float64 `json:"amount"` // 金额
SalesCost float64 `json:"sales_cost"` // 成本价
Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败 Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中 QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
@@ -170,4 +193,58 @@ type (
Message string `json:"message"` // 执行结果消息 Message string `json:"message"` // 执行结果消息
ProcessedAt string `json:"processed_at"` // 处理时间 ProcessedAt string `json:"processed_at"` // 处理时间
} }
// 获取退款统计数据请求
AdminGetRefundStatisticsReq {
}
// 获取退款统计数据响应
AdminGetRefundStatisticsResp {
TotalRefundAmount float64 `json:"total_refund_amount"` // 总退款金额
TodayRefundAmount float64 `json:"today_refund_amount"` // 今日退款金额
}
// 获取收入和利润统计数据请求
AdminGetRevenueStatisticsReq {
}
// 获取收入和利润统计数据响应
AdminGetRevenueStatisticsResp {
TotalRevenueAmount float64 `json:"total_revenue_amount"` // 总收入金额
TodayRevenueAmount float64 `json:"today_revenue_amount"` // 今日收入金额
TotalProfitAmount float64 `json:"total_profit_amount"` // 总利润金额
TodayProfitAmount float64 `json:"today_profit_amount"` // 今日利润金额
}
// 获取订单来源统计数据请求
AdminGetOrderSourceStatisticsReq {
}
// 订单来源统计项
OrderSourceStatisticsItem {
ProductName string `json:"product_name"` // 产品名称
OrderCount int64 `json:"order_count"` // 订单数量
}
// 获取订单来源统计数据响应
AdminGetOrderSourceStatisticsResp {
Items []OrderSourceStatisticsItem `json:"items"` // 订单来源统计列表
}
// 获取订单统计数据请求
AdminGetOrderStatisticsReq {
Dimension string `form:"dimension"` // 时间维度day-日(当月1号到今天)month-月(今年1月到当月)year-年(过去5年)all-全部(按日统计)
}
// 订单统计项
OrderStatisticsItem {
Date string `json:"date"` // 日期
Count int64 `json:"count"` // 订单数量
Amount float64 `json:"amount"` // 订单金额
}
// 获取订单统计数据响应
AdminGetOrderStatisticsResp {
Items []OrderStatisticsItem `json:"items"` // 订单统计列表
}
) )

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

@@ -326,11 +326,14 @@ type (
SubWithdrawReward float64 `json:"sub_withdraw_reward"` SubWithdrawReward float64 `json:"sub_withdraw_reward"`
} }
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"` // 原始佣金金额
CreateTime string `json:"create_time"` RefundedAmount float64 `json:"refunded_amount"` // 已退款佣金金额
QueryParams map[string]interface{} `json:"query_params,omitempty"` NetAmount float64 `json:"net_amount"` // 剩余净佣金金额 = amount - refunded_amount
Status int64 `json:"status"` // 状态0-已结算1-冻结中2-已退款
CreateTime string `json:"create_time"`
QueryParams map[string]interface{} `json:"query_params,omitempty"`
} }
GetCommissionReq { GetCommissionReq {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码

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"
@@ -82,9 +89,10 @@ TaxConfig:
TaxRate: 0.06 TaxRate: 0.06
TaxExemptionAmount: 0.00 TaxExemptionAmount: 0.00
Tianyuanapi: Tianyuanapi:
AccessID: "7f8a9b2c4d5e6f1a" AccessID: "9e60b34eb51f3827"
Key: "9e4f8a1b3c6d7e2f5a8b9c0d1e4f7a2b" Key: "04c6b4c559be6d5ba5351c04c8713a64"
BaseURL: "https://api.tianyuanapi.com" BaseURL: "https://api.tianyuanapi.com"
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"
@@ -83,9 +92,10 @@ TaxConfig:
TaxRate: 0.06 TaxRate: 0.06
TaxExemptionAmount: 0.00 TaxExemptionAmount: 0.00
Tianyuanapi: Tianyuanapi:
AccessID: "7f8a9b2c4d5e6f1a" AccessID: "9e60b34eb51f3827"
Key: "9e4f8a1b3c6d7e2f5a8b9c0d1e4f7a2b" Key: "04c6b4c559be6d5ba5351c04c8713a64"
BaseURL: "https://api.tianyuanapi.com" BaseURL: "https://api.tianyuanapi.com"
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

@@ -7,16 +7,17 @@ import (
type Config struct { type Config struct {
rest.RestConf rest.RestConf
DataSource string DataSource string
CacheRedis cache.CacheConf CacheRedis cache.CacheConf
JwtAuth JwtAuth // JWT 鉴权相关配置 JwtAuth JwtAuth // JWT 鉴权相关配置
VerifyCode VerifyCode VerifyCode VerifyCode
Encrypt Encrypt Captcha CaptchaConfig
Alipay AlipayConfig Encrypt Encrypt
Wxpay WxpayConfig Alipay AlipayConfig
Applepay ApplepayConfig Wxpay WxpayConfig
Ali AliConfig Applepay ApplepayConfig
Tianyuanapi TianyuanapiConfig Ali AliConfig
Tianyuanapi TianyuanapiConfig
SystemConfig SystemConfig SystemConfig SystemConfig
WechatH5 WechatH5Config WechatH5 WechatH5Config
Authorization AuthorizationConfig // 授权书配置 Authorization AuthorizationConfig // 授权书配置
@@ -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,15 +101,16 @@ 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
AppSecret string AppSecret string
} }
type WechatMiniConfig struct { type WechatMiniConfig struct {
AppID string AppID string
AppSecret string AppSecret string
} }
type QueryConfig struct { type QueryConfig struct {
ShareLinkExpire int64 ShareLinkExpire int64
@@ -126,4 +137,4 @@ type TianyuanapiConfig struct {
type AuthorizationConfig struct { type AuthorizationConfig struct {
FileBaseURL string // 授权书文件访问基础URL FileBaseURL string // 授权书文件访问基础URL
} }

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,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 AdminGetAgentLinkProductStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetAgentLinkProductStatisticsReq
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.NewAdminGetAgentLinkProductStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetAgentLinkProductStatistics(&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 AdminGetAgentOrderStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetAgentOrderStatisticsReq
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.NewAdminGetAgentOrderStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetAgentOrderStatistics(&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 AdminGetAgentStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetAgentStatisticsReq
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.NewAdminGetAgentStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetAgentStatistics(&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 AdminGetWithdrawalStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetWithdrawalStatisticsReq
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.NewAdminGetWithdrawalStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetWithdrawalStatistics(&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 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

@@ -0,0 +1,30 @@
package admin_order
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_order"
"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 AdminGetOrderSourceStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetOrderSourceStatisticsReq
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_order.NewAdminGetOrderSourceStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetOrderSourceStatistics(&req)
result.HttpResult(r, w, resp, err)
}
}

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
package admin_order
import (
"net/http"
"tydata-server/app/main/api/internal/logic/admin_order"
"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 AdminGetRevenueStatisticsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminGetRevenueStatisticsReq
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_order.NewAdminGetRevenueStatisticsLogic(r.Context(), svcCtx)
resp, err := l.AdminGetRevenueStatistics(&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,16 +42,31 @@ 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",
Handler: admin_agent.AdminGetAgentLinkListHandler(serverCtx), Handler: admin_agent.AdminGetAgentLinkListHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/agent-link/product-statistics",
Handler: admin_agent.AdminGetAgentLinkProductStatisticsHandler(serverCtx),
},
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/agent-membership-config/list", Path: "/agent-membership-config/list",
@@ -67,6 +82,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/agent-membership-recharge-order/list", Path: "/agent-membership-recharge-order/list",
Handler: admin_agent.AdminGetAgentMembershipRechargeOrderListHandler(serverCtx), Handler: admin_agent.AdminGetAgentMembershipRechargeOrderListHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/agent-order/statistics",
Handler: admin_agent.AdminGetAgentOrderStatisticsHandler(serverCtx),
},
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/agent-platform-deduction/list", Path: "/agent-platform-deduction/list",
@@ -97,11 +117,46 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/agent-withdrawal/list", Path: "/agent-withdrawal/list",
Handler: admin_agent.AdminGetAgentWithdrawalListHandler(serverCtx), Handler: admin_agent.AdminGetAgentWithdrawalListHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/agent-withdrawal/statistics",
Handler: admin_agent.AdminGetWithdrawalStatisticsHandler(serverCtx),
},
{ {
Method: http.MethodGet, Method: http.MethodGet,
Path: "/list", Path: "/list",
Handler: admin_agent.AdminGetAgentListHandler(serverCtx), Handler: admin_agent.AdminGetAgentListHandler(serverCtx),
}, },
{
Method: http.MethodGet,
Path: "/statistics",
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"),
@@ -309,6 +364,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/list", Path: "/list",
Handler: admin_order.AdminGetOrderListHandler(serverCtx), Handler: admin_order.AdminGetOrderListHandler(serverCtx),
}, },
{
// 获取退款统计数据
Method: http.MethodGet,
Path: "/refund-statistics",
Handler: admin_order.AdminGetRefundStatisticsHandler(serverCtx),
},
{ {
// 订单退款 // 订单退款
Method: http.MethodPost, Method: http.MethodPost,
@@ -321,6 +382,24 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/retry-agent-process/:id", Path: "/retry-agent-process/:id",
Handler: admin_order.AdminRetryAgentProcessHandler(serverCtx), Handler: admin_order.AdminRetryAgentProcessHandler(serverCtx),
}, },
{
// 获取收入和利润统计数据
Method: http.MethodGet,
Path: "/revenue-statistics",
Handler: admin_order.AdminGetRevenueStatisticsHandler(serverCtx),
},
{
// 获取订单来源统计数据
Method: http.MethodGet,
Path: "/source-statistics",
Handler: admin_order.AdminGetOrderSourceStatisticsHandler(serverCtx),
},
{
// 获取订单统计数据
Method: http.MethodGet,
Path: "/statistics",
Handler: admin_order.AdminGetOrderStatisticsHandler(serverCtx),
},
{ {
// 更新订单 // 更新订单
Method: http.MethodPut, Method: http.MethodPut,
@@ -524,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,100 @@
package admin_agent
import (
"context"
"fmt"
"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 AdminGetAgentLinkProductStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentLinkProductStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentLinkProductStatisticsLogic {
return &AdminGetAgentLinkProductStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentLinkProductStatisticsLogic) AdminGetAgentLinkProductStatistics(req *types.AdminGetAgentLinkProductStatisticsReq) (resp *types.AdminGetAgentLinkProductStatisticsResp, err error) {
// 构建查询
query := squirrel.Select(
"p.product_name",
"COUNT(al.id) as link_count",
).
From("agent_link al").
Join("product p ON al.product_id = p.id").
Where(squirrel.Eq{"al.del_state": 0}).
Where(squirrel.Eq{"p.del_state": 0}).
GroupBy("p.product_name").
OrderBy("link_count DESC")
// 执行查询
sql, args, err := query.ToSql()
if err != nil {
return nil, err
}
type Result struct {
ProductName string `db:"product_name"`
LinkCount int64 `db:"link_count"`
}
var results []Result
// 使用模型的方法执行查询
// 通过反射获取底层的QueryRowsNoCacheCtx方法避免直接类型断言
if agentLinkModel, ok := l.svcCtx.AgentLinkModel.(interface {
QueryRowsNoCacheCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
}); ok {
err = agentLinkModel.QueryRowsNoCacheCtx(l.ctx, &results, sql, args...)
} else {
// 如果无法使用模型的方法,则使用原始的连接方式(安全地获取连接)
if cachedConn, ok := l.svcCtx.AgentLinkModel.(interface {
GetConn() interface{}
}); ok {
conn := cachedConn.GetConn()
if sqlxConn, ok := conn.(interface {
QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
}); ok {
err = sqlxConn.QueryRowsCtx(l.ctx, &results, sql, args...)
} else {
return nil, fmt.Errorf("无法获取数据库连接")
}
} else {
return nil, fmt.Errorf("无法获取数据库连接")
}
}
if err != nil {
return nil, err
}
// 处理空结果
if len(results) == 0 {
return &types.AdminGetAgentLinkProductStatisticsResp{
Items: []types.AgentLinkProductStatisticsItem{},
}, nil
}
// 转换为返回结果
items := make([]types.AgentLinkProductStatisticsItem, 0, len(results))
for _, r := range results {
items = append(items, types.AgentLinkProductStatisticsItem{
ProductName: r.ProductName,
LinkCount: r.LinkCount,
})
}
return &types.AdminGetAgentLinkProductStatisticsResp{
Items: items,
}, nil
}

View File

@@ -0,0 +1,60 @@
package admin_agent
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentOrderStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentOrderStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentOrderStatisticsLogic {
return &AdminGetAgentOrderStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentOrderStatisticsLogic) AdminGetAgentOrderStatistics(req *types.AdminGetAgentOrderStatisticsReq) (resp *types.AdminGetAgentOrderStatisticsResp, err error) {
// 获取今日的开始和结束时间
today := time.Now()
startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
endOfDay := startOfDay.Add(24 * time.Hour)
// 构建查询条件
builder := l.svcCtx.AgentOrderModel.SelectBuilder()
// 查询总代理订单数
totalBuilder := builder
totalAgentOrderCount, err := l.svcCtx.AgentOrderModel.FindCount(l.ctx, totalBuilder, "id")
if err != nil {
logx.Errorf("查询总代理订单数失败: %v", err)
return nil, fmt.Errorf("查询总代理订单数失败: %w", err)
}
// 查询今日代理订单数
todayBuilder := builder.Where("create_time >= ? AND create_time < ?", startOfDay, endOfDay)
todayAgentOrderCount, err := l.svcCtx.AgentOrderModel.FindCount(l.ctx, todayBuilder, "id")
if err != nil {
logx.Errorf("查询今日代理订单数失败: %v", err)
return nil, fmt.Errorf("查询今日代理订单数失败: %w", err)
}
// 构建响应
resp = &types.AdminGetAgentOrderStatisticsResp{
TotalAgentOrderCount: totalAgentOrderCount,
TodayAgentOrderCount: todayAgentOrderCount,
}
return resp, nil
}

View File

@@ -0,0 +1,56 @@
package admin_agent
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentStatisticsLogic {
return &AdminGetAgentStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentStatisticsLogic) AdminGetAgentStatistics(req *types.AdminGetAgentStatisticsReq) (resp *types.AdminGetAgentStatisticsResp, err error) {
// 使用AgentModel的SelectBuilder和FindCount方法获取总代理数
totalBuilder := l.svcCtx.AgentModel.SelectBuilder()
totalAgentCount, err := l.svcCtx.AgentModel.FindCount(l.ctx, totalBuilder, "id")
if err != nil {
logx.Errorf("获取总代理数失败: %v", err)
return nil, fmt.Errorf("获取总代理数失败: %w", err)
}
// 获取今日新增代理数
todayBuilder := l.svcCtx.AgentModel.SelectBuilder()
today := time.Now()
startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
endOfDay := startOfDay.Add(24 * time.Hour)
todayBuilder = todayBuilder.Where("create_time >= ? AND create_time < ?", startOfDay, endOfDay)
todayAgentCount, err := l.svcCtx.AgentModel.FindCount(l.ctx, todayBuilder, "id")
if err != nil {
logx.Errorf("获取今日新增代理数失败: %v", err)
return nil, fmt.Errorf("获取今日新增代理数失败: %w", err)
}
resp = &types.AdminGetAgentStatisticsResp{
TotalAgentCount: totalAgentCount,
TodayAgentCount: todayAgentCount,
}
return resp, nil
}

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
@@ -49,7 +52,7 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
item.Remark = v.Remark.String item.Remark = v.Remark.String
} }
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05") item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
// 手动设置银行卡信息copier不会自动处理sql.NullString // 手动设置银行卡信息copier不会自动处理sql.NullString
item.WithdrawType = v.WithdrawType item.WithdrawType = v.WithdrawType
if v.BankCardNo.Valid { if v.BankCardNo.Valid {
@@ -61,7 +64,7 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
if v.PayeeName.Valid { if v.PayeeName.Valid {
item.PayeeName = v.PayeeName.String item.PayeeName = v.PayeeName.String
} }
items = append(items, item) items = append(items, item)
} }
resp = &types.AdminGetAgentWithdrawalListResp{ resp = &types.AdminGetAgentWithdrawalListResp{

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

@@ -0,0 +1,76 @@
package admin_agent
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetWithdrawalStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetWithdrawalStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetWithdrawalStatisticsLogic {
return &AdminGetWithdrawalStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetWithdrawalStatisticsLogic) AdminGetWithdrawalStatistics(req *types.AdminGetWithdrawalStatisticsReq) (resp *types.AdminGetWithdrawalStatisticsResp, err error) {
// 获取今日的开始和结束时间
today := time.Now()
startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
endOfDay := startOfDay.Add(24 * time.Hour)
// 构建查询条件
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder()
// 查询总提现金额status=2表示成功
totalBuilder := builder.Where("status = ?", 2)
totalWithdrawalAmount, err := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, totalBuilder, "amount")
if err != nil {
logx.Errorf("查询总提现金额失败: %v", err)
return nil, fmt.Errorf("查询总提现金额失败: %w", err)
}
// 查询今日提现金额status=2表示成功
todayBuilder := builder.Where("status = ? AND create_time >= ? AND create_time < ?", 2, startOfDay, endOfDay)
todayWithdrawalAmount, err := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, todayBuilder, "amount")
if err != nil {
logx.Errorf("查询今日提现金额失败: %v", err)
return nil, fmt.Errorf("查询今日提现金额失败: %w", err)
}
// 查询总实际到账金额status=2表示成功
totalActualAmount, err := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, totalBuilder, "actual_amount")
if err != nil {
logx.Errorf("查询总实际到账金额失败: %v", err)
return nil, fmt.Errorf("查询总实际到账金额失败: %w", err)
}
// 查询总扣税金额status=2表示成功
totalTaxAmount, err := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, totalBuilder, "tax_amount")
if err != nil {
logx.Errorf("查询总扣税金额失败: %v", err)
return nil, fmt.Errorf("查询总扣税金额失败: %w", err)
}
// 构建响应
resp = &types.AdminGetWithdrawalStatisticsResp{
TotalWithdrawalAmount: totalWithdrawalAmount,
TodayWithdrawalAmount: todayWithdrawalAmount,
TotalActualAmount: totalActualAmount,
TotalTaxAmount: totalTaxAmount,
}
return resp, nil
}

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"
@@ -24,9 +25,9 @@ const (
// 状态常量 // 状态常量
const ( const (
StatusPending = 1 // 申请中/处理中 StatusPending = 1 // 申请中/处理中
StatusSuccess = 2 // 成功 StatusSuccess = 2 // 成功
StatusFailed = 3 // 失败 StatusFailed = 3 // 失败
) )
// 提现类型常量 // 提现类型常量
@@ -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

@@ -115,6 +115,7 @@ func (l *AdminGetOrderDetailLogic) AdminGetOrderDetail(req *types.AdminGetOrderD
PaymentPlatform: order.PaymentPlatform, PaymentPlatform: order.PaymentPlatform,
PaymentScene: order.PaymentScene, PaymentScene: order.PaymentScene,
Amount: order.Amount, Amount: order.Amount,
SalesCost: order.SalesCost,
Status: order.Status, Status: order.Status,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"), CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: order.UpdateTime.Format("2006-01-02 15:04:05"), UpdateTime: order.UpdateTime.Format("2006-01-02 15:04:05"),

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
@@ -264,6 +280,7 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
PaymentPlatform: order.PaymentPlatform, PaymentPlatform: order.PaymentPlatform,
PaymentScene: order.PaymentScene, PaymentScene: order.PaymentScene,
Amount: order.Amount, Amount: order.Amount,
SalesCost: order.SalesCost,
Status: order.Status, Status: order.Status,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"), CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
QueryState: queryStateMap[order.Id], QueryState: queryStateMap[order.Id],
@@ -293,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

@@ -0,0 +1,87 @@
package admin_order
import (
"context"
"fmt"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetOrderSourceStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetOrderSourceStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetOrderSourceStatisticsLogic {
return &AdminGetOrderSourceStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetOrderSourceStatisticsLogic) AdminGetOrderSourceStatistics(req *types.AdminGetOrderSourceStatisticsReq) (resp *types.AdminGetOrderSourceStatisticsResp, err error) {
// 查询所有有产品ID的订单
builder := l.svcCtx.OrderModel.SelectBuilder()
builder = builder.Where("product_id IS NOT NULL")
// 获取所有符合条件的订单
orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, builder, "id DESC")
if err != nil {
logx.Errorf("查询订单列表失败: %v", err)
return nil, fmt.Errorf("查询订单列表失败: %w", err)
}
logx.Infof("获取到订单数量: %d", len(orders))
// 统计每个产品的订单数量
productCountMap := make(map[int64]int64)
for _, order := range orders {
productCountMap[order.ProductId]++
}
// 构建返回结果
items := make([]types.OrderSourceStatisticsItem, 0, len(productCountMap))
for productId, count := range productCountMap {
// 获取产品信息
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, productId)
if err != nil {
logx.Errorf("查询产品信息失败 productId=%d, err=%v", productId, err)
// 如果查询失败,使用默认值
items = append(items, types.OrderSourceStatisticsItem{
ProductName: "未知产品",
OrderCount: count,
})
continue
}
items = append(items, types.OrderSourceStatisticsItem{
ProductName: product.ProductName,
OrderCount: count,
})
}
// 按订单数量降序排序
for i := 0; i < len(items)-1; i++ {
for j := i + 1; j < len(items); j++ {
if items[i].OrderCount < items[j].OrderCount {
items[i], items[j] = items[j], items[i]
}
}
}
logx.Infof("查询到订单来源统计数据: %d 个产品,订单总数: %d", len(items), len(orders))
return &types.AdminGetOrderSourceStatisticsResp{
Items: items,
}, nil
}
// 定义结果结构体
type OrderSourceResult struct {
ProductName string `db:"product_name"`
OrderCount int64 `db:"order_count"`
}

View File

@@ -0,0 +1,106 @@
package admin_order
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetOrderStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetOrderStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetOrderStatisticsLogic {
return &AdminGetOrderStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetOrderStatisticsLogic) AdminGetOrderStatistics(req *types.AdminGetOrderStatisticsReq) (resp *types.AdminGetOrderStatisticsResp, err error) {
// 获取当前时间
now := time.Now()
var startTime, endTime time.Time
// 根据时间维度设置时间范围和格式
switch req.Dimension {
case "day":
// 日当月1号到今天
startTime = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
case "month":
// 月今年1月到当月
startTime = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), now.Month(), 1, 23, 59, 59, 999999999, now.Location()).AddDate(0, 1, -1)
case "year":
// 年过去5年
startTime = time.Date(now.Year()-5, 1, 1, 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), 12, 31, 23, 59, 59, 999999999, now.Location())
case "all":
// 全部:所有时间,但按日统计
startTime = time.Date(2020, 1, 1, 0, 0, 0, 0, now.Location()) // 假设从2020年开始
endTime = now
default:
// 默认为日
startTime = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
}
// 构建查询条件
builder := l.svcCtx.OrderModel.SelectBuilder().
Where("create_time >= ? AND create_time <= ?", startTime, endTime).
Where("status = ?", "paid") // 只统计已支付的订单
// 查询所有符合条件的订单
orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, builder, "create_time ASC")
if err != nil {
logx.Errorf("查询订单统计数据失败: %v", err)
return nil, fmt.Errorf("查询订单统计数据失败: %w", err)
}
// 按日期分组统计
dateMap := make(map[string]*types.OrderStatisticsItem)
for _, order := range orders {
var dateKey string
if req.Dimension == "year" {
dateKey = order.CreateTime.Format("2006")
} else if req.Dimension == "month" {
dateKey = order.CreateTime.Format("2006-01")
} else {
dateKey = order.CreateTime.Format("2006-01-02")
}
if item, exists := dateMap[dateKey]; exists {
item.Count++
item.Amount += order.Amount
} else {
dateMap[dateKey] = &types.OrderStatisticsItem{
Date: dateKey,
Count: 1,
Amount: order.Amount,
}
}
}
// 转换为切片
items := make([]types.OrderStatisticsItem, 0, len(dateMap))
for date := range dateMap {
items = append(items, *dateMap[date])
}
// 构建响应
resp = &types.AdminGetOrderStatisticsResp{
Items: items,
}
return resp, nil
}

View File

@@ -0,0 +1,60 @@
package admin_order
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetRefundStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetRefundStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetRefundStatisticsLogic {
return &AdminGetRefundStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetRefundStatisticsLogic) AdminGetRefundStatistics(req *types.AdminGetRefundStatisticsReq) (resp *types.AdminGetRefundStatisticsResp, err error) {
// 获取今日的开始和结束时间
today := time.Now()
startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
endOfDay := startOfDay.Add(24 * time.Hour)
// 构建查询条件
builder := l.svcCtx.OrderModel.SelectBuilder()
// 查询总退款金额status=refunded表示已退款
totalBuilder := builder.Where("status = ?", "refunded")
totalRefundAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, totalBuilder, "amount")
if err != nil {
logx.Errorf("查询总退款金额失败: %v", err)
return nil, fmt.Errorf("查询总退款金额失败: %w", err)
}
// 查询今日退款金额status=refunded表示已退款且退款时间为今日
todayBuilder := builder.Where("status = ? AND refund_time >= ? AND refund_time < ?", "refunded", startOfDay, endOfDay)
todayRefundAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, todayBuilder, "amount")
if err != nil {
logx.Errorf("查询今日退款金额失败: %v", err)
return nil, fmt.Errorf("查询今日退款金额失败: %w", err)
}
// 构建响应
resp = &types.AdminGetRefundStatisticsResp{
TotalRefundAmount: totalRefundAmount,
TodayRefundAmount: todayRefundAmount,
}
return resp, nil
}

View File

@@ -0,0 +1,143 @@
package admin_order
import (
"context"
"fmt"
"time"
"tydata-server/app/main/api/internal/svc"
"tydata-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetRevenueStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetRevenueStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetRevenueStatisticsLogic {
return &AdminGetRevenueStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetRevenueStatisticsLogic) AdminGetRevenueStatistics(req *types.AdminGetRevenueStatisticsReq) (resp *types.AdminGetRevenueStatisticsResp, err error) {
// 获取今日的开始和结束时间
today := time.Now()
startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
endOfDay := startOfDay.Add(24 * time.Hour)
// 构建查询条件
builder := l.svcCtx.OrderModel.SelectBuilder()
// 查询总流水收入金额status=paid表示已支付
totalRevenueBuilder := builder.Where("status = ?", "paid")
totalRevenueAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, totalRevenueBuilder, "amount")
if err != nil {
logx.Errorf("查询总收入金额失败: %v", err)
return nil, fmt.Errorf("查询总收入金额失败: %w", err)
}
// 查询今日流水收入金额status=paid表示已支付且支付时间为今日
todayRevenueBuilder := builder.Where("status = ? AND pay_time >= ? AND pay_time < ?", "paid", startOfDay, endOfDay)
todayRevenueAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, todayRevenueBuilder, "amount")
if err != nil {
logx.Errorf("查询今日收入金额失败: %v", err)
return nil, fmt.Errorf("查询今日收入金额失败: %w", err)
}
// 查询代理订单金额总和只查询agent_platform_deduction表不进行联表
deductionAmount, err := l.svcCtx.AgentPlatformDeductionModel.FindSum(
l.ctx,
l.svcCtx.AgentPlatformDeductionModel.SelectBuilder(),
"amount")
if err != nil {
logx.Errorf("查询代理订单金额总和失败: %v", err)
return nil, fmt.Errorf("查询代理订单金额总和失败: %w", err)
}
// 计算非代理订单金额使用订单表作为主表关联agent_platform_deduction表查询没有代理记录的订单
// 使用子查询方式避免JOIN与order/list接口保持一致的查询风格
nonDeductionBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND id NOT IN (SELECT order_id FROM agent_platform_deduction WHERE order_id IS NOT NULL)", "paid")
nonDeductionAmount, err := l.svcCtx.OrderModel.FindSum(
l.ctx,
nonDeductionBuilder,
"amount")
if err != nil {
logx.Errorf("查询非代理订单金额失败: %v", err)
return nil, fmt.Errorf("查询非代理订单金额失败: %w", err)
}
// 查询订单成本总和只查询order表不进行联表
orderCostBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND del_state = 0", "paid")
orderCostAmount, err := l.svcCtx.OrderModel.FindSum(
l.ctx,
orderCostBuilder,
"sales_cost")
if err != nil {
logx.Errorf("查询订单成本总和失败: %v", err)
return nil, fmt.Errorf("查询订单成本总和失败: %w", err)
}
// 计算总利润 = 代理订单金额总和 + 非代理订单金额总和 - 订单成本总和
// 总收入 = 代理订单金额 + 非代理订单金额
// 总利润 = 总收入 - 所有订单成本
totalProfitAmount := deductionAmount + nonDeductionAmount - orderCostAmount
// 计算今日利润 = 今日代理订单金额 + 今日非代理订单金额 - 今日订单成本总和
// 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{
TotalRevenueAmount: totalRevenueAmount,
TodayRevenueAmount: todayRevenueAmount,
TotalProfitAmount: totalProfitAmount,
TodayProfitAmount: todayProfitAmount,
}
return resp, nil
}

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,17 +44,20 @@ 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)
} }
// 检查手机号是否在一分钟内已发送过验证码 // 开发环境下跳过验证码验证
redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile) if os.Getenv("ENV") != "development" {
cacheCode, err := l.svcCtx.Redis.Get(redisKey) // 检查手机号是否在一分钟内已发送过验证码
if err != nil { redisKey := fmt.Sprintf("%s:%s", "realName", encryptedMobile)
if errors.Is(err, redis.Nil) { cacheCode, err := l.svcCtx.Redis.Get(redisKey)
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理实名, 验证码过期: %s", encryptedMobile) if err != nil {
if errors.Is(err, redis.Nil) {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理实名, 验证码过期: %s", encryptedMobile)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理实名, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
}
if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理实名, 验证码不正确: %s", encryptedMobile)
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理实名, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
}
if cacheCode != req.Code {
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 {

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) {
@@ -109,14 +111,14 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
) )
// 统一扣税逻辑所有提现都按6%收取税收 // 统一扣税逻辑所有提现都按6%收取税收
exemptionAmount = 0 // 免税金额 = 0 exemptionAmount = 0 // 免税金额 = 0
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
taxDeductionPart = req.Amount // 应税金额 = 提现金额 taxDeductionPart = req.Amount // 应税金额 = 提现金额
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率 taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
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 { withdrawRes.Status = WithdrawStatusProcessing
l.Logger.Errorf("【支付宝转账失败】outBizNo:%s error:%v", outBizNo, err) withdrawRes.FailMsg = ""
l.handleTransferError(outBizNo, err, "支付宝接口调用失败")
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
}
switch { l.Logger.Infof("支付宝提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount)
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
default:
// 未知状态按失败处理
l.handleTransferError(outBizNo, fmt.Errorf("未知状态:%s", transferResp.Status), "支付宝返回未知状态")
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

@@ -118,11 +118,11 @@ func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdraw
) )
// 统一扣税逻辑所有提现都按6%收取税收 // 统一扣税逻辑所有提现都按6%收取税收
exemptionAmount = 0 // 免税金额 = 0 exemptionAmount = 0 // 免税金额 = 0
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税 TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
taxDeductionPart = req.Amount // 应税金额 = 提现金额 taxDeductionPart = req.Amount // 应税金额 = 提现金额
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率 taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费 finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
// 创建提现记录(初始状态为申请中,提现类型为银行卡) // 创建提现记录(初始状态为申请中,提现类型为银行卡)
withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, req.Amount, finalWithdrawAmount, taxAmount, outBizNo) withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
@@ -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,13 +58,22 @@ 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)
} }
commission.CreateTime = agentCommissionModel.CreateTime.Format("2006-01-02 15:04:05") commission.CreateTime = agentCommissionModel.CreateTime.Format("2006-01-02 15:04:05")
commission.ProductName = product.ProductName commission.ProductName = product.ProductName
// 从 order 表获取 platform_order_id // 从 order 表获取 platform_order_id
orderModel, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, agentCommissionModel.OrderId) orderModel, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, agentCommissionModel.OrderId)
if findOrderErr == nil && orderModel != nil && orderModel.PlatformOrderId.Valid { if findOrderErr == nil && orderModel != nil && orderModel.PlatformOrderId.Valid {

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

@@ -62,7 +62,7 @@ func (l *GetBankCardInfoLogic) GetBankCardInfo(req *types.GetBankCardInfoReq) (r
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder() builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder()
builder = builder.Where(squirrel.Eq{"agent_id": agentModel.Id}) builder = builder.Where(squirrel.Eq{"agent_id": agentModel.Id})
builder = builder.Where(squirrel.Eq{"withdraw_type": 2}) // 银行卡提现 builder = builder.Where(squirrel.Eq{"withdraw_type": 2}) // 银行卡提现
builder = builder.Where(squirrel.Eq{"status": 2}) // 成功状态 builder = builder.Where(squirrel.Eq{"status": 2}) // 成功状态
builder = builder.OrderBy("create_time DESC") builder = builder.OrderBy("create_time DESC")
builder = builder.Limit(1) builder = builder.Limit(1)

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状态的退款记录
@@ -174,7 +324,7 @@ func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWr
var statusDetermined bool = false var statusDetermined bool = false
if notification.Status != nil { if notification.Status != nil {
status = *notification.Status status = *notification.Status
statusDetermined = true statusDetermined = true
} else if notification.SuccessTime != nil && !notification.SuccessTime.IsZero() { } else if notification.SuccessTime != nil && !notification.SuccessTime.IsZero() {
// 如果Status为空但SuccessTime有值说明退款成功 // 如果Status为空但SuccessTime有值说明退款成功
@@ -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,17 +38,20 @@ 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)
} }
// 检查手机号是否在一分钟内已发送过验证码 // 开发环境下跳过验证码验证
redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile) if os.Getenv("ENV") != "development" {
cacheCode, err := l.svcCtx.Redis.Get(redisKey) // 检查手机号是否在一分钟内已发送过验证码
if err != nil { redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile)
if errors.Is(err, redis.Nil) { cacheCode, err := l.svcCtx.Redis.Get(redisKey)
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机登录, 验证码过期: %s", encryptedMobile) if err != nil {
if errors.Is(err, redis.Nil) {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机登录, 验证码过期: %s", encryptedMobile)
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
}
if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile)
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
}
if cacheCode != req.Code {
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})
@@ -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,10 +143,53 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
decryptData = updatedDecryptData decryptData = updatedDecryptData
} }
} }
// 调用API请求服务开发环境下不调用其它产品使用默认空报告
var responseData []service.APIResponseData
if env == "development" {
// 开发环境:生成仅包含基本信息的默认空报告,不调用外部 API
// 空报告模式生成空的报告数据跳过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)
}
}
// 调用API请求服务 // 计算成功模块的总成本价
combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id) totalCostPrice := 0.0
if err != nil { if responseData != nil {
for _, item := range responseData {
if item.Success {
// 根据API ID查找功能模块
feature, err := l.svcCtx.FeatureModel.FindOneByApiId(ctx, item.ApiID)
if err != nil {
logx.Errorf("查找功能模块失败, API ID: %s, 错误: %v", item.ApiID, err)
continue
}
// 累加成本价
totalCostPrice += feature.CostPrice
}
}
}
// 更新订单的销售成本
order.SalesCost = totalCostPrice
updateOrderErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, nil, order)
if updateOrderErr != nil {
logx.Errorf("更新订单销售成本失败, 订单ID: %d, 错误: %v", order.Id, updateOrderErr)
// 注意:这里不返回错误,因为订单销售成本更新失败不应影响整个查询流程
} else {
logx.Infof("成功更新订单销售成本, 订单ID: %d, 总成本价: %f", order.Id, totalCostPrice)
}
// 对返回的类型进行二进制转换
combinedResponse, marshalErr := json.Marshal(responseData)
if marshalErr != nil {
err = fmt.Errorf("响应数据转 JSON 失败: %v", marshalErr)
return l.handleError(ctx, err, order, query) return l.handleError(ctx, err, order, query)
} }
// 加密返回响应 // 加密返回响应
@@ -212,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)
@@ -232,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,
} }
} }
@@ -80,13 +88,13 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return findAgentProductConfigModelErr return findAgentProductConfigModelErr
} }
// 平台底价成本 // 平台底价成本
PlatformCostAmount, platformCostErr := l.PlatformCost(transCtx, agentID, agentProductConfigModel, session) PlatformCostAmount, platformCostErr := l.PlatformCost(transCtx, agentID, order.Id, agentProductConfigModel, session)
if platformCostErr != nil { if platformCostErr != nil {
return platformCostErr return platformCostErr
} }
// 平台提价成本 // 平台提价成本
PlatformPricingAmount, platformPricingErr := l.PlatformPricing(transCtx, agentID, order.Amount, agentProductConfigModel, session) PlatformPricingAmount, platformPricingErr := l.PlatformPricing(transCtx, agentID, order.Id, order.Amount, agentProductConfigModel, session)
if platformPricingErr != nil { if platformPricingErr != nil {
return platformPricingErr return platformPricingErr
} }
@@ -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
} }
ancestorWallet.Balance += ancestorCommissionAmount // 记录变动前的余额
ancestorWallet.TotalEarnings += ancestorCommissionAmount 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
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
agentWalletModel.Balance += finalCommission
// 记录变动前的余额
balanceBefore := agentWalletModel.Balance
frozenBalanceBefore := agentWalletModel.FrozenBalance
// 根据安全防御模式配置决定佣金状态和钱包操作
if l.config.SystemConfig.CommissionSafeMode {
// 安全防御模式佣金冻结在frozen_balance中
agentWalletModel.FrozenBalance += finalCommission
} else {
// 非安全防御模式佣金直接进入balance
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
} }
@@ -227,10 +371,11 @@ func (l *AgentService) AncestorCommission(ctx context.Context, descendantId int6
} }
// PlatformCost 平台底价成本 // PlatformCost 平台底价成本
func (l *AgentService) PlatformCost(ctx context.Context, agentID int64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) { func (l *AgentService) PlatformCost(ctx context.Context, agentID int64, orderID int64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) {
costAgentPlatformDeductionModel := model.AgentPlatformDeduction{ costAgentPlatformDeductionModel := model.AgentPlatformDeduction{
AgentId: agentID, AgentId: agentID,
OrderId: orderID,
Amount: agentProductConfigModel.CostPrice, Amount: agentProductConfigModel.CostPrice,
Type: model.AgentDeductionTypeCost, Type: model.AgentDeductionTypeCost,
} }
@@ -243,7 +388,7 @@ func (l *AgentService) PlatformCost(ctx context.Context, agentID int64, agentPro
} }
// PlatformPricing 平台提价成本 // PlatformPricing 平台提价成本
func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, pricing float64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) { func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, orderID int64, pricing float64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) {
// 2. 计算平台提价成本 // 2. 计算平台提价成本
if pricing > agentProductConfigModel.PricingStandard { if pricing > agentProductConfigModel.PricingStandard {
// 超出部分 // 超出部分
@@ -254,6 +399,7 @@ func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, prici
pricingAgentPlatformDeductionModel := model.AgentPlatformDeduction{ pricingAgentPlatformDeductionModel := model.AgentPlatformDeduction{
AgentId: agentID, AgentId: agentID,
OrderId: orderID,
Amount: overpricingCost, Amount: overpricingCost,
Type: model.AgentDeductionTypePricing, Type: model.AgentDeductionTypePricing,
} }
@@ -268,7 +414,7 @@ func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, prici
} }
// 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)
@@ -288,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)
@@ -301,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 {
// 拥有则查看该上级设定的成本 // 拥有则查看该上级设定的成本
@@ -332,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 {
@@ -384,7 +532,7 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old
// 获取各等级的奖励金额 // 获取各等级的奖励金额
var vipRewardAmount float64 var vipRewardAmount float64
var svipRewardAmount float64 var svipRewardAmount float64
if agentMembershipConfigModel.LowerConvertVipReward.Valid { if agentMembershipConfigModel.LowerConvertVipReward.Valid {
vipRewardAmount = agentMembershipConfigModel.LowerConvertVipReward.Float64 vipRewardAmount = agentMembershipConfigModel.LowerConvertVipReward.Float64
} }
@@ -430,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
} }
@@ -441,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
@@ -500,7 +678,7 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi
return nil return nil
} }
rewardAmount := withdrawAmount * rewardRatio rewardAmount := withdrawAmount * rewardRatio
if rewardAmount > 0 { if rewardAmount > 0 {
// 创建奖励记录 // 创建奖励记录
agentRewards := model.AgentRewards{ agentRewards := model.AgentRewards{
@@ -510,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
} }
@@ -521,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
}
} }
} }
@@ -586,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

@@ -58,7 +58,7 @@ type APIResponseData struct {
} }
// ProcessRequests 处理请求 // ProcessRequests 处理请求
func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]byte, error) { func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]APIResponseData, error) {
var ctx, cancel = context.WithCancel(context.Background()) var ctx, cancel = context.WithCancel(context.Background())
defer cancel() defer cancel()
build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{ build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{
@@ -166,12 +166,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]b
return nil, fmt.Errorf("请求失败次数超过 %d 次: %v", errorLimit, allErrors) return nil, fmt.Errorf("请求失败次数超过 %d 次: %v", errorLimit, allErrors)
} }
combinedResponse, err := json.Marshal(responseData) return responseData, nil
if err != nil {
return nil, fmt.Errorf("响应数据转 JSON 失败: %v", err)
}
return combinedResponse, nil
} }
// ------------------------------------请求处理器-------------------------- // ------------------------------------请求处理器--------------------------
@@ -200,6 +195,7 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
"IVYZ81NC": (*ApiRequestService).ProcessIVYZ81NCRequest, "IVYZ81NC": (*ApiRequestService).ProcessIVYZ81NCRequest,
"IVYZ7F3A": (*ApiRequestService).ProcessIVYZ7F3ARequest, "IVYZ7F3A": (*ApiRequestService).ProcessIVYZ7F3ARequest,
"IVYZ3P9M": (*ApiRequestService).ProcessIVYZ3P9MRequest, "IVYZ3P9M": (*ApiRequestService).ProcessIVYZ3P9MRequest,
"FLXG7E8F": (*ApiRequestService).ProcessFLXG7E8FRequest,
} }
// PreprocessRequestApi 调用指定的请求处理函数 // PreprocessRequestApi 调用指定的请求处理函数
@@ -1418,6 +1414,7 @@ func (a *ApiRequestService) ProcessIVYZ7F3ARequest(params []byte) ([]byte, error
// 直接返回解密后的数据而不是再次进行JSON编码 // 直接返回解密后的数据而不是再次进行JSON编码
return convertTianyuanResponse(resp) return convertTianyuanResponse(resp)
} }
// ProcessIVYZ3P9MRequest 学历实时查询 // ProcessIVYZ3P9MRequest 学历实时查询
func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error) { func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card") idCard := gjson.GetBytes(params, "id_card")
@@ -1437,3 +1434,25 @@ func (a *ApiRequestService) ProcessIVYZ3P9MRequest(params []byte) ([]byte, error
return convertTianyuanResponse(resp) return convertTianyuanResponse(resp)
} }
// ProcessFLXG7E8FRequest 个人涉诉
func (a *ApiRequestService) ProcessFLXG7E8FRequest(params []byte) ([]byte, error) {
idCard := gjson.GetBytes(params, "id_card")
name := gjson.GetBytes(params, "name")
mobile := gjson.GetBytes(params, "mobile")
if !idCard.Exists() || !name.Exists() || !mobile.Exists() {
return nil, errors.New("api请求, FLXG7E8F, 获取相关参数失败")
}
resp, err := a.tianyuanapi.CallInterface("FLXG7E8F", map[string]interface{}{
"id_card": idCard.String(),
"name": name.String(),
"mobile_no": mobile.String(),
})
if err != nil {
return nil, err
}
return convertTianyuanResponse(resp)
}

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

@@ -254,7 +254,7 @@ func TestGenerateAuthorizationDocument(t *testing.T) {
assert.False(t, authDoc.ExpireTime.Valid) // 永久保留,不设置过期时间 assert.False(t, authDoc.ExpireTime.Valid) // 永久保留,不设置过期时间
// 验证文件路径格式兼容Windows和Unix路径分隔符 // 验证文件路径格式兼容Windows和Unix路径分隔符
assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") || assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") ||
strings.Contains(authDoc.FilePath, "data\\authorization_docs")) strings.Contains(authDoc.FilePath, "data\\authorization_docs"))
assert.Contains(t, authDoc.FileName, "auth_") assert.Contains(t, authDoc.FileName, "auth_")
assert.Contains(t, authDoc.FileName, ".pdf") assert.Contains(t, authDoc.FileName, ".pdf")
@@ -372,7 +372,7 @@ func TestGeneratePDFContent(t *testing.T) {
t.Logf("📁 文件路径: %s", filePath) t.Logf("📁 文件路径: %s", filePath)
t.Logf("🔗 相对路径: %s", relativePath) t.Logf("🔗 相对路径: %s", relativePath)
t.Logf("📊 文件大小: %d 字节", len(pdfBytes)) t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
// 获取绝对路径 // 获取绝对路径
absPath, _ := filepath.Abs(filePath) absPath, _ := filepath.Abs(filePath)
t.Logf("📍 绝对路径: %s", absPath) t.Logf("📍 绝对路径: %s", absPath)
@@ -430,16 +430,16 @@ func TestSavePDFToLocal(t *testing.T) {
t.Logf("📁 文件路径: %s", filePath) t.Logf("📁 文件路径: %s", filePath)
t.Logf("🔗 相对路径: %s", relativePath) t.Logf("🔗 相对路径: %s", relativePath)
t.Logf("📊 文件大小: %d 字节", len(pdfBytes)) t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
// 获取绝对路径 // 获取绝对路径
absPath, _ := filepath.Abs(filePath) absPath, _ := filepath.Abs(filePath)
t.Logf("📍 绝对路径: %s", absPath) t.Logf("📍 绝对路径: %s", absPath)
// 验证文件内容 // 验证文件内容
fileInfo, err := os.Stat(filePath) fileInfo, err := os.Stat(filePath)
assert.NoError(t, err) assert.NoError(t, err)
assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
t.Logf("🎉 文件保存验证通过!") t.Logf("🎉 文件保存验证通过!")
} else { } else {
t.Errorf("❌ PDF文件保存失败: %v", err) t.Errorf("❌ PDF文件保存失败: %v", err)
@@ -648,18 +648,18 @@ func TestGeneratePDFFile(t *testing.T) {
t.Logf("📁 文件路径: %s", authDoc.FilePath) t.Logf("📁 文件路径: %s", authDoc.FilePath)
t.Logf("🔗 相对路径: %s", authDoc.FileUrl) t.Logf("🔗 相对路径: %s", authDoc.FileUrl)
t.Logf("📊 文件大小: %d 字节", authDoc.FileSize) t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
// 验证文件内容 // 验证文件内容
fileInfo, err := os.Stat(authDoc.FilePath) fileInfo, err := os.Stat(authDoc.FilePath)
assert.NoError(t, err) assert.NoError(t, err)
assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
// 验证文件名格式 // 验证文件名格式
assert.Regexp(t, `^auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileName) assert.Regexp(t, `^auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileName)
// 验证路径格式 // 验证路径格式
assert.Regexp(t, `^\d{4}/\d{2}/auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl) assert.Regexp(t, `^\d{4}/\d{2}/auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl)
t.Logf("🎉 所有验证通过!") t.Logf("🎉 所有验证通过!")
} else { } else {
t.Errorf("❌ PDF文件未创建: %s", authDoc.FilePath) t.Errorf("❌ PDF文件未创建: %s", authDoc.FilePath)

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"`
@@ -214,11 +224,14 @@ type AdminGetAgentCommissionDeductionListResp struct {
} }
type AdminGetAgentCommissionListReq struct { 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可选
ProductName *string `form:"product_name,optional"` // 产品名(可选) OrderId *int64 `form:"order_id,optional"` // 订单ID(可选)
Status *int64 `form:"status,optional"` // 状态(可选) ProductName *string `form:"product_name,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 {
@@ -239,6 +252,13 @@ type AdminGetAgentLinkListResp struct {
Items []AgentLinkListItem `json:"items"` // 列表数据 Items []AgentLinkListItem `json:"items"` // 列表数据
} }
type AdminGetAgentLinkProductStatisticsReq struct {
}
type AdminGetAgentLinkProductStatisticsResp struct {
Items []AgentLinkProductStatisticsItem `json:"items"` // 列表数据
}
type AdminGetAgentListReq struct { type AdminGetAgentListReq struct {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
@@ -279,6 +299,14 @@ type AdminGetAgentMembershipRechargeOrderListResp struct {
Items []AgentMembershipRechargeOrderListItem `json:"items"` // 列表数据 Items []AgentMembershipRechargeOrderListItem `json:"items"` // 列表数据
} }
type AdminGetAgentOrderStatisticsReq struct {
}
type AdminGetAgentOrderStatisticsResp struct {
TotalAgentOrderCount int64 `json:"total_agent_order_count"` // 总代理订单数
TodayAgentOrderCount int64 `json:"today_agent_order_count"` // 今日代理订单数
}
type AdminGetAgentPlatformDeductionListReq struct { type AdminGetAgentPlatformDeductionListReq struct {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
@@ -317,12 +345,45 @@ type AdminGetAgentRewardListResp struct {
Items []AgentRewardListItem `json:"items"` // 列表数据 Items []AgentRewardListItem `json:"items"` // 列表数据
} }
type AdminGetAgentStatisticsReq struct {
}
type AdminGetAgentStatisticsResp struct {
TotalAgentCount int64 `json:"total_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 {
@@ -386,11 +447,10 @@ type AdminGetFeatureExampleResp struct {
} }
type AdminGetFeatureListReq struct { type AdminGetFeatureListReq struct {
Page int64 `form:"page"` // 页码 Page int64 `form:"page"` // 页码
PageSize int64 `form:"pageSize"` // 每页数量 PageSize int64 `form:"pageSize"` // 每页数量
ApiId *string `form:"api_id,optional"` // API标识 ApiId *string `form:"api_id,optional"` // API标识
Name *string `form:"name,optional"` // 描述 Name *string `form:"name,optional"` // 描述
CostPrice *float64 `form:"cost_price,optional"` // 成本价
} }
type AdminGetFeatureListResp struct { type AdminGetFeatureListResp struct {
@@ -443,6 +503,7 @@ type AdminGetOrderDetailResp struct {
PaymentPlatform string `json:"payment_platform"` // 支付方式 PaymentPlatform string `json:"payment_platform"` // 支付方式
PaymentScene string `json:"payment_scene"` // 支付平台 PaymentScene string `json:"payment_scene"` // 支付平台
Amount float64 `json:"amount"` // 金额 Amount float64 `json:"amount"` // 金额
SalesCost float64 `json:"sales_cost"` // 成本价
Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败 Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中 QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
@@ -471,6 +532,10 @@ type AdminGetOrderListReq struct {
PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束 PayTimeEnd string `form:"pay_time_end,optional"` // 支付时间结束
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"` // 成本价
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 {
@@ -478,6 +543,21 @@ type AdminGetOrderListResp struct {
Items []OrderListItem `json:"items"` // 列表 Items []OrderListItem `json:"items"` // 列表
} }
type AdminGetOrderSourceStatisticsReq struct {
}
type AdminGetOrderSourceStatisticsResp struct {
Items []OrderSourceStatisticsItem `json:"items"` // 订单来源统计列表
}
type AdminGetOrderStatisticsReq struct {
Dimension string `form:"dimension"` // 时间维度day-日(当月1号到今天)month-月(今年1月到当月)year-年(过去5年)all-全部(按日统计)
}
type AdminGetOrderStatisticsResp struct {
Items []OrderStatisticsItem `json:"items"` // 订单统计列表
}
type AdminGetPlatformUserDetailReq struct { type AdminGetPlatformUserDetailReq struct {
Id int64 `path:"id"` // 用户ID Id int64 `path:"id"` // 用户ID
} }
@@ -488,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"` // 更新时间
} }
@@ -602,6 +683,24 @@ type AdminGetQueryDetailByOrderIdResp struct {
QueryState string `json:"query_state"` // 查询状态 QueryState string `json:"query_state"` // 查询状态
} }
type AdminGetRefundStatisticsReq struct {
}
type AdminGetRefundStatisticsResp struct {
TotalRefundAmount float64 `json:"total_refund_amount"` // 总退款金额
TodayRefundAmount float64 `json:"today_refund_amount"` // 今日退款金额
}
type AdminGetRevenueStatisticsReq struct {
}
type AdminGetRevenueStatisticsResp struct {
TotalRevenueAmount float64 `json:"total_revenue_amount"` // 总收入金额
TodayRevenueAmount float64 `json:"today_revenue_amount"` // 今日收入金额
TotalProfitAmount float64 `json:"total_profit_amount"` // 总利润金额
TodayProfitAmount float64 `json:"today_profit_amount"` // 今日利润金额
}
type AdminGetRoleApiListReq struct { type AdminGetRoleApiListReq struct {
RoleId int64 `path:"role_id"` RoleId int64 `path:"role_id"`
} }
@@ -610,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
} }
@@ -637,6 +740,16 @@ type AdminGetUserListResp struct {
Items []AdminUserListItem `json:"items"` // 列表 Items []AdminUserListItem `json:"items"` // 列表
} }
type AdminGetWithdrawalStatisticsReq struct {
}
type AdminGetWithdrawalStatisticsResp struct {
TotalWithdrawalAmount float64 `json:"total_withdrawal_amount"` // 总提现金额
TodayWithdrawalAmount float64 `json:"today_withdrawal_amount"` // 今日提现金额
TotalActualAmount float64 `json:"total_actual_amount"` // 总实际到账金额
TotalTaxAmount float64 `json:"total_tax_amount"` // 总扣税金额
}
type AdminLoginReq struct { type AdminLoginReq struct {
Username string `json:"username" validate:"required"` Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"` Password string `json:"password" validate:"required"`
@@ -717,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"` // 会员级别名称
@@ -752,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"`
@@ -818,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 {
@@ -866,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"` // 用户名
@@ -971,6 +1112,11 @@ type AgentLinkListItem struct {
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
} }
type AgentLinkProductStatisticsItem struct {
ProductName string `json:"product_name"` // 产品名称
LinkCount int64 `json:"link_count"` // 推广链接数量
}
type AgentListItem struct { type AgentListItem struct {
Id int64 `json:"id"` // 主键 Id int64 `json:"id"` // 主键
UserId int64 `json:"user_id"` // 用户ID UserId int64 `json:"user_id"` // 用户ID
@@ -1134,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
@@ -1182,11 +1343,14 @@ 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"` // 原始佣金金额
CreateTime string `json:"create_time"` RefundedAmount float64 `json:"refunded_amount"` // 已退款佣金金额
QueryParams map[string]interface{} `json:"query_params,omitempty"` NetAmount float64 `json:"net_amount"` // 剩余净佣金金额 = amount - refunded_amount
Status int64 `json:"status"` // 状态0-已结算1-冻结中2-已退款
CreateTime string `json:"create_time"`
QueryParams map[string]interface{} `json:"query_params,omitempty"`
} }
type CreateMenuReq struct { type CreateMenuReq struct {
@@ -1643,6 +1807,7 @@ type OrderListItem struct {
PaymentPlatform string `json:"payment_platform"` // 支付方式 PaymentPlatform string `json:"payment_platform"` // 支付方式
PaymentScene string `json:"payment_scene"` // 支付平台 PaymentScene string `json:"payment_scene"` // 支付平台
Amount float64 `json:"amount"` // 金额 Amount float64 `json:"amount"` // 金额
SalesCost float64 `json:"sales_cost"` // 成本价
Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败 Status string `json:"status"` // 支付状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中 QueryState string `json:"query_state"` // 查询状态pending-待查询success-查询成功failed-查询失败 processing-查询中
CreateTime string `json:"create_time"` // 创建时间 CreateTime string `json:"create_time"` // 创建时间
@@ -1653,6 +1818,17 @@ type OrderListItem struct {
AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态not_agent-非代理订单success-处理成功failed-处理失败pending-待处理 AgentProcessStatus string `json:"agent_process_status"` // 代理事务处理状态not_agent-非代理订单success-处理成功failed-处理失败pending-待处理
} }
type OrderSourceStatisticsItem struct {
ProductName string `json:"product_name"` // 产品名称
OrderCount int64 `json:"order_count"` // 订单数量
}
type OrderStatisticsItem struct {
Date string `json:"date"` // 日期
Count int64 `json:"count"` // 订单数量
Amount float64 `json:"amount"` // 订单金额
}
type PaymentCheckReq struct { type PaymentCheckReq struct {
OrderNo string `json:"order_no" validate:"required"` OrderNo string `json:"order_no" validate:"required"`
} }
@@ -1665,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 {
@@ -1680,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"` // 更新时间
} }
@@ -2043,6 +2220,7 @@ 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

@@ -61,7 +61,7 @@ func main() {
defer server.Stop() defer server.Stop()
handler.RegisterHandlers(server, svcContext) handler.RegisterHandlers(server, svcContext)
// 自动注册API到数据库 // 自动注册API到数据库
apiRegistry := service.NewApiRegistryService(svcContext.AdminApiModel) apiRegistry := service.NewApiRegistryService(svcContext.AdminApiModel)
routes := server.Routes() routes := server.Routes()
@@ -70,7 +70,7 @@ func main() {
} else { } else {
logx.Infof("API注册成功共注册 %d 个路由", len(routes)) logx.Infof("API注册成功共注册 %d 个路由", len(routes))
} }
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start() server.Start()
} }

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 (
@@ -55,18 +55,19 @@ type (
} }
AgentCommissionDeduction struct { AgentCommissionDeduction struct {
Id int64 `db:"id"` Id int64 `db:"id"`
AgentId int64 `db:"agent_id"` AgentId int64 `db:"agent_id"`
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
Type string `db:"type"` OrderId sql.NullInt64 `db:"order_id"` // 关联订单ID
Status int64 `db:"status"` // 状态 Type string `db:"type"`
CreateTime time.Time `db:"create_time"` Status int64 `db:"status"` // 状态
UpdateTime time.Time `db:"update_time"` // 更新时间 CreateTime time.Time `db:"create_time"`
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 UpdateTime time.Time `db:"update_time"` // 更新时间
DelState int64 `db:"del_state"` // 删除状态 DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
Version int64 `db:"version"` // 版本号 DelState int64 `db:"del_state"` // 删除状态
Version int64 `db:"version"` // 版本号
} }
) )
@@ -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 (
@@ -55,17 +54,18 @@ type (
} }
AgentCommission struct { AgentCommission struct {
Id int64 `db:"id"` Id int64 `db:"id"`
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"`
ProductId int64 `db:"product_id"` // 产品ID RefundedAmount float64 `db:"refunded_amount"` // 已退款佣金金额
Status int64 `db:"status"` ProductId int64 `db:"product_id"` // 产品ID
CreateTime time.Time `db:"create_time"` Status int64 `db:"status"`
UpdateTime time.Time `db:"update_time"` // 更新时间 CreateTime time.Time `db:"create_time"`
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 UpdateTime time.Time `db:"update_time"` // 更新时间
DelState int64 `db:"del_state"` // 删除状态 DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
Version int64 `db:"version"` // 版本号 DelState int64 `db:"del_state"` // 删除状态
Version int64 `db:"version"` // 版本号
} }
) )
@@ -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

@@ -27,9 +27,9 @@ var (
agentRowsExpectAutoSet = strings.Join(stringx.Remove(agentFieldNames, "`id`", "`create_time`", "`update_time`"), ",") agentRowsExpectAutoSet = strings.Join(stringx.Remove(agentFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
agentRowsWithPlaceHolder = strings.Join(stringx.Remove(agentFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" agentRowsWithPlaceHolder = strings.Join(stringx.Remove(agentFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmAgentIdPrefix = "cache:tydata:agent:id:" cacheTydataAgentIdPrefix = "cache:tydata:agent:id:"
cacheHmAgentMobilePrefix = "cache:tydata:agent:mobile:" cacheTydataAgentMobilePrefix = "cache:tydata:agent:mobile:"
cacheHmAgentUserIdPrefix = "cache:tydata:agent:userId:" cacheTydataAgentUserIdPrefix = "cache:tydata:agent:userId:"
) )
type ( type (
@@ -83,22 +83,22 @@ func newAgentModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentModel {
func (m *defaultAgentModel) Insert(ctx context.Context, session sqlx.Session, data *Agent) (sql.Result, error) { func (m *defaultAgentModel) Insert(ctx context.Context, session sqlx.Session, data *Agent) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmAgentIdKey := fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, data.Id) tydataAgentIdKey := fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, data.Id)
hmAgentMobileKey := fmt.Sprintf("%s%v", cacheHmAgentMobilePrefix, data.Mobile) tydataAgentMobileKey := fmt.Sprintf("%s%v", cacheTydataAgentMobilePrefix, data.Mobile)
hmAgentUserIdKey := fmt.Sprintf("%s%v", cacheHmAgentUserIdPrefix, data.UserId) tydataAgentUserIdKey := fmt.Sprintf("%s%v", cacheTydataAgentUserIdPrefix, data.UserId)
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, agentRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) return session.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version)
} }
return conn.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version) return conn.ExecCtx(ctx, query, data.UserId, data.LevelName, data.Region, data.Mobile, data.WechatId, data.MembershipExpiryTime, data.DeleteTime, data.DelState, data.Version)
}, hmAgentIdKey, hmAgentMobileKey, hmAgentUserIdKey) }, tydataAgentIdKey, tydataAgentMobileKey, tydataAgentUserIdKey)
} }
func (m *defaultAgentModel) FindOne(ctx context.Context, id int64) (*Agent, error) { func (m *defaultAgentModel) FindOne(ctx context.Context, id int64) (*Agent, error) {
hmAgentIdKey := fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, id) tydataAgentIdKey := fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, id)
var resp Agent var resp Agent
err := m.QueryRowCtx(ctx, &resp, hmAgentIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataAgentIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -113,9 +113,9 @@ func (m *defaultAgentModel) FindOne(ctx context.Context, id int64) (*Agent, erro
} }
func (m *defaultAgentModel) FindOneByMobile(ctx context.Context, mobile string) (*Agent, error) { func (m *defaultAgentModel) FindOneByMobile(ctx context.Context, mobile string) (*Agent, error) {
hmAgentMobileKey := fmt.Sprintf("%s%v", cacheHmAgentMobilePrefix, mobile) tydataAgentMobileKey := fmt.Sprintf("%s%v", cacheTydataAgentMobilePrefix, mobile)
var resp Agent var resp Agent
err := m.QueryRowIndexCtx(ctx, &resp, hmAgentMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndexCtx(ctx, &resp, tydataAgentMobileKey, 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", agentRows, m.table) query := fmt.Sprintf("select %s from %s where `mobile` = ? and del_state = ? limit 1", agentRows, 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,9 +133,9 @@ func (m *defaultAgentModel) FindOneByMobile(ctx context.Context, mobile string)
} }
func (m *defaultAgentModel) FindOneByUserId(ctx context.Context, userId int64) (*Agent, error) { func (m *defaultAgentModel) FindOneByUserId(ctx context.Context, userId int64) (*Agent, error) {
hmAgentUserIdKey := fmt.Sprintf("%s%v", cacheHmAgentUserIdPrefix, userId) tydataAgentUserIdKey := fmt.Sprintf("%s%v", cacheTydataAgentUserIdPrefix, userId)
var resp Agent var resp Agent
err := m.QueryRowIndexCtx(ctx, &resp, hmAgentUserIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndexCtx(ctx, &resp, tydataAgentUserIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `user_id` = ? and del_state = ? limit 1", agentRows, m.table) query := fmt.Sprintf("select %s from %s where `user_id` = ? and del_state = ? limit 1", agentRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, userId, globalkey.DelStateNo); err != nil { if err := conn.QueryRowCtx(ctx, &resp, query, userId, globalkey.DelStateNo); err != nil {
return nil, err return nil, err
@@ -157,16 +157,16 @@ func (m *defaultAgentModel) Update(ctx context.Context, session sqlx.Session, ne
if err != nil { if err != nil {
return nil, err return nil, err
} }
hmAgentIdKey := fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, data.Id) tydataAgentIdKey := fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, data.Id)
hmAgentMobileKey := fmt.Sprintf("%s%v", cacheHmAgentMobilePrefix, data.Mobile) tydataAgentMobileKey := fmt.Sprintf("%s%v", cacheTydataAgentMobilePrefix, data.Mobile)
hmAgentUserIdKey := fmt.Sprintf("%s%v", cacheHmAgentUserIdPrefix, data.UserId) tydataAgentUserIdKey := fmt.Sprintf("%s%v", cacheTydataAgentUserIdPrefix, data.UserId)
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, agentRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
} }
return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id) return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
}, hmAgentIdKey, hmAgentMobileKey, hmAgentUserIdKey) }, tydataAgentIdKey, tydataAgentMobileKey, tydataAgentUserIdKey)
} }
func (m *defaultAgentModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *Agent) error { func (m *defaultAgentModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *Agent) error {
@@ -181,16 +181,16 @@ func (m *defaultAgentModel) UpdateWithVersion(ctx context.Context, session sqlx.
if err != nil { if err != nil {
return err return err
} }
hmAgentIdKey := fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, data.Id) tydataAgentIdKey := fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, data.Id)
hmAgentMobileKey := fmt.Sprintf("%s%v", cacheHmAgentMobilePrefix, data.Mobile) tydataAgentMobileKey := fmt.Sprintf("%s%v", cacheTydataAgentMobilePrefix, data.Mobile)
hmAgentUserIdKey := fmt.Sprintf("%s%v", cacheHmAgentUserIdPrefix, data.UserId) tydataAgentUserIdKey := fmt.Sprintf("%s%v", cacheTydataAgentUserIdPrefix, data.UserId)
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, agentRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) return session.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion) return conn.ExecCtx(ctx, query, newData.UserId, newData.LevelName, newData.Region, newData.Mobile, newData.WechatId, newData.MembershipExpiryTime, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
}, hmAgentIdKey, hmAgentMobileKey, hmAgentUserIdKey) }, tydataAgentIdKey, tydataAgentMobileKey, tydataAgentUserIdKey)
if err != nil { if err != nil {
return err return err
} }
@@ -413,20 +413,20 @@ func (m *defaultAgentModel) Delete(ctx context.Context, session sqlx.Session, id
return err return err
} }
hmAgentIdKey := fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, id) tydataAgentIdKey := fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, id)
hmAgentMobileKey := fmt.Sprintf("%s%v", cacheHmAgentMobilePrefix, data.Mobile) tydataAgentMobileKey := fmt.Sprintf("%s%v", cacheTydataAgentMobilePrefix, data.Mobile)
hmAgentUserIdKey := fmt.Sprintf("%s%v", cacheHmAgentUserIdPrefix, data.UserId) tydataAgentUserIdKey := fmt.Sprintf("%s%v", cacheTydataAgentUserIdPrefix, data.UserId)
_, 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)
}, hmAgentIdKey, hmAgentMobileKey, hmAgentUserIdKey) }, tydataAgentIdKey, tydataAgentMobileKey, tydataAgentUserIdKey)
return err return err
} }
func (m *defaultAgentModel) formatPrimary(primary interface{}) string { func (m *defaultAgentModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmAgentIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataAgentIdPrefix, primary)
} }
func (m *defaultAgentModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultAgentModel) 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", agentRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentRows, m.table)

View File

@@ -27,7 +27,7 @@ var (
agentPlatformDeductionRowsExpectAutoSet = strings.Join(stringx.Remove(agentPlatformDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), ",") agentPlatformDeductionRowsExpectAutoSet = strings.Join(stringx.Remove(agentPlatformDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
agentPlatformDeductionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentPlatformDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" agentPlatformDeductionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentPlatformDeductionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmAgentPlatformDeductionIdPrefix = "cache:tydata:agentPlatformDeduction:id:" cacheTydataAgentPlatformDeductionIdPrefix = "cache:tydata:agentPlatformDeduction:id:"
) )
type ( type (
@@ -56,6 +56,7 @@ type (
AgentPlatformDeduction struct { AgentPlatformDeduction struct {
Id int64 `db:"id"` Id int64 `db:"id"`
OrderId int64 `db:"order_id"` // 订单ID
AgentId int64 `db:"agent_id"` // 被抽佣代理ID AgentId int64 `db:"agent_id"` // 被抽佣代理ID
Amount float64 `db:"amount"` Amount float64 `db:"amount"`
Type string `db:"type"` Type string `db:"type"`
@@ -77,20 +78,20 @@ func newAgentPlatformDeductionModel(conn sqlx.SqlConn, c cache.CacheConf) *defau
func (m *defaultAgentPlatformDeductionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) (sql.Result, error) { func (m *defaultAgentPlatformDeductionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, data.Id) tydataAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, 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, agentPlatformDeductionRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentPlatformDeductionRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version) return session.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version) return conn.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version)
}, hmAgentPlatformDeductionIdKey) }, tydataAgentPlatformDeductionIdKey)
} }
func (m *defaultAgentPlatformDeductionModel) FindOne(ctx context.Context, id int64) (*AgentPlatformDeduction, error) { func (m *defaultAgentPlatformDeductionModel) FindOne(ctx context.Context, id int64) (*AgentPlatformDeduction, error) {
hmAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, id) tydataAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, id)
var resp AgentPlatformDeduction var resp AgentPlatformDeduction
err := m.QueryRowCtx(ctx, &resp, hmAgentPlatformDeductionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataAgentPlatformDeductionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentPlatformDeductionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentPlatformDeductionRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -105,14 +106,14 @@ func (m *defaultAgentPlatformDeductionModel) FindOne(ctx context.Context, id int
} }
func (m *defaultAgentPlatformDeductionModel) Update(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) (sql.Result, error) { func (m *defaultAgentPlatformDeductionModel) Update(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) (sql.Result, error) {
hmAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, data.Id) tydataAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, 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, agentPlatformDeductionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentPlatformDeductionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return session.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id) return conn.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id)
}, hmAgentPlatformDeductionIdKey) }, tydataAgentPlatformDeductionIdKey)
} }
func (m *defaultAgentPlatformDeductionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) error { func (m *defaultAgentPlatformDeductionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentPlatformDeduction) error {
@@ -123,14 +124,14 @@ func (m *defaultAgentPlatformDeductionModel) UpdateWithVersion(ctx context.Conte
var sqlResult sql.Result var sqlResult sql.Result
var err error var err error
hmAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, data.Id) tydataAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, 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, agentPlatformDeductionRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentPlatformDeductionRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return session.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion) return conn.ExecCtx(ctx, query, data.OrderId, data.AgentId, data.Amount, data.Type, data.Status, data.DeleteTime, data.DelState, data.Version, data.Id, oldVersion)
}, hmAgentPlatformDeductionIdKey) }, tydataAgentPlatformDeductionIdKey)
if err != nil { if err != nil {
return err return err
} }
@@ -348,18 +349,18 @@ func (m *defaultAgentPlatformDeductionModel) SelectBuilder() squirrel.SelectBuil
return squirrel.Select().From(m.table) return squirrel.Select().From(m.table)
} }
func (m *defaultAgentPlatformDeductionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error { func (m *defaultAgentPlatformDeductionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
hmAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, id) tydataAgentPlatformDeductionIdKey := fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, 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)
}, hmAgentPlatformDeductionIdKey) }, tydataAgentPlatformDeductionIdKey)
return err return err
} }
func (m *defaultAgentPlatformDeductionModel) formatPrimary(primary interface{}) string { func (m *defaultAgentPlatformDeductionModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmAgentPlatformDeductionIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataAgentPlatformDeductionIdPrefix, primary)
} }
func (m *defaultAgentPlatformDeductionModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultAgentPlatformDeductionModel) 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", agentPlatformDeductionRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentPlatformDeductionRows, 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 (
@@ -27,8 +26,8 @@ var (
orderRowsExpectAutoSet = strings.Join(stringx.Remove(orderFieldNames, "`id`", "`create_time`", "`update_time`"), ",") orderRowsExpectAutoSet = strings.Join(stringx.Remove(orderFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
orderRowsWithPlaceHolder = strings.Join(stringx.Remove(orderFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?" orderRowsWithPlaceHolder = strings.Join(stringx.Remove(orderFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheHmOrderIdPrefix = "cache:tydata:order:id:" cacheTydataOrderIdPrefix = "cache:tydata:order:id:"
cacheHmOrderOrderNoPrefix = "cache:tydata:order:orderNo:" cacheTydataOrderOrderNoPrefix = "cache:tydata:order:orderNo:"
) )
type ( type (
@@ -74,6 +73,7 @@ type (
RefundTime sql.NullTime `db:"refund_time"` // 退款时间 RefundTime sql.NullTime `db:"refund_time"` // 退款时间
CloseTime sql.NullTime `db:"close_time"` // 订单关闭时间 CloseTime sql.NullTime `db:"close_time"` // 订单关闭时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间 DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
SalesCost float64 `db:"sales_cost"` // 销售成本
} }
) )
@@ -86,21 +86,21 @@ func newOrderModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultOrderModel {
func (m *defaultOrderModel) Insert(ctx context.Context, session sqlx.Session, data *Order) (sql.Result, error) { func (m *defaultOrderModel) Insert(ctx context.Context, session sqlx.Session, data *Order) (sql.Result, error) {
data.DelState = globalkey.DelStateNo data.DelState = globalkey.DelStateNo
hmOrderIdKey := fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, data.Id) tydataOrderIdKey := fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, data.Id)
hmOrderOrderNoKey := fmt.Sprintf("%s%v", cacheHmOrderOrderNoPrefix, data.OrderNo) tydataOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTydataOrderOrderNoPrefix, data.OrderNo)
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, orderRowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, orderRowsExpectAutoSet)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime) return session.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
} }
return conn.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime) return conn.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
}, hmOrderIdKey, hmOrderOrderNoKey) }, tydataOrderIdKey, tydataOrderOrderNoKey)
} }
func (m *defaultOrderModel) FindOne(ctx context.Context, id int64) (*Order, error) { func (m *defaultOrderModel) FindOne(ctx context.Context, id int64) (*Order, error) {
hmOrderIdKey := fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, id) tydataOrderIdKey := fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, id)
var resp Order var resp Order
err := m.QueryRowCtx(ctx, &resp, hmOrderIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { err := m.QueryRowCtx(ctx, &resp, tydataOrderIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", orderRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", orderRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo) return conn.QueryRowCtx(ctx, v, query, id, globalkey.DelStateNo)
}) })
@@ -115,9 +115,9 @@ func (m *defaultOrderModel) FindOne(ctx context.Context, id int64) (*Order, erro
} }
func (m *defaultOrderModel) FindOneByOrderNo(ctx context.Context, orderNo string) (*Order, error) { func (m *defaultOrderModel) FindOneByOrderNo(ctx context.Context, orderNo string) (*Order, error) {
hmOrderOrderNoKey := fmt.Sprintf("%s%v", cacheHmOrderOrderNoPrefix, orderNo) tydataOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTydataOrderOrderNoPrefix, orderNo)
var resp Order var resp Order
err := m.QueryRowIndexCtx(ctx, &resp, hmOrderOrderNoKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndexCtx(ctx, &resp, tydataOrderOrderNoKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `order_no` = ? and del_state = ? limit 1", orderRows, m.table) query := fmt.Sprintf("select %s from %s where `order_no` = ? and del_state = ? limit 1", orderRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, orderNo, globalkey.DelStateNo); err != nil { if err := conn.QueryRowCtx(ctx, &resp, query, orderNo, globalkey.DelStateNo); err != nil {
return nil, err return nil, err
@@ -139,15 +139,15 @@ func (m *defaultOrderModel) Update(ctx context.Context, session sqlx.Session, ne
if err != nil { if err != nil {
return nil, err return nil, err
} }
hmOrderIdKey := fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, data.Id) tydataOrderIdKey := fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, data.Id)
hmOrderOrderNoKey := fmt.Sprintf("%s%v", cacheHmOrderOrderNoPrefix, data.OrderNo) tydataOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTydataOrderOrderNoPrefix, data.OrderNo)
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, orderRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id) return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
} }
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id) return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
}, hmOrderIdKey, hmOrderOrderNoKey) }, tydataOrderIdKey, tydataOrderOrderNoKey)
} }
func (m *defaultOrderModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *Order) error { func (m *defaultOrderModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *Order) error {
@@ -162,15 +162,15 @@ func (m *defaultOrderModel) UpdateWithVersion(ctx context.Context, session sqlx.
if err != nil { if err != nil {
return err return err
} }
hmOrderIdKey := fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, data.Id) tydataOrderIdKey := fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, data.Id)
hmOrderOrderNoKey := fmt.Sprintf("%s%v", cacheHmOrderOrderNoPrefix, data.OrderNo) tydataOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTydataOrderOrderNoPrefix, data.OrderNo)
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, orderRowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, orderRowsWithPlaceHolder)
if session != nil { if session != nil {
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion) return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
} }
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion) return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
}, hmOrderIdKey, hmOrderOrderNoKey) }, tydataOrderIdKey, tydataOrderOrderNoKey)
if err != nil { if err != nil {
return err return err
} }
@@ -393,19 +393,19 @@ func (m *defaultOrderModel) Delete(ctx context.Context, session sqlx.Session, id
return err return err
} }
hmOrderIdKey := fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, id) tydataOrderIdKey := fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, id)
hmOrderOrderNoKey := fmt.Sprintf("%s%v", cacheHmOrderOrderNoPrefix, data.OrderNo) tydataOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTydataOrderOrderNoPrefix, data.OrderNo)
_, 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)
}, hmOrderIdKey, hmOrderOrderNoKey) }, tydataOrderIdKey, tydataOrderOrderNoKey)
return err return err
} }
func (m *defaultOrderModel) formatPrimary(primary interface{}) string { func (m *defaultOrderModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheHmOrderIdPrefix, primary) return fmt.Sprintf("%s%v", cacheTydataOrderIdPrefix, primary)
} }
func (m *defaultOrderModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { func (m *defaultOrderModel) 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", orderRows, m.table) query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", orderRows, m.table)

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),
}
}

Some files were not shown because too many files have changed in this diff Show More