Merge branch 'main' of http://1.117.67.95:3000/team/hm-server
This commit is contained in:
@@ -61,6 +61,10 @@ service main {
|
||||
// 代理会员配置编辑
|
||||
@handler AdminUpdateAgentMembershipConfig
|
||||
post /agent-membership-config/update (AdminUpdateAgentMembershipConfigReq) returns (AdminUpdateAgentMembershipConfigResp)
|
||||
|
||||
// 银行卡提现审核(确认/拒绝)
|
||||
@handler AdminReviewBankCardWithdrawal
|
||||
post /agent-withdrawal/bank-card/review (AdminReviewBankCardWithdrawalReq) returns (AdminReviewBankCardWithdrawalResp)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -189,10 +193,16 @@ type (
|
||||
AgentId int64 `json:"agent_id"` // 代理ID
|
||||
WithdrawNo string `json:"withdraw_no"` // 提现单号
|
||||
Amount float64 `json:"amount"` // 金额
|
||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
||||
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
||||
Status int64 `json:"status"` // 状态
|
||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
||||
Remark string `json:"remark"` // 备注
|
||||
CreateTime string `json:"create_time"` // 创建时间
|
||||
WithdrawType int64 `json:"withdraw_type"` // 提现类型:1-支付宝,2-银行卡
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||
}
|
||||
|
||||
// 代理提现分页查询响应
|
||||
@@ -383,4 +393,16 @@ type (
|
||||
AdminUpdateAgentMembershipConfigResp {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
|
||||
// 银行卡提现审核请求
|
||||
AdminReviewBankCardWithdrawalReq {
|
||||
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
||||
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
||||
Remark string `json:"remark"` // 备注(拒绝时必填)
|
||||
}
|
||||
|
||||
// 银行卡提现审核响应
|
||||
AdminReviewBankCardWithdrawalResp {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
)
|
||||
@@ -278,6 +278,14 @@ service main {
|
||||
|
||||
@handler GetAgentWithdrawalTaxExemption
|
||||
get /withdrawal/tax/exemption (GetWithdrawalTaxExemptionReq) returns (GetWithdrawalTaxExemptionResp)
|
||||
|
||||
// 银行卡提现申请
|
||||
@handler BankCardWithdrawal
|
||||
post /withdrawal/bank-card (BankCardWithdrawalReq) returns (WithdrawalResp)
|
||||
|
||||
// 获取历史银行卡信息
|
||||
@handler GetBankCardInfo
|
||||
get /withdrawal/bank-card/info (GetBankCardInfoReq) returns (GetBankCardInfoResp)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -318,9 +326,10 @@ type (
|
||||
SubWithdrawReward float64 `json:"sub_withdraw_reward"`
|
||||
}
|
||||
Commission {
|
||||
ProductName string `json:"product_name"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreateTime string `json:"create_time"`
|
||||
OrderId string `json:"order_id"` // 订单号
|
||||
ProductName string `json:"product_name"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreateTime string `json:"create_time"`
|
||||
QueryParams map[string]interface{} `json:"query_params,omitempty"`
|
||||
}
|
||||
GetCommissionReq {
|
||||
@@ -384,6 +393,21 @@ type (
|
||||
RemainingExemptionAmount float64 `json:"remaining_exemption_amount"`
|
||||
TaxRate float64 `json:"tax_rate"`
|
||||
}
|
||||
// 银行卡提现申请请求
|
||||
BankCardWithdrawalReq {
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
Amount float64 `json:"amount"` // 提现金额
|
||||
}
|
||||
// 获取历史银行卡信息请求
|
||||
GetBankCardInfoReq {}
|
||||
// 获取历史银行卡信息响应
|
||||
GetBankCardInfoResp {
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||
IdCard string `json:"id_card"` // 身份证号
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
|
||||
@@ -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 AdminReviewBankCardWithdrawalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminReviewBankCardWithdrawalReq
|
||||
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.NewAdminReviewBankCardWithdrawalLogic(r.Context(), svcCtx)
|
||||
resp, err := l.AdminReviewBankCardWithdrawal(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"tydata-server/app/main/api/internal/logic/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 BankCardWithdrawalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.BankCardWithdrawalReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := agent.NewBankCardWithdrawalLogic(r.Context(), svcCtx)
|
||||
resp, err := l.BankCardWithdrawal(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"tydata-server/app/main/api/internal/logic/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 GetBankCardInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.GetBankCardInfoReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
if err := validator.Validate(req); err != nil {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := agent.NewGetBankCardInfoLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetBankCardInfo(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Path: "/agent-reward/list",
|
||||
Handler: admin_agent.AdminGetAgentRewardListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/agent-withdrawal/bank-card/review",
|
||||
Handler: admin_agent.AdminReviewBankCardWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/agent-withdrawal/list",
|
||||
@@ -765,6 +770,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Path: "/withdrawal",
|
||||
Handler: agent.AgentWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/withdrawal/bank-card",
|
||||
Handler: agent.BankCardWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/withdrawal/bank-card/info",
|
||||
Handler: agent.GetBankCardInfoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/withdrawal/tax/exemption",
|
||||
|
||||
@@ -49,6 +49,19 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
|
||||
item.Remark = v.Remark.String
|
||||
}
|
||||
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
|
||||
|
||||
// 手动设置银行卡信息(copier不会自动处理sql.NullString)
|
||||
item.WithdrawType = v.WithdrawType
|
||||
if v.BankCardNo.Valid {
|
||||
item.BankCardNo = v.BankCardNo.String
|
||||
}
|
||||
if v.BankName.Valid {
|
||||
item.BankName = v.BankName.String
|
||||
}
|
||||
if v.PayeeName.Valid {
|
||||
item.PayeeName = v.PayeeName.String
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
resp = &types.AdminGetAgentWithdrawalListResp{
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package admin_agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
"tydata-server/app/main/model"
|
||||
"tydata-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"tydata-server/app/main/api/internal/svc"
|
||||
"tydata-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 审核操作常量
|
||||
const (
|
||||
ReviewActionApprove = 1 // 确认
|
||||
ReviewActionReject = 2 // 拒绝
|
||||
)
|
||||
|
||||
// 状态常量
|
||||
const (
|
||||
StatusPending = 1 // 申请中/处理中
|
||||
StatusSuccess = 2 // 成功
|
||||
StatusFailed = 3 // 失败
|
||||
)
|
||||
|
||||
// 提现类型常量
|
||||
const (
|
||||
WithdrawTypeAlipay = 1 // 支付宝提现
|
||||
WithdrawTypeBankCard = 2 // 银行卡提现
|
||||
)
|
||||
|
||||
type AdminReviewBankCardWithdrawalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAdminReviewBankCardWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminReviewBankCardWithdrawalLogic {
|
||||
return &AdminReviewBankCardWithdrawalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *types.AdminReviewBankCardWithdrawalReq) (resp *types.AdminReviewBankCardWithdrawalResp, err error) {
|
||||
// 验证操作类型
|
||||
if req.Action != ReviewActionApprove && req.Action != ReviewActionReject {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("操作类型不正确"), "操作类型验证失败")
|
||||
}
|
||||
|
||||
// 拒绝操作必须填写备注
|
||||
if req.Action == ReviewActionReject && req.Remark == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("拒绝提现必须填写拒绝原因"), "拒绝原因验证失败")
|
||||
}
|
||||
|
||||
resp = &types.AdminReviewBankCardWithdrawalResp{
|
||||
Success: false,
|
||||
}
|
||||
|
||||
// 使用事务处理审核操作
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 获取提现记录
|
||||
record, err := l.svcCtx.AgentWithdrawalModel.FindOne(ctx, req.WithdrawalId)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("提现记录不存在"), "提现记录不存在")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证提现记录状态必须是申请中
|
||||
if record.Status != StatusPending {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该提现记录已处理,无法重复操作"), "状态验证失败")
|
||||
}
|
||||
|
||||
// 验证提现类型
|
||||
if record.WithdrawType != WithdrawTypeBankCard {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该记录不是银行卡提现,无法审核"), "提现类型验证失败")
|
||||
}
|
||||
|
||||
if req.Action == ReviewActionApprove {
|
||||
// 确认提现
|
||||
return l.approveWithdrawal(ctx, session, record)
|
||||
} else {
|
||||
// 拒绝提现
|
||||
return l.rejectWithdrawal(ctx, session, record, req.Remark)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Success = true
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 确认提现
|
||||
func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 更新扣税记录状态为成功
|
||||
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 amount:%f", record.Id, record.Amount)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 拒绝提现
|
||||
func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, remark string) error {
|
||||
// 更新提现记录状态为失败
|
||||
record.Status = StatusFailed
|
||||
record.Remark = sql.NullString{String: "管理员拒绝:" + remark, 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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 更新扣税记录状态为失败
|
||||
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 amount:%f reason:%s", record.Id, record.Amount, remark)
|
||||
return nil
|
||||
}
|
||||
@@ -220,6 +220,7 @@ func (l *AgentWithdrawalLogic) mapAlipayError(code string) string {
|
||||
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
|
||||
record := &model.AgentWithdrawal{
|
||||
AgentId: agentID,
|
||||
WithdrawType: 1, // 支付宝提现
|
||||
WithdrawNo: outBizNo,
|
||||
PayeeAccount: payeeAccount,
|
||||
Amount: amount,
|
||||
|
||||
199
app/main/api/internal/logic/agent/bankcardwithdrawallogic.go
Normal file
199
app/main/api/internal/logic/agent/bankcardwithdrawallogic.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"regexp"
|
||||
"time"
|
||||
"tydata-server/app/main/model"
|
||||
"tydata-server/common/ctxdata"
|
||||
"tydata-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"tydata-server/app/main/api/internal/svc"
|
||||
"tydata-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 提现类型常量
|
||||
const (
|
||||
WithdrawTypeAlipay = 1 // 支付宝提现
|
||||
WithdrawTypeBankCard = 2 // 银行卡提现
|
||||
)
|
||||
|
||||
type BankCardWithdrawalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewBankCardWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BankCardWithdrawalLogic {
|
||||
return &BankCardWithdrawalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BankCardWithdrawalLogic) BankCardWithdrawal(req *types.BankCardWithdrawalReq) (resp *types.WithdrawalResp, err error) {
|
||||
var (
|
||||
outBizNo string
|
||||
withdrawRes = &types.WithdrawalResp{}
|
||||
agentID int64
|
||||
)
|
||||
var finalWithdrawAmount float64 // 实际到账金额
|
||||
|
||||
// 验证银行卡号格式(16-19位数字)
|
||||
bankCardNoRegex := regexp.MustCompile(`^\d{16,19}$`)
|
||||
if !bankCardNoRegex.MatchString(req.BankCardNo) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("银行卡号格式不正确,请输入16-19位数字"), "银行卡号格式验证失败")
|
||||
}
|
||||
|
||||
// 验证开户支行不能为空
|
||||
if req.BankName == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("开户支行不能为空"), "开户支行验证失败")
|
||||
}
|
||||
|
||||
// 使用事务处理核心操作
|
||||
err = l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
agentID = agentModel.Id // 保存agentId用于日志
|
||||
|
||||
// 查询实名认证信息
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您未进行实名认证, 无法提现"), "您未进行实名认证")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err)
|
||||
}
|
||||
if agentRealName.Status != model.AgentRealNameStatusApproved {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您的实名认证未通过, 无法提现"), "您的实名认证未通过")
|
||||
}
|
||||
|
||||
// 查询钱包
|
||||
agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理钱包失败: %v", err)
|
||||
}
|
||||
|
||||
// 校验可提现金额
|
||||
if req.Amount > agentWallet.Balance {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "余额不足")
|
||||
}
|
||||
|
||||
// 最低提现金额验证
|
||||
if req.Amount < 50 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("提现金额不能低于50元"), "金额验证失败")
|
||||
}
|
||||
|
||||
// 生成交易号
|
||||
outBizNo = "BC_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err)
|
||||
}
|
||||
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
// 统一按6%收取税收
|
||||
taxRate := l.svcCtx.Config.TaxConfig.TaxRate
|
||||
var (
|
||||
taxAmount float64 // 应缴税费
|
||||
taxDeductionPart float64 // 应税金额
|
||||
TaxStatus int64 // 扣税状态
|
||||
exemptionAmount float64 // 免税金额(固定为0)
|
||||
)
|
||||
|
||||
// 统一扣税逻辑:所有提现都按6%收取税收
|
||||
exemptionAmount = 0 // 免税金额 = 0
|
||||
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
|
||||
taxDeductionPart = req.Amount // 应税金额 = 提现金额
|
||||
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
|
||||
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
|
||||
|
||||
// 创建提现记录(初始状态为申请中,提现类型为银行卡)
|
||||
withdrawalID, err := l.createBankCardWithdrawalRecord(session, agentModel.Id, req.BankCardNo, req.BankName, agentRealName.Name, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 扣税记录
|
||||
taxModel := &model.AgentWithdrawalTax{
|
||||
AgentId: agentModel.Id,
|
||||
YearMonth: yearMonth,
|
||||
WithdrawalId: withdrawalID,
|
||||
WithdrawalAmount: req.Amount,
|
||||
ExemptionAmount: exemptionAmount,
|
||||
TaxableAmount: taxDeductionPart,
|
||||
TaxRate: taxRate,
|
||||
TaxAmount: taxAmount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxStatus: TaxStatus,
|
||||
Remark: sql.NullString{String: "银行卡提现申请,待审核扣税", Valid: true},
|
||||
ExemptionRecordId: 0, // 不再使用免税额度记录
|
||||
}
|
||||
_, err = l.svcCtx.AgentWithdrawalTaxModel.Insert(ctx, session, taxModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建扣税记录失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 银行卡提现不需要调用支付接口,直接返回申请中状态
|
||||
withdrawRes.Status = WithdrawStatusProcessing
|
||||
withdrawRes.FailMsg = ""
|
||||
|
||||
l.Logger.Infof("银行卡提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount)
|
||||
return withdrawRes, nil
|
||||
}
|
||||
|
||||
// 创建银行卡提现记录(事务内操作)
|
||||
func (l *BankCardWithdrawalLogic) createBankCardWithdrawalRecord(session sqlx.Session, agentID int64, bankCardNo string, bankName string, payeeName string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
|
||||
record := &model.AgentWithdrawal{
|
||||
AgentId: agentID,
|
||||
WithdrawType: WithdrawTypeBankCard, // 银行卡提现
|
||||
WithdrawNo: outBizNo,
|
||||
PayeeAccount: bankCardNo, // 银行卡号存储在PayeeAccount字段
|
||||
Amount: amount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxAmount: taxAmount,
|
||||
Status: StatusProcessing, // 申请中状态
|
||||
BankCardNo: sql.NullString{String: bankCardNo, Valid: true},
|
||||
BankName: sql.NullString{String: bankName, Valid: true},
|
||||
PayeeName: sql.NullString{String: payeeName, Valid: true},
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.LastInsertId()
|
||||
}
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
func (l *BankCardWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error {
|
||||
wallet.Balance -= amount
|
||||
wallet.FrozenBalance += amount
|
||||
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -64,6 +64,14 @@ func (l *GetAgentCommissionLogic) GetAgentCommission(req *types.GetCommissionReq
|
||||
}
|
||||
commission.CreateTime = agentCommissionModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||
commission.ProductName = product.ProductName
|
||||
|
||||
// 从 order 表获取 platform_order_id
|
||||
orderModel, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, agentCommissionModel.OrderId)
|
||||
if findOrderErr == nil && orderModel != nil && orderModel.PlatformOrderId.Valid {
|
||||
commission.OrderId = orderModel.PlatformOrderId.String
|
||||
} else {
|
||||
commission.OrderId = ""
|
||||
}
|
||||
|
||||
queryModel, queryErr := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, agentCommissionModel.OrderId)
|
||||
if queryErr == nil && queryModel != nil {
|
||||
|
||||
81
app/main/api/internal/logic/agent/getbankcardinfologic.go
Normal file
81
app/main/api/internal/logic/agent/getbankcardinfologic.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tydata-server/app/main/model"
|
||||
"tydata-server/common/ctxdata"
|
||||
"tydata-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"tydata-server/app/main/api/internal/svc"
|
||||
"tydata-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetBankCardInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetBankCardInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetBankCardInfoLogic {
|
||||
return &GetBankCardInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetBankCardInfoLogic) GetBankCardInfo(req *types.GetBankCardInfoReq) (resp *types.GetBankCardInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询实名认证信息
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您未进行实名认证"), "您未进行实名认证")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 初始化响应,包含实名认证信息
|
||||
resp = &types.GetBankCardInfoResp{
|
||||
PayeeName: agentRealName.Name,
|
||||
IdCard: agentRealName.IdCard,
|
||||
BankCardNo: "",
|
||||
BankName: "",
|
||||
}
|
||||
|
||||
// 查询最近一次成功的银行卡提现记录,用于自动填充
|
||||
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder()
|
||||
builder = builder.Where(squirrel.Eq{"agent_id": agentModel.Id})
|
||||
builder = builder.Where(squirrel.Eq{"withdraw_type": 2}) // 银行卡提现
|
||||
builder = builder.Where(squirrel.Eq{"status": 2}) // 成功状态
|
||||
builder = builder.OrderBy("create_time DESC")
|
||||
builder = builder.Limit(1)
|
||||
|
||||
list, err := l.svcCtx.AgentWithdrawalModel.FindAll(l.ctx, builder, "create_time DESC")
|
||||
if err == nil && len(list) > 0 {
|
||||
lastRecord := list[0]
|
||||
if lastRecord.BankCardNo.Valid {
|
||||
resp.BankCardNo = lastRecord.BankCardNo.String
|
||||
}
|
||||
if lastRecord.BankName.Valid {
|
||||
resp.BankName = lastRecord.BankName.String
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -269,7 +269,7 @@ func (s *AuthorizationService) generatePDFContent(userInfo map[string]interface{
|
||||
}
|
||||
|
||||
// 构建授权书内容(去掉标题部分)
|
||||
content := fmt.Sprintf(`海南天远大数据科技有限公司:
|
||||
content := fmt.Sprintf(`海南海宇大数据有限公司:
|
||||
本人%s拟向贵司申请大数据分析报告查询业务,贵司需要了解本人相关状况,用于查询大数据分析报告,因此本人同意向贵司提供本人的姓名和手机号等个人信息,并同意贵司向第三方传送上述信息。第三方将使用上述信息核实信息真实情况,查询信用记录,并生成报告。
|
||||
|
||||
授权内容如下:
|
||||
@@ -292,8 +292,8 @@ func (s *AuthorizationService) generatePDFContent(userInfo map[string]interface{
|
||||
附加说明:
|
||||
本人在授权的相关数据将依据法律法规及贵司内部数据管理规范妥善存储,存储期限为法律要求的最短必要时间。超过存储期限或在数据使用目的达成后,贵司将对相关数据进行销毁或匿名化处理。
|
||||
本人有权随时撤回本授权书中的授权,但撤回前的授权行为及其法律后果仍具有法律效力。若需撤回授权,本人可通过贵司官方渠道提交书面申请,贵司将在收到申请后依法停止对本人数据的使用。
|
||||
你通过"天远数据",自愿支付相应费用,用于购买海南天远大数据科技有限公司的大数据报告产品。如若对产品内容存在异议,可通过邮箱admin@iieeii.com或APP"联系客服"按钮进行反馈,贵司将在收到异议之日起20日内进行核查和处理,并将结果答复。
|
||||
你向海南天远大数据科技有限公司的支付方式为:海南天远大数据科技有限公司及其经官方授权的相关企业的支付宝账户。
|
||||
你通过"天远数据",自愿支付相应费用,用于购买海南海宇大数据有限公司的大数据报告产品。如若对产品内容存在异议,可通过邮箱admin@iieeii.com或APP"联系客服"按钮进行反馈,贵司将在收到异议之日起20日内进行核查和处理,并将结果答复。
|
||||
你向海南海宇大数据有限公司的支付方式为:海南海宇大数据有限公司及其经官方授权的相关企业的支付宝账户。
|
||||
|
||||
争议解决机制:
|
||||
若因本授权书引发争议,双方应友好协商解决;协商不成的,双方同意将争议提交至授权书签署地(海南省)有管辖权的人民法院解决。
|
||||
|
||||
@@ -695,6 +695,16 @@ type AdminRetryAgentProcessResp struct {
|
||||
ProcessedAt string `json:"processed_at"` // 处理时间
|
||||
}
|
||||
|
||||
type AdminReviewBankCardWithdrawalReq struct {
|
||||
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
||||
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
||||
Remark string `json:"remark"` // 备注(拒绝时必填)
|
||||
}
|
||||
|
||||
type AdminReviewBankCardWithdrawalResp struct {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
|
||||
type AdminRoleApiInfo struct {
|
||||
Id int64 `json:"id"`
|
||||
RoleId int64 `json:"role_id"`
|
||||
@@ -1129,10 +1139,16 @@ type AgentWithdrawalListItem struct {
|
||||
AgentId int64 `json:"agent_id"` // 代理ID
|
||||
WithdrawNo string `json:"withdraw_no"` // 提现单号
|
||||
Amount float64 `json:"amount"` // 金额
|
||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
||||
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
||||
Status int64 `json:"status"` // 状态
|
||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
||||
Remark string `json:"remark"` // 备注
|
||||
CreateTime string `json:"create_time"` // 创建时间
|
||||
WithdrawType int64 `json:"withdraw_type"` // 提现类型:1-支付宝,2-银行卡
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||
}
|
||||
|
||||
type AuthorizationDocumentInfo struct {
|
||||
@@ -1148,6 +1164,12 @@ type AuthorizationDocumentInfo struct {
|
||||
CreateTime string `json:"createTime"` // 创建时间
|
||||
}
|
||||
|
||||
type BankCardWithdrawalReq struct {
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
Amount float64 `json:"amount"` // 提现金额
|
||||
}
|
||||
|
||||
type BindMobileReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
@@ -1160,6 +1182,7 @@ type BindMobileResp struct {
|
||||
}
|
||||
|
||||
type Commission struct {
|
||||
OrderId string `json:"order_id"` // 订单号
|
||||
ProductName string `json:"product_name"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreateTime string `json:"create_time"`
|
||||
@@ -1333,6 +1356,16 @@ type GetAuthorizationDocumentResp struct {
|
||||
CreateTime string `json:"createTime"` // 创建时间
|
||||
}
|
||||
|
||||
type GetBankCardInfoReq struct {
|
||||
}
|
||||
|
||||
type GetBankCardInfoResp struct {
|
||||
BankCardNo string `json:"bank_card_no"` // 银行卡号
|
||||
BankName string `json:"bank_name"` // 开户支行
|
||||
PayeeName string `json:"payee_name"` // 收款人姓名
|
||||
IdCard string `json:"id_card"` // 身份证号
|
||||
}
|
||||
|
||||
type GetCommissionReq struct {
|
||||
Page int64 `form:"page"` // 页码
|
||||
PageSize int64 `form:"page_size"` // 每页数据量
|
||||
|
||||
Reference in New Issue
Block a user