This commit is contained in:
2026-01-12 16:43:08 +08:00
parent dc747139c9
commit 3c6e2683f5
110 changed files with 9630 additions and 481 deletions

View File

@@ -153,6 +153,7 @@ type (
AgentId *string `form:"agent_id,optional"` // 代理ID可选
OrderId *string `form:"order_id,optional"` // 订单ID可选
ProcessStatus *int64 `form:"process_status,optional"` // 处理状态(可选)
OrderStatus *string `form:"order_status,optional"` // 订单状态可选pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
}
AgentOrderListItem {
Id string `json:"id"` // 主键
@@ -165,7 +166,8 @@ type (
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
PriceCost float64 `json:"price_cost"` // 提价成本
AgentProfit float64 `json:"agent_profit"` // 代理收益
ProcessStatus int64 `json:"process_status"` // 处理状态
ProcessStatus int64 `json:"process_status"` // 处理状态(保留用于筛选,前端不显示)
OrderStatus string `json:"order_status"` // 订单状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
CreateTime string `json:"create_time"` // 创建时间
}
AdminGetAgentOrderListResp {
@@ -200,6 +202,7 @@ type (
AgentId *string `form:"agent_id,optional"` // 代理ID可选
SourceAgentId *string `form:"source_agent_id,optional"` // 来源代理ID可选
RebateType *int64 `form:"rebate_type,optional"` // 返佣类型(可选)
Status *int64 `form:"status,optional"` // 状态可选1=已发放2=已冻结3=已取消(已退款)
}
AgentRebateListItem {
Id string `json:"id"` // 主键
@@ -208,6 +211,7 @@ type (
OrderId string `json:"order_id"` // 订单ID
RebateType int64 `json:"rebate_type"` // 返佣类型
Amount float64 `json:"amount"` // 金额
Status int64 `json:"status"` // 状态1=已发放2=已冻结3=已取消(已退款)
CreateTime string `json:"create_time"` // 创建时间
}
AdminGetAgentRebateListResp {
@@ -246,17 +250,20 @@ type (
WithdrawNo *string `form:"withdraw_no,optional"` // 提现单号(可选)
}
AgentWithdrawalListItem {
Id string `json:"id"` // 主键
AgentId string `json:"agent_id"` // 代理ID
WithdrawNo string `json:"withdraw_no"` // 提现单号
Amount float64 `json:"amount"` // 金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
Id string `json:"id"` // 主键
AgentId string `json:"agent_id"` // 代理ID
WithdrawNo string `json:"withdraw_no"` // 提现单号
Amount float64 `json:"amount"` // 金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时使用)
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时使用)
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}
AdminGetAgentWithdrawalListResp {
Total int64 `json:"total"` // 总数

View File

@@ -0,0 +1,179 @@
syntax = "v1"
info (
title: "投诉管理服务"
desc: "投诉管理相关接口"
version: "v1"
)
@server (
prefix: api/v1/admin/complaint
group: admin_complaint
middleware: AdminAuthInterceptor
)
service main {
@doc "获取投诉列表"
@handler AdminGetComplaintList
get /list (AdminGetComplaintListReq) returns (AdminGetComplaintListResp)
@doc "获取投诉详情"
@handler AdminGetComplaintDetail
get /detail/:id (AdminGetComplaintDetailReq) returns (AdminGetComplaintDetailResp)
@doc "更新投诉状态"
@handler AdminUpdateComplaintStatus
put /update-status/:id (AdminUpdateComplaintStatusReq) returns (AdminUpdateComplaintStatusResp)
@doc "更新投诉备注"
@handler AdminUpdateComplaintRemark
put /update-remark/:id (AdminUpdateComplaintRemarkReq) returns (AdminUpdateComplaintRemarkResp)
}
type (
// 列表请求
AdminGetComplaintListReq {
Page int64 `form:"page,default=1"` // 页码
PageSize int64 `form:"pageSize,default=20"` // 每页数量
Type string `form:"type,optional"` // 投诉类型alipay-支付宝投诉manual-主动投诉
Status string `form:"status,optional"` // 投诉状态pending-待处理processing-处理中resolved-已解决closed-已关闭
Name string `form:"name,optional"` // 投诉人姓名
Contact string `form:"contact,optional"` // 联系方式
OrderId string `form:"order_id,optional"` // 关联订单ID
CreateTimeStart string `form:"create_time_start,optional"` // 创建时间开始
CreateTimeEnd string `form:"create_time_end,optional"` // 创建时间结束
HandleTimeStart string `form:"handle_time_start,optional"` // 处理时间开始
HandleTimeEnd string `form:"handle_time_end,optional"` // 处理时间结束
}
// 列表响应
AdminGetComplaintListResp {
Total int64 `json:"total"` // 总数
Items []ComplaintListItem `json:"items"` // 列表
}
// 列表项
ComplaintListItem {
Id string `json:"id"` // 投诉ID
Type string `json:"type"` // 投诉类型alipay-支付宝投诉manual-主动投诉
OrderId string `json:"order_id"` // 关联订单ID
Name string `json:"name"` // 投诉人姓名
Contact string `json:"contact"` // 联系方式
Content string `json:"content"` // 投诉内容
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 状态描述
Remark string `json:"remark"` // 处理备注
HandlerId string `json:"handler_id"` // 处理人ID
HandleTime string `json:"handle_time"` // 处理时间
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
// 支付宝投诉特有字段
TaskId string `json:"task_id"` // 支付宝投诉单号
TradeNo string `json:"trade_no"` // 支付宝交易单号
ComplainAmount string `json:"complain_amount"` // 投诉金额
GmtComplain string `json:"gmt_complain"` // 投诉时间
// 主动投诉特有字段
Subject string `json:"subject"` // 投诉主题
Priority string `json:"priority"` // 优先级
Source string `json:"source"` // 投诉来源
}
// 详情请求
AdminGetComplaintDetailReq {
Id string `path:"id"` // 投诉ID
}
// 详情响应
AdminGetComplaintDetailResp {
Id string `json:"id"` // 投诉ID
Type string `json:"type"` // 投诉类型alipay-支付宝投诉manual-主动投诉
OrderId string `json:"order_id"` // 关联订单ID
Name string `json:"name"` // 投诉人姓名
Contact string `json:"contact"` // 联系方式
Content string `json:"content"` // 投诉内容
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 状态描述
Remark string `json:"remark"` // 处理备注
HandlerId string `json:"handler_id"` // 处理人ID
HandleTime string `json:"handle_time"` // 处理时间
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
// 支付宝投诉详情
AlipayComplaint *AlipayComplaintDetail `json:"alipay_complaint,optional"` // 支付宝投诉详情
// 主动投诉详情
ManualComplaint *ManualComplaintDetail `json:"manual_complaint,optional"` // 主动投诉详情
}
// 支付宝投诉详情
AlipayComplaintDetail {
Id string `json:"id"` // 支付宝投诉表ID
AlipayId int64 `json:"alipay_id"` // 支付宝投诉主表的主键id
TaskId string `json:"task_id"` // 投诉单号id
OppositePid string `json:"opposite_pid"` // 被投诉人pid
OppositeName string `json:"opposite_name"` // 被投诉方名称
ComplainAmount string `json:"complain_amount"` // 投诉单涉及交易总金额
GmtComplain string `json:"gmt_complain"` // 投诉时间
GmtProcess string `json:"gmt_process"` // 处理时间
GmtOverdue string `json:"gmt_overdue"` // 过期时间
ComplainContent string `json:"complain_content"` // 用户投诉内容
TradeNo string `json:"trade_no"` // 投诉交易单号
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 投诉单状态枚举值描述
ProcessCode string `json:"process_code"` // 商家处理结果码
ProcessMessage string `json:"process_message"` // 商家处理结果码对应描述
ProcessRemark string `json:"process_remark"` // 商家处理备注
ProcessImgUrlList []string `json:"process_img_url_list"` // 商家处理备注图片url列表
GmtRiskFinishTime string `json:"gmt_risk_finish_time"` // 推送时间
ComplainUrl string `json:"complain_url"` // 投诉网址
CertifyInfo []string `json:"certify_info"` // 投诉凭证图片信息
TradeInfoList []AlipayComplaintTradeInfo `json:"trade_info_list"` // 交易信息列表
}
// 支付宝投诉交易信息
AlipayComplaintTradeInfo {
Id string `json:"id"` // 交易信息表ID
AlipayTradeId string `json:"alipay_trade_id"` // 交易信息表主键id
AlipayComplaintRecordId string `json:"alipay_complaint_record_id"` // 投诉主表id
TradeNo string `json:"trade_no"` // 支付宝交易单号
OutNo string `json:"out_no"` // 商家订单号
GmtTrade string `json:"gmt_trade"` // 交易时间
GmtRefund string `json:"gmt_refund"` // 退款时间
Status string `json:"status"` // 交易投诉状态
StatusDescription string `json:"status_description"` // 交易投诉状态描述
Amount string `json:"amount"` // 交易单金额
}
// 主动投诉详情
ManualComplaintDetail {
Id string `json:"id"` // 主动投诉表ID
UserId string `json:"user_id"` // 关联用户ID
Subject string `json:"subject"` // 投诉主题
Priority string `json:"priority"` // 优先级low-低medium-中high-高urgent-紧急
Source string `json:"source"` // 投诉来源web-网站phone-电话email-邮件other-其他
AttachmentUrls []string `json:"attachment_urls"` // 附件URL列表
}
// 更新投诉状态请求
AdminUpdateComplaintStatusReq {
Id string `path:"id"` // 投诉ID
Status string `json:"status"` // 投诉状态pending-待处理processing-处理中resolved-已解决closed-已关闭
StatusDescription string `json:"status_description,optional"` // 状态描述
HandlerId string `json:"handler_id,optional"` // 处理人ID
}
// 更新投诉状态响应
AdminUpdateComplaintStatusResp {
Success bool `json:"success"` // 是否成功
}
// 更新投诉备注请求
AdminUpdateComplaintRemarkReq {
Id string `path:"id"` // 投诉ID
Remark string `json:"remark"` // 处理备注
}
// 更新投诉备注响应
AdminUpdateComplaintRemarkResp {
Success bool `json:"success"` // 是否成功
}
)

View File

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

View File

@@ -0,0 +1,77 @@
syntax = "v1"
info (
title: "后台统计面板服务"
desc: "后台统计面板相关接口"
author: "team"
version: "v1"
)
// ============================================
// 统计面板接口
// ============================================
@server (
prefix: /api/v1/admin/dashboard
group: admin_dashboard
middleware: AdminAuthInterceptor
)
service main {
// 获取统计面板数据
@handler AdminGetDashboardStatistics
get /statistics returns (AdminGetDashboardStatisticsResp)
}
type (
// 统计面板响应
AdminGetDashboardStatisticsResp {
// 订单统计
OrderStats AdminOrderStatistics `json:"order_stats"`
// 营收统计
RevenueStats AdminRevenueStatistics `json:"revenue_stats"`
// 代理统计
AgentStats AdminAgentStatistics `json:"agent_stats"`
// 利润统计
ProfitStats AdminProfitStatistics `json:"profit_stats"`
// 订单趋势最近7天
OrderTrend []AdminTrendData `json:"order_trend"`
// 营收趋势最近7天
RevenueTrend []AdminTrendData `json:"revenue_trend"`
}
// 订单统计
AdminOrderStatistics {
TodayCount int64 `json:"today_count"` // 今日订单数
MonthCount int64 `json:"month_count"` // 当月订单数
TotalCount int64 `json:"total_count"` // 总订单数
YesterdayCount int64 `json:"yesterday_count"` // 昨日订单数
ChangeRate float64 `json:"change_rate"` // 变化率(百分比)
}
// 营收统计
AdminRevenueStatistics {
TodayAmount float64 `json:"today_amount"` // 今日营收
MonthAmount float64 `json:"month_amount"` // 当月营收
TotalAmount float64 `json:"total_amount"` // 总营收
YesterdayAmount float64 `json:"yesterday_amount"` // 昨日营收
ChangeRate float64 `json:"change_rate"` // 变化率(百分比)
}
// 代理统计
AdminAgentStatistics {
TotalCount int64 `json:"total_count"` // 代理总数
TodayNew int64 `json:"today_new"` // 今日新增
MonthNew int64 `json:"month_new"` // 当月新增
}
// 利润统计
AdminProfitStatistics {
TodayProfit float64 `json:"today_profit"` // 今日利润
MonthProfit float64 `json:"month_profit"` // 当月利润
TotalProfit float64 `json:"total_profit"` // 总利润
TodayProfitRate float64 `json:"today_profit_rate"` // 今日利润率
MonthProfitRate float64 `json:"month_profit_rate"` // 当月利润率
TotalProfitRate float64 `json:"total_profit_rate"` // 总利润率
}
// 趋势数据
AdminTrendData {
Date string `json:"date"` // 日期格式MM-DD
Value float64 `json:"value"` // 数值
}
)

View File

@@ -1,5 +1,7 @@
syntax = "v1"
import "product.api"
info (
title: "代理服务"
desc: "新代理系统接口"
@@ -224,6 +226,10 @@ service main {
@handler ApplyWithdrawal
post /withdrawal/apply (ApplyWithdrawalReq) returns (ApplyWithdrawalResp)
// 获取上次提现信息(用于前端预填)
@handler GetLastWithdrawalInfo
get /withdrawal/last_info (GetLastWithdrawalInfoReq) returns (GetLastWithdrawalInfoResp)
// 实名认证
@handler RealNameAuth
post /real_name (RealNameAuthReq) returns (RealNameAuthResp)
@@ -251,6 +257,28 @@ service main {
// 获取推广查询报告列表
@handler GetPromotionQueryList
get /promotion/query/list (GetPromotionQueryListReq) returns (GetPromotionQueryListResp)
// ============================================
// 用户模块白名单相关接口
// ============================================
@handler GetWhitelistFeatures
get /whitelist/features (GetWhitelistFeaturesReq) returns (GetWhitelistFeaturesResp)
// 创建白名单订单
@handler CreateWhitelistOrder
post /whitelist/order/create (CreateWhitelistOrderReq) returns (CreateWhitelistOrderResp)
// 获取用户白名单列表
@handler GetWhitelistList
get /whitelist/list (GetWhitelistListReq) returns (GetWhitelistListResp)
// 检查模块是否已下架(用于显示下架按钮状态)
@handler CheckFeatureWhitelistStatus
get /whitelist/check (CheckFeatureWhitelistStatusReq) returns (CheckFeatureWhitelistStatusResp)
// 下架单个模块(创建订单并支付)
@handler OfflineFeature
post /whitelist/offline (OfflineFeatureReq) returns (OfflineFeatureResp)
}
type (
@@ -425,16 +453,18 @@ type (
}
// 收益信息
GetRevenueInfoResp {
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
AlipayMonthQuota float64 `json:"alipay_month_quota"` // 支付宝每月提现总额度
AlipayMonthUsed float64 `json:"alipay_month_used"` // 本月已使用的支付宝提现额度
}
// 佣金记录
GetCommissionListReq {
@@ -473,6 +503,7 @@ type (
OrderNo string `json:"order_no"` // 订单号
RebateType int64 `json:"rebate_type"` // 返佣类型1=直接上级2=钻石上级3=黄金上级
Amount float64 `json:"amount"` // 返佣金额
Status int64 `json:"status"` // 状态1=已发放2=已冻结3=已取消(已退款)
CreateTime string `json:"create_time"` // 创建时间
}
// 升级返佣记录
@@ -539,28 +570,46 @@ type (
Total int64 `json:"total"` // 总数
List []WithdrawalItem `json:"list"` // 列表
}
// 提现记录
WithdrawalItem {
Id string `json:"id"` // 记录ID
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
Amount float64 `json:"amount"` // 提现金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态1=待审核2=审核通过3=审核拒绝4=提现中5=提现成功6=提现失败
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
Id string `json:"id"` // 记录ID
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
Amount float64 `json:"amount"` // 提现金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态1=待审核2=审核通过3=审核拒绝4=提现中5=提现成功6=提现失败
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时填写)
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时填写)
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}
// 申请提现
ApplyWithdrawalReq {
Amount float64 `json:"amount"` // 提现金额
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Amount float64 `json:"amount"` // 提现金额
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no,optional"` // 银行卡号(银行卡提现必填)
BankName string `json:"bank_name,optional"` // 开户行名称(银行卡提现必填)
}
ApplyWithdrawalResp {
WithdrawalId string `json:"withdrawal_id"` // 提现记录ID
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
}
// 获取上次提现信息(用于前端预填)
GetLastWithdrawalInfoReq {
WithdrawalType int64 `form:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
}
GetLastWithdrawalInfoResp {
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号
BankName string `json:"bank_name"` // 开户行名称
}
// 实名认证
RealNameAuthReq {
Name string `json:"name"` // 姓名
@@ -589,6 +638,73 @@ type (
Params map[string]interface{} `json:"params"` // 查询参数(已脱敏)
Price float64 `json:"price"` // 查询价格
}
// ============================================
// 用户模块白名单相关类型
// ============================================
GetWhitelistFeaturesReq {}
GetWhitelistFeaturesResp {
List []WhitelistFeatureItem `json:"list"` // 可屏蔽的feature列表
}
WhitelistFeatureItem {
FeatureId string `json:"feature_id"` // Feature的UUID
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
FeatureName string `json:"feature_name"` // Feature的名称
WhitelistPrice float64 `json:"whitelist_price"` // 屏蔽价格(单位:元)
}
CreateWhitelistOrderReq {
IdCard string `json:"id_card"` // 身份证号(查询对象标识)
FeatureIds []string `json:"feature_ids"` // 要屏蔽的feature ID列表
OrderId string `json:"order_id,optional"` // 关联的查询订单ID可选
}
CreateWhitelistOrderResp {
OrderId string `json:"order_id"` // 订单ID
OrderNo string `json:"order_no"` // 订单号
TotalAmount float64 `json:"total_amount"` // 总金额
}
GetWhitelistListReq {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数量
IdCard string `form:"id_card,optional"` // 身份证号(可选,用于筛选)
}
GetWhitelistListResp {
Total int64 `json:"total"` // 总数
List []WhitelistItem `json:"list"` // 列表
}
WhitelistItem {
Id string `json:"id"` // 白名单记录ID
IdCard string `json:"id_card"` // 身份证号
FeatureId string `json:"feature_id"` // Feature的UUID
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
FeatureName string `json:"feature_name"` // Feature的名称
Amount float64 `json:"amount"` // 费用
Status int64 `json:"status"` // 状态1=生效2=已失效
StatusText string `json:"status_text"` // 状态文本
CreateTime string `json:"create_time"` // 创建时间
}
// 检查模块白名单状态请求
CheckFeatureWhitelistStatusReq {
IdCard string `form:"id_card"` // 身份证号
FeatureApiId string `form:"feature_api_id"` // Feature的API标识
QueryId string `form:"query_id,optional"` // 查询记录ID可选用于检查报告数据是否已删除
}
// 检查模块白名单状态响应
CheckFeatureWhitelistStatusResp {
IsWhitelisted bool `json:"is_whitelisted"` // 是否在白名单中
WhitelistPrice float64 `json:"whitelist_price"` // 屏蔽价格单位如果为0表示不支持下架
FeatureId string `json:"feature_id"` // Feature的UUID
DataDeleted bool `json:"data_deleted"` // 报告数据是否已删除仅当提供了query_id时有效
}
// 下架单个模块请求
OfflineFeatureReq {
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
QueryId string `json:"query_id"` // 查询记录IDQuery表的ID必选
}
// 下架单个模块响应
OfflineFeatureResp {
Success bool `json:"success"` // 是否已完成下架
NeedPay bool `json:"need_pay"` // 是否需要发起支付
Amount float64 `json:"amount"` // 需要支付的金额单位0表示无需支付
}
)
// ============================================

View File

@@ -19,6 +19,10 @@ service main {
@handler AlipayCallback
post /pay/alipay/callback
// 支付宝from消息回调
@handler AlipayFrom
post /pay/alipay/from
// 微信退款回调
@handler WechatPayRefundCallback
post /pay/wechat/refund_callback
@@ -46,7 +50,7 @@ type (
PaymentReq {
Id string `json:"id"`
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade whitelist"`
}
PaymentResp {
PrepayData interface{} `json:"prepay_data"`

View File

@@ -28,3 +28,5 @@ import "./admin/admin_query.api"
import "./admin/admin_agent.api"
import "./admin/admin_api.api"
import "./admin/admin_role_api.api"
import "./admin/admin_complaint.api"
import "./admin/dashboard.api"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
package pay
import (
"net/http"
"ycc-server/app/main/api/internal/logic/pay"
"ycc-server/app/main/api/internal/svc"
"ycc-server/common/result"
)
func AlipayFromHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := pay.NewAlipayFromLogic(r.Context(), svcCtx)
err := l.AlipayFrom(w, r)
result.HttpResult(r, w, nil, err)
}
}

View File

@@ -7,6 +7,8 @@ import (
admin_agent "ycc-server/app/main/api/internal/handler/admin_agent"
admin_api "ycc-server/app/main/api/internal/handler/admin_api"
admin_auth "ycc-server/app/main/api/internal/handler/admin_auth"
admin_complaint "ycc-server/app/main/api/internal/handler/admin_complaint"
admin_dashboard "ycc-server/app/main/api/internal/handler/admin_dashboard"
admin_feature "ycc-server/app/main/api/internal/handler/admin_feature"
admin_menu "ycc-server/app/main/api/internal/handler/admin_menu"
admin_notification "ycc-server/app/main/api/internal/handler/admin_notification"
@@ -172,6 +174,53 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
rest.WithPrefix("/api/v1/admin/auth"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AdminAuthInterceptor},
[]rest.Route{
{
// 获取投诉详情
Method: http.MethodGet,
Path: "/detail/:id",
Handler: admin_complaint.AdminGetComplaintDetailHandler(serverCtx),
},
{
// 获取投诉列表
Method: http.MethodGet,
Path: "/list",
Handler: admin_complaint.AdminGetComplaintListHandler(serverCtx),
},
{
// 更新投诉备注
Method: http.MethodPut,
Path: "/update-remark/:id",
Handler: admin_complaint.AdminUpdateComplaintRemarkHandler(serverCtx),
},
{
// 更新投诉状态
Method: http.MethodPut,
Path: "/update-status/:id",
Handler: admin_complaint.AdminUpdateComplaintStatusHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1/admin/complaint"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AdminAuthInterceptor},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/statistics",
Handler: admin_dashboard.AdminGetDashboardStatisticsHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1/admin/dashboard"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AdminAuthInterceptor},
@@ -731,11 +780,41 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/upgrade/subordinate",
Handler: agent.UpgradeSubordinateHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/whitelist/check",
Handler: agent.CheckFeatureWhitelistStatusHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/whitelist/features",
Handler: agent.GetWhitelistFeaturesHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/whitelist/list",
Handler: agent.GetWhitelistListHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/whitelist/offline",
Handler: agent.OfflineFeatureHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/whitelist/order/create",
Handler: agent.CreateWhitelistOrderHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/withdrawal/apply",
Handler: agent.ApplyWithdrawalHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/withdrawal/last_info",
Handler: agent.GetLastWithdrawalInfoHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/withdrawal/list",
@@ -831,6 +910,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/pay/alipay/callback",
Handler: pay.AlipayCallbackHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/pay/alipay/from",
Handler: pay.AlipayFromHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/pay/wechat/callback",

View File

@@ -4,8 +4,8 @@ import (
"context"
"database/sql"
"fmt"
"os"
"time"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
@@ -32,6 +32,13 @@ func NewAdminAuditWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContex
}
}
// parseFloat 解析配置中的浮点数
func (l *AdminAuditWithdrawalLogic) parseFloat(s string) (float64, error) {
var result float64
_, err := fmt.Sscanf(s, "%f", &result)
return result, err
}
func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWithdrawalReq) (resp *types.AdminAuditWithdrawalResp, err error) {
// 1. 查询提现记录
withdrawal, err := l.svcCtx.AgentWithdrawalModel.FindOne(l.ctx, req.WithdrawalId)
@@ -47,38 +54,122 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
// 4. 使用事务处理审核
err = l.svcCtx.AgentWithdrawalModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
if req.Status == 2 { // 审核通过
// 4.1 更新提现记录状态为提现中
withdrawal.Status = 4 // 提现中
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 4.1 根据提现方式处理
if withdrawal.WithdrawalType == 1 {
// 支付宝提现:审核通过前再次校验月度额度,避免一次性通过多笔超限
now := time.Now()
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
nextMonthStart := monthStart.AddDate(0, 1, 0)
// 4.2 调用支付宝转账接口
outBizNo := withdrawal.WithdrawNo
transferResp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
if err != nil {
// 转账失败,更新状态为失败
withdrawal.Status = 6 // 提现失败
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("转账失败: %v", err), Valid: true}
l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
// 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err == nil {
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
// 获取支付宝月度额度配置(默认 800 元)
alipayQuota := 800.0
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(transCtx, "alipay_month_quota"); cfgErr == nil {
if parsed, parseErr := l.parseFloat(cfg.ConfigValue); parseErr == nil && parsed > 0 {
alipayQuota = parsed
}
}
return errors.Wrapf(err, "支付宝转账失败")
}
// 统计本月已成功的支付宝提现金额status=5
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
Where("agent_id = ? AND withdrawal_type = ? AND status = ? AND create_time >= ? AND create_time < ?",
withdrawal.AgentId, 1, 5, monthStart, nextMonthStart)
usedAmount, sumErr := l.svcCtx.AgentWithdrawalModel.FindSum(transCtx, withdrawBuilder, "amount")
if sumErr != nil {
return errors.Wrapf(sumErr, "查询本月支付宝提现额度使用情况失败")
}
// 4.3 根据转账结果更新状态
switch transferResp.Status {
case "SUCCESS":
// 转账成功
if usedAmount+withdrawal.Amount > alipayQuota {
// 超出额度,不允许通过,保持待审核状态并提示原因
withdrawal.Status = 1
withdrawal.Remark = sql.NullString{
String: fmt.Sprintf("超过本月支付宝提现额度(限额:%.2f 元,已用:%.2f 元),请使用银行卡提现或调整金额", alipayQuota, usedAmount),
Valid: true,
}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
return errors.Wrapf(xerr.NewErrMsg("超过本月支付宝提现额度,无法通过该笔提现"), "")
}
// 支付宝提现:开发环境下做模拟,不调用真实支付宝转账
mockTransferStatus := "SUCCESS"
var transferResp struct {
Status string
SubCode string
}
if os.Getenv("ENV") == "development" {
transferResp.Status = mockTransferStatus
logx.Infof("【DEV】模拟支付宝转账成功withdrawNo=%s, amount=%.2f, payee=%s",
withdrawal.WithdrawNo, withdrawal.ActualAmount, withdrawal.PayeeAccount)
} else {
// 生产环境:同步调用支付宝转账接口
outBizNo := withdrawal.WithdrawNo
resp, err := l.svcCtx.AlipayService.AliTransfer(transCtx, withdrawal.PayeeAccount, withdrawal.PayeeName, withdrawal.ActualAmount, "代理提现", outBizNo)
if err != nil {
// 调用失败保持状态为待审核1只记录备注方便管理员重试
withdrawal.Status = 1 // 待审核
withdrawal.Remark = sql.NullString{String: fmt.Sprintf("支付宝转账调用失败: %v", err), Valid: true}
_ = l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal)
return errors.Wrapf(err, "支付宝转账失败")
}
transferResp.Status = resp.Status
transferResp.SubCode = resp.SubCode
}
// 4.2 根据转账结果更新状态
switch transferResp.Status {
case "SUCCESS":
// 转账成功
withdrawal.Status = 5 // 提现成功
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 更新钱包(解冻并扣除)
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败")
}
wallet.FrozenBalance -= withdrawal.Amount
wallet.WithdrawnAmount += withdrawal.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "更新钱包失败")
}
// 更新扣税记录状态
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("withdrawal_id = ?", withdrawal.Id)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
if err == nil && len(taxRecords) > 0 {
taxRecord := taxRecords[0]
taxRecord.TaxStatus = 2 // 已扣税
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
}
case "FAIL":
// 转账失败:保持待审核状态,方便人工处理或重试
withdrawal.Status = 1 // 待审核
errorMsg := l.mapAlipayError(transferResp.SubCode)
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
case "DEALING":
// 处理中同样保持待审核状态1由管理员后续确认
withdrawal.Status = 1
withdrawal.Remark = sql.NullString{String: "支付宝处理中,请稍后重试或联系平台", Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
}
} else if withdrawal.WithdrawalType == 2 {
// 银行卡提现:审核通过即视为提现成功(线下已/将立即打款)
withdrawal.Status = 5 // 提现成功
withdrawal.Remark = sql.NullString{String: req.Remark, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
@@ -96,7 +187,7 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
// 更新扣税记录状态
taxBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("withdrawal_id = ? AND del_state = ?", withdrawal.Id, globalkey.DelStateNo)
Where("withdrawal_id = ?", withdrawal.Id)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(transCtx, taxBuilder, "")
if err == nil && len(taxRecords) > 0 {
taxRecord := taxRecords[0]
@@ -104,27 +195,6 @@ func (l *AdminAuditWithdrawalLogic) AdminAuditWithdrawal(req *types.AdminAuditWi
taxRecord.TaxTime = lzUtils.TimeToNullTime(time.Now())
l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(transCtx, session, taxRecord)
}
case "FAIL":
// 转账失败
withdrawal.Status = 6 // 提现失败
errorMsg := l.mapAlipayError(transferResp.SubCode)
withdrawal.Remark = sql.NullString{String: errorMsg, Valid: true}
if err := l.svcCtx.AgentWithdrawalModel.UpdateWithVersion(transCtx, session, withdrawal); err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
// 解冻余额
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(transCtx, withdrawal.AgentId)
if err == nil {
wallet.FrozenBalance -= withdrawal.Amount
wallet.Balance += withdrawal.Amount
l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet)
}
case "DEALING":
// 处理中,保持提现中状态,后续通过轮询更新
// 状态已经是4提现中无需更新
}
} else if req.Status == 3 { // 审核拒绝

View File

@@ -2,7 +2,6 @@ package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/Masterminds/squirrel"
@@ -29,8 +28,7 @@ func NewAdminGetAgentOrderListLogic(ctx context.Context, svcCtx *svc.ServiceCont
}
func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGetAgentOrderListReq) (resp *types.AdminGetAgentOrderListResp, err error) {
builder := l.svcCtx.AgentOrderModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentOrderModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
@@ -42,6 +40,27 @@ func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGet
builder = builder.Where("process_status = ?", *req.ProcessStatus)
}
// 如果提供了订单状态筛选先查询符合条件的订单ID列表
var filteredOrderIds []string
if req.OrderStatus != nil && *req.OrderStatus != "" {
orderBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ?", *req.OrderStatus).
Columns("id")
orders, _ := l.svcCtx.OrderModel.FindAll(l.ctx, orderBuilder, "")
filteredOrderIds = make([]string, 0, len(orders))
for _, o := range orders {
filteredOrderIds = append(filteredOrderIds, o.Id)
}
// 如果没有符合条件的订单,直接返回空结果
if len(filteredOrderIds) == 0 {
return &types.AdminGetAgentOrderListResp{
Total: 0,
Items: []types.AgentOrderListItem{},
}, nil
}
builder = builder.Where(squirrel.Eq{"order_id": filteredOrderIds})
}
// 分页查询
page := req.Page
if page <= 0 {
@@ -74,9 +93,30 @@ func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGet
}
}
// 批量查询订单状态
orderIdSet := make(map[string]struct{})
for _, order := range orders {
orderIdSet[order.OrderId] = struct{}{}
}
orderIdList := make([]string, 0, len(orderIdSet))
for id := range orderIdSet {
orderIdList = append(orderIdList, id)
}
orderStatusMap := make(map[string]string)
if len(orderIdList) > 0 {
orderList, _ := l.svcCtx.OrderModel.FindAll(l.ctx, l.svcCtx.OrderModel.SelectBuilder().Where(squirrel.Eq{"id": orderIdList}), "")
for _, o := range orderList {
orderStatusMap[o.Id] = o.Status
}
}
// 组装响应
items := make([]types.AgentOrderListItem, 0, len(orders))
for _, order := range orders {
orderStatus := orderStatusMap[order.OrderId]
if orderStatus == "" {
orderStatus = "unknown" // 如果查询不到订单,默认为 unknown
}
items = append(items, types.AgentOrderListItem{
Id: order.Id,
AgentId: order.AgentId,
@@ -89,6 +129,7 @@ func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGet
PriceCost: order.PriceCost,
AgentProfit: order.AgentProfit,
ProcessStatus: order.ProcessStatus,
OrderStatus: orderStatus,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
})
}

View File

@@ -28,8 +28,7 @@ func NewAdminGetAgentProductConfigListLogic(ctx context.Context, svcCtx *svc.Ser
}
func (l *AdminGetAgentProductConfigListLogic) AdminGetAgentProductConfigList(req *types.AdminGetAgentProductConfigListReq) (resp *types.AdminGetAgentProductConfigListResp, err error) {
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
// 如果提供了产品ID直接过滤
if req.ProductId != nil {

View File

@@ -2,7 +2,6 @@ package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
@@ -29,8 +28,7 @@ func NewAdminGetAgentRealNameListLogic(ctx context.Context, svcCtx *svc.ServiceC
}
func (l *AdminGetAgentRealNameListLogic) AdminGetAgentRealNameList(req *types.AdminGetAgentRealNameListReq) (resp *types.AdminGetAgentRealNameListResp, err error) {
builder := l.svcCtx.AgentRealNameModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentRealNameModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)

View File

@@ -2,7 +2,6 @@ package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/Masterminds/squirrel"
@@ -29,8 +28,7 @@ func NewAdminGetAgentRebateListLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
func (l *AdminGetAgentRebateListLogic) AdminGetAgentRebateList(req *types.AdminGetAgentRebateListReq) (resp *types.AdminGetAgentRebateListResp, err error) {
builder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentRebateModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
@@ -41,6 +39,9 @@ func (l *AdminGetAgentRebateListLogic) AdminGetAgentRebateList(req *types.AdminG
if req.RebateType != nil {
builder = builder.Where("rebate_type = ?", *req.RebateType)
}
if req.Status != nil {
builder = builder.Where("status = ?", *req.Status)
}
// 分页查询
page := req.Page
@@ -84,6 +85,7 @@ func (l *AdminGetAgentRebateListLogic) AdminGetAgentRebateList(req *types.AdminG
OrderId: rebate.OrderId,
RebateType: rebate.RebateType,
Amount: rebate.RebateAmount,
Status: rebate.Status,
CreateTime: rebate.CreateTime.Format("2006-01-02 15:04:05"),
})
}

View File

@@ -2,7 +2,6 @@ package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
@@ -28,8 +27,7 @@ func NewAdminGetAgentUpgradeListLogic(ctx context.Context, svcCtx *svc.ServiceCo
}
func (l *AdminGetAgentUpgradeListLogic) AdminGetAgentUpgradeList(req *types.AdminGetAgentUpgradeListReq) (resp *types.AdminGetAgentUpgradeListResp, err error) {
builder := l.svcCtx.AgentUpgradeModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentUpgradeModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)

View File

@@ -49,6 +49,17 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
item.Remark = v.Remark.String
}
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
// 如果是银行卡提现,填充银行卡信息
if v.WithdrawalType == 2 {
if v.BankCardNo.Valid {
item.BankCardNo = v.BankCardNo.String
}
if v.BankName.Valid {
item.BankName = v.BankName.String
}
}
items = append(items, item)
}
resp = &types.AdminGetAgentWithdrawalListResp{

View File

@@ -2,7 +2,6 @@ package admin_agent
import (
"context"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
@@ -31,8 +30,7 @@ func NewAdminGetInviteCodeListLogic(ctx context.Context, svcCtx *svc.ServiceCont
func (l *AdminGetInviteCodeListLogic) AdminGetInviteCodeList(req *types.AdminGetInviteCodeListReq) (resp *types.AdminGetInviteCodeListResp, err error) {
// 1. 构建查询条件
builder := l.svcCtx.AgentInviteCodeModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.AgentInviteCodeModel.SelectBuilder()
if req.Code != nil && *req.Code != "" {
builder = builder.Where("code = ?", *req.Code)

View File

@@ -0,0 +1,188 @@
package admin_complaint
import (
"context"
"encoding/json"
"strconv"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetComplaintDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetComplaintDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetComplaintDetailLogic {
return &AdminGetComplaintDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetComplaintDetailLogic) AdminGetComplaintDetail(req *types.AdminGetComplaintDetailReq) (resp *types.AdminGetComplaintDetailResp, err error) {
// 获取投诉主表信息
complaint, err := l.svcCtx.ComplaintMainModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintDetail, 查询投诉失败 err: %v", err)
}
// 构建响应
resp = &types.AdminGetComplaintDetailResp{
Id: complaint.Id,
Type: complaint.Type,
OrderId: lzUtils.NullStringToString(complaint.OrderId),
Name: lzUtils.NullStringToString(complaint.Name),
Contact: lzUtils.NullStringToString(complaint.Contact),
Content: lzUtils.NullStringToString(complaint.Content),
Status: lzUtils.NullStringToString(complaint.Status),
StatusDescription: lzUtils.NullStringToString(complaint.StatusDescription),
Remark: lzUtils.NullStringToString(complaint.Remark),
HandlerId: lzUtils.NullStringToString(complaint.HandlerId),
CreateTime: complaint.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: complaint.UpdateTime.Format("2006-01-02 15:04:05"),
}
if complaint.HandleTime.Valid {
resp.HandleTime = complaint.HandleTime.Time.Format("2006-01-02 15:04:05")
}
// 获取支付宝投诉详情
if complaint.Type == "alipay" {
alipayBuilder := l.svcCtx.ComplaintAlipayModel.SelectBuilder().
Where("complaint_id = ?", complaint.Id)
alipayComplaints, err := l.svcCtx.ComplaintAlipayModel.FindAll(l.ctx, alipayBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintDetail, 查询支付宝投诉失败 err: %v", err)
}
if len(alipayComplaints) > 0 {
alipayComplaint := alipayComplaints[0]
alipayDetail := &types.AlipayComplaintDetail{
Id: alipayComplaint.Id,
AlipayId: alipayComplaint.AlipayId,
TaskId: alipayComplaint.TaskId,
OppositePid: lzUtils.NullStringToString(alipayComplaint.OppositePid),
OppositeName: lzUtils.NullStringToString(alipayComplaint.OppositeName),
ComplainContent: lzUtils.NullStringToString(alipayComplaint.ComplainContent),
TradeNo: lzUtils.NullStringToString(alipayComplaint.TradeNo),
Status: lzUtils.NullStringToString(alipayComplaint.Status),
StatusDescription: lzUtils.NullStringToString(alipayComplaint.StatusDescription),
ProcessCode: lzUtils.NullStringToString(alipayComplaint.ProcessCode),
ProcessMessage: lzUtils.NullStringToString(alipayComplaint.ProcessMessage),
ProcessRemark: lzUtils.NullStringToString(alipayComplaint.ProcessRemark),
ComplainUrl: lzUtils.NullStringToString(alipayComplaint.ComplainUrl),
}
if alipayComplaint.ComplainAmount.Valid {
alipayDetail.ComplainAmount = strconv.FormatFloat(alipayComplaint.ComplainAmount.Float64, 'f', -1, 64)
}
if alipayComplaint.GmtComplain.Valid {
alipayDetail.GmtComplain = alipayComplaint.GmtComplain.Time.Format("2006-01-02 15:04:05")
}
if alipayComplaint.GmtProcess.Valid {
alipayDetail.GmtProcess = alipayComplaint.GmtProcess.Time.Format("2006-01-02 15:04:05")
}
if alipayComplaint.GmtOverdue.Valid {
alipayDetail.GmtOverdue = alipayComplaint.GmtOverdue.Time.Format("2006-01-02 15:04:05")
}
if alipayComplaint.GmtRiskFinishTime.Valid {
alipayDetail.GmtRiskFinishTime = alipayComplaint.GmtRiskFinishTime.Time.Format("2006-01-02 15:04:05")
}
// 解析图片列表
if alipayComplaint.ProcessImgUrlList.Valid {
var imgList []string
if err := json.Unmarshal([]byte(alipayComplaint.ProcessImgUrlList.String), &imgList); err == nil {
alipayDetail.ProcessImgUrlList = imgList
}
}
if alipayComplaint.CertifyInfo.Valid {
var certifyList []string
if err := json.Unmarshal([]byte(alipayComplaint.CertifyInfo.String), &certifyList); err == nil {
alipayDetail.CertifyInfo = certifyList
}
}
// 获取交易信息列表
tradeBuilder := l.svcCtx.ComplaintAlipayTradeModel.SelectBuilder().
Where("complaint_alipay_id = ?", alipayComplaint.Id)
trades, err := l.svcCtx.ComplaintAlipayTradeModel.FindAll(l.ctx, tradeBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintDetail, 查询交易信息失败 err: %v", err)
}
alipayDetail.TradeInfoList = make([]types.AlipayComplaintTradeInfo, 0, len(trades))
for _, trade := range trades {
tradeInfo := types.AlipayComplaintTradeInfo{
Id: trade.Id,
TradeNo: lzUtils.NullStringToString(trade.TradeNo),
OutNo: lzUtils.NullStringToString(trade.OutNo),
Status: lzUtils.NullStringToString(trade.Status),
StatusDescription: lzUtils.NullStringToString(trade.StatusDescription),
}
if trade.AlipayTradeId.Valid {
tradeInfo.AlipayTradeId = strconv.FormatInt(trade.AlipayTradeId.Int64, 10)
}
if trade.AlipayComplaintRecordId.Valid {
tradeInfo.AlipayComplaintRecordId = strconv.FormatInt(trade.AlipayComplaintRecordId.Int64, 10)
}
if trade.GmtTrade.Valid {
tradeInfo.GmtTrade = trade.GmtTrade.Time.Format("2006-01-02 15:04:05")
}
if trade.GmtRefund.Valid {
tradeInfo.GmtRefund = trade.GmtRefund.Time.Format("2006-01-02 15:04:05")
}
if trade.Amount.Valid {
tradeInfo.Amount = strconv.FormatFloat(trade.Amount.Float64, 'f', -1, 64)
}
alipayDetail.TradeInfoList = append(alipayDetail.TradeInfoList, tradeInfo)
}
resp.AlipayComplaint = alipayDetail
}
}
// 获取主动投诉详情
if complaint.Type == "manual" {
manualBuilder := l.svcCtx.ComplaintManualModel.SelectBuilder().
Where("complaint_id = ?", complaint.Id)
manualComplaints, err := l.svcCtx.ComplaintManualModel.FindAll(l.ctx, manualBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintDetail, 查询主动投诉失败 err: %v", err)
}
if len(manualComplaints) > 0 {
manualComplaint := manualComplaints[0]
manualDetail := &types.ManualComplaintDetail{
Id: manualComplaint.Id,
UserId: lzUtils.NullStringToString(manualComplaint.UserId),
Subject: lzUtils.NullStringToString(manualComplaint.Subject),
Priority: lzUtils.NullStringToString(manualComplaint.Priority),
Source: lzUtils.NullStringToString(manualComplaint.Source),
}
// 解析附件URL列表
if manualComplaint.AttachmentUrls.Valid {
var urlList []string
if err := json.Unmarshal([]byte(manualComplaint.AttachmentUrls.String), &urlList); err == nil {
manualDetail.AttachmentUrls = urlList
}
}
resp.ManualComplaint = manualDetail
}
}
return resp, nil
}

View File

@@ -0,0 +1,174 @@
package admin_complaint
import (
"context"
"strconv"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mr"
)
type AdminGetComplaintListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetComplaintListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetComplaintListLogic {
return &AdminGetComplaintListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetComplaintListLogic) AdminGetComplaintList(req *types.AdminGetComplaintListReq) (resp *types.AdminGetComplaintListResp, err error) {
// 构建查询条件
builder := l.svcCtx.ComplaintMainModel.SelectBuilder()
if req.Type != "" {
builder = builder.Where("type = ?", req.Type)
}
if req.Status != "" {
builder = builder.Where("status = ?", req.Status)
}
if req.Name != "" {
builder = builder.Where("name LIKE ?", "%"+req.Name+"%")
}
if req.Contact != "" {
builder = builder.Where("contact LIKE ?", "%"+req.Contact+"%")
}
if req.OrderId != "" {
builder = builder.Where("order_id = ?", req.OrderId)
}
// 时间范围查询
if req.CreateTimeStart != "" {
builder = builder.Where("create_time >= ?", req.CreateTimeStart)
}
if req.CreateTimeEnd != "" {
builder = builder.Where("create_time <= ?", req.CreateTimeEnd)
}
if req.HandleTimeStart != "" {
builder = builder.Where("handle_time >= ?", req.HandleTimeStart)
}
if req.HandleTimeEnd != "" {
builder = builder.Where("handle_time <= ?", req.HandleTimeEnd)
}
// 并发获取总数和列表
var total int64
var complaints []*model.ComplaintMain
err = mr.Finish(func() error {
var err error
total, err = l.svcCtx.ComplaintMainModel.FindCount(l.ctx, builder, "id")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintList, 查询投诉总数失败 err: %v", err)
}
return nil
}, func() error {
var err error
complaints, err = l.svcCtx.ComplaintMainModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintList, 查询投诉列表失败 err: %v", err)
}
return nil
})
if err != nil {
return nil, err
}
// 构建响应
resp = &types.AdminGetComplaintListResp{
Total: total,
Items: make([]types.ComplaintListItem, 0, len(complaints)),
}
// 批量获取支付宝投诉和主动投诉的详细信息
complaintIds := make([]string, 0, len(complaints))
for _, complaint := range complaints {
complaintIds = append(complaintIds, complaint.Id)
}
// 获取支付宝投诉信息
alipayComplaintMap := make(map[string]*model.ComplaintAlipay)
if len(complaintIds) > 0 {
alipayBuilder := l.svcCtx.ComplaintAlipayModel.SelectBuilder().
Where(squirrel.Eq{"complaint_id": complaintIds})
alipayComplaints, err := l.svcCtx.ComplaintAlipayModel.FindAll(l.ctx, alipayBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintList, 批量查询支付宝投诉失败 err: %v", err)
}
for _, alipayComplaint := range alipayComplaints {
alipayComplaintMap[alipayComplaint.ComplaintId] = alipayComplaint
}
}
// 获取主动投诉信息
manualComplaintMap := make(map[string]*model.ComplaintManual)
if len(complaintIds) > 0 {
manualBuilder := l.svcCtx.ComplaintManualModel.SelectBuilder().
Where(squirrel.Eq{"complaint_id": complaintIds})
manualComplaints, err := l.svcCtx.ComplaintManualModel.FindAll(l.ctx, manualBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminGetComplaintList, 批量查询主动投诉失败 err: %v", err)
}
for _, manualComplaint := range manualComplaints {
manualComplaintMap[manualComplaint.ComplaintId] = manualComplaint
}
}
// 构建列表项
for _, complaint := range complaints {
item := types.ComplaintListItem{
Id: complaint.Id,
Type: complaint.Type,
OrderId: lzUtils.NullStringToString(complaint.OrderId),
Name: lzUtils.NullStringToString(complaint.Name),
Contact: lzUtils.NullStringToString(complaint.Contact),
Content: lzUtils.NullStringToString(complaint.Content),
Status: lzUtils.NullStringToString(complaint.Status),
StatusDescription: lzUtils.NullStringToString(complaint.StatusDescription),
Remark: lzUtils.NullStringToString(complaint.Remark),
HandlerId: lzUtils.NullStringToString(complaint.HandlerId),
CreateTime: complaint.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: complaint.UpdateTime.Format("2006-01-02 15:04:05"),
}
if complaint.HandleTime.Valid {
item.HandleTime = complaint.HandleTime.Time.Format("2006-01-02 15:04:05")
}
// 填充支付宝投诉特有字段
if complaint.Type == "alipay" {
if alipayComplaint, ok := alipayComplaintMap[complaint.Id]; ok {
item.TaskId = alipayComplaint.TaskId
item.TradeNo = lzUtils.NullStringToString(alipayComplaint.TradeNo)
if alipayComplaint.ComplainAmount.Valid {
item.ComplainAmount = strconv.FormatFloat(alipayComplaint.ComplainAmount.Float64, 'f', -1, 64)
}
if alipayComplaint.GmtComplain.Valid {
item.GmtComplain = alipayComplaint.GmtComplain.Time.Format("2006-01-02 15:04:05")
}
}
}
// 填充主动投诉特有字段
if complaint.Type == "manual" {
if manualComplaint, ok := manualComplaintMap[complaint.Id]; ok {
item.Subject = lzUtils.NullStringToString(manualComplaint.Subject)
item.Priority = lzUtils.NullStringToString(manualComplaint.Priority)
item.Source = lzUtils.NullStringToString(manualComplaint.Source)
}
}
resp.Items = append(resp.Items, item)
}
return resp, nil
}

View File

@@ -0,0 +1,47 @@
package admin_complaint
import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateComplaintRemarkLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateComplaintRemarkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateComplaintRemarkLogic {
return &AdminUpdateComplaintRemarkLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateComplaintRemarkLogic) AdminUpdateComplaintRemark(req *types.AdminUpdateComplaintRemarkReq) (resp *types.AdminUpdateComplaintRemarkResp, err error) {
// 获取投诉主表信息
complaint, err := l.svcCtx.ComplaintMainModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateComplaintRemark, 查询投诉失败 err: %v", err)
}
// 更新备注
complaint.Remark = lzUtils.StringToNullString(req.Remark)
// 更新数据库
err = l.svcCtx.ComplaintMainModel.UpdateWithVersion(l.ctx, nil, complaint)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateComplaintRemark, 更新投诉备注失败 err: %v", err)
}
return &types.AdminUpdateComplaintRemarkResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,55 @@
package admin_complaint
import (
"context"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateComplaintStatusLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateComplaintStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateComplaintStatusLogic {
return &AdminUpdateComplaintStatusLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateComplaintStatusLogic) AdminUpdateComplaintStatus(req *types.AdminUpdateComplaintStatusReq) (resp *types.AdminUpdateComplaintStatusResp, err error) {
// 获取投诉主表信息
complaint, err := l.svcCtx.ComplaintMainModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateComplaintStatus, 查询投诉失败 err: %v", err)
}
// 更新状态
complaint.Status = lzUtils.StringToNullString(req.Status)
if req.StatusDescription != "" {
complaint.StatusDescription = lzUtils.StringToNullString(req.StatusDescription)
}
if req.HandlerId != "" {
complaint.HandlerId = lzUtils.StringToNullString(req.HandlerId)
complaint.HandleTime = lzUtils.TimeToNullTime(time.Now())
}
// 更新数据库
err = l.svcCtx.ComplaintMainModel.UpdateWithVersion(l.ctx, nil, complaint)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminUpdateComplaintStatus, 更新投诉状态失败 err: %v", err)
}
return &types.AdminUpdateComplaintStatusResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,381 @@
package admin_dashboard
import (
"context"
"time"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetDashboardStatisticsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetDashboardStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetDashboardStatisticsLogic {
return &AdminGetDashboardStatisticsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetDashboardStatisticsLogic) AdminGetDashboardStatistics() (resp *types.AdminGetDashboardStatisticsResp, err error) {
// 使用Asia/Shanghai时区
loc, _ := time.LoadLocation("Asia/Shanghai")
now := time.Now().In(loc)
// 计算时间范围
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
todayEnd := todayStart.AddDate(0, 0, 1)
yesterdayStart := todayStart.AddDate(0, 0, -1)
yesterdayEnd := todayStart
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, loc)
monthEnd := monthStart.AddDate(0, 1, 0)
// 1. 订单统计
orderStats, err := l.calculateOrderStatistics(todayStart, todayEnd, yesterdayStart, yesterdayEnd, monthStart, monthEnd)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算订单统计失败, %v", err)
}
// 2. 营收统计
revenueStats, err := l.calculateRevenueStatistics(todayStart, todayEnd, yesterdayStart, yesterdayEnd, monthStart, monthEnd)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算营收统计失败, %v", err)
}
// 3. 代理统计
agentStats, err := l.calculateAgentStatistics(todayStart, monthStart)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算代理统计失败, %v", err)
}
// 4. 利润统计
profitStats, err := l.calculateProfitStatistics(todayStart, todayEnd, monthStart, monthEnd, revenueStats)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算利润统计失败, %v", err)
}
// 5. 订单趋势最近7天
orderTrend, err := l.calculateOrderTrend(now, loc)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算订单趋势失败, %v", err)
}
// 6. 营收趋势最近7天
revenueTrend, err := l.calculateRevenueTrend(now, loc)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "计算营收趋势失败, %v", err)
}
return &types.AdminGetDashboardStatisticsResp{
OrderStats: orderStats,
RevenueStats: revenueStats,
AgentStats: agentStats,
ProfitStats: profitStats,
OrderTrend: orderTrend,
RevenueTrend: revenueTrend,
}, nil
}
// calculateOrderStatistics 计算订单统计
func (l *AdminGetDashboardStatisticsLogic) calculateOrderStatistics(todayStart, todayEnd, yesterdayStart, yesterdayEnd, monthStart, monthEnd time.Time) (types.AdminOrderStatistics, error) {
var stats types.AdminOrderStatistics
// 今日订单数
todayBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", todayStart, todayEnd)
todayCount, err := l.svcCtx.OrderModel.FindCount(l.ctx, todayBuilder, "id")
if err != nil {
return stats, err
}
stats.TodayCount = todayCount
// 昨日订单数
yesterdayBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", yesterdayStart, yesterdayEnd)
yesterdayCount, err := l.svcCtx.OrderModel.FindCount(l.ctx, yesterdayBuilder, "id")
if err != nil {
return stats, err
}
stats.YesterdayCount = yesterdayCount
// 当月订单数
monthBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", monthStart, monthEnd)
monthCount, err := l.svcCtx.OrderModel.FindCount(l.ctx, monthBuilder, "id")
if err != nil {
return stats, err
}
stats.MonthCount = monthCount
// 总订单数
totalBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ?", "paid")
totalCount, err := l.svcCtx.OrderModel.FindCount(l.ctx, totalBuilder, "id")
if err != nil {
return stats, err
}
stats.TotalCount = totalCount
// 计算变化率
if stats.YesterdayCount > 0 {
stats.ChangeRate = float64(stats.TodayCount-stats.YesterdayCount) / float64(stats.YesterdayCount) * 100
} else if stats.TodayCount > 0 {
stats.ChangeRate = 100 // 从0增长到有值算100%增长
}
return stats, nil
}
// calculateRevenueStatistics 计算营收统计
func (l *AdminGetDashboardStatisticsLogic) calculateRevenueStatistics(todayStart, todayEnd, yesterdayStart, yesterdayEnd, monthStart, monthEnd time.Time) (types.AdminRevenueStatistics, error) {
var stats types.AdminRevenueStatistics
// 今日营收
todayBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", todayStart, todayEnd)
todayAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, todayBuilder, "amount")
if err != nil {
return stats, err
}
stats.TodayAmount = todayAmount
// 昨日营收
yesterdayBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", yesterdayStart, yesterdayEnd)
yesterdayAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, yesterdayBuilder, "amount")
if err != nil {
return stats, err
}
stats.YesterdayAmount = yesterdayAmount
// 当月营收
monthBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", monthStart, monthEnd)
monthAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, monthBuilder, "amount")
if err != nil {
return stats, err
}
stats.MonthAmount = monthAmount
// 总营收
totalBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ?", "paid")
totalAmount, err := l.svcCtx.OrderModel.FindSum(l.ctx, totalBuilder, "amount")
if err != nil {
return stats, err
}
stats.TotalAmount = totalAmount
// 计算变化率
if stats.YesterdayAmount > 0 {
stats.ChangeRate = (stats.TodayAmount - stats.YesterdayAmount) / stats.YesterdayAmount * 100
} else if stats.TodayAmount > 0 {
stats.ChangeRate = 100 // 从0增长到有值算100%增长
}
return stats, nil
}
// calculateAgentStatistics 计算代理统计
func (l *AdminGetDashboardStatisticsLogic) calculateAgentStatistics(todayStart, monthStart time.Time) (types.AdminAgentStatistics, error) {
var stats types.AdminAgentStatistics
// 代理总数
totalBuilder := l.svcCtx.AgentModel.SelectBuilder()
totalCount, err := l.svcCtx.AgentModel.FindCount(l.ctx, totalBuilder, "id")
if err != nil {
return stats, err
}
stats.TotalCount = totalCount
// 今日新增
todayBuilder := l.svcCtx.AgentModel.SelectBuilder().
Where("create_time >= ?", todayStart)
todayNew, err := l.svcCtx.AgentModel.FindCount(l.ctx, todayBuilder, "id")
if err != nil {
return stats, err
}
stats.TodayNew = todayNew
// 当月新增
monthBuilder := l.svcCtx.AgentModel.SelectBuilder().
Where("create_time >= ?", monthStart)
monthNew, err := l.svcCtx.AgentModel.FindCount(l.ctx, monthBuilder, "id")
if err != nil {
return stats, err
}
stats.MonthNew = monthNew
return stats, nil
}
// calculateProfitStatistics 计算利润统计
func (l *AdminGetDashboardStatisticsLogic) calculateProfitStatistics(todayStart, todayEnd, monthStart, monthEnd time.Time, revenueStats types.AdminRevenueStatistics) (types.AdminProfitStatistics, error) {
var stats types.AdminProfitStatistics
// 公司交税比例6%
const companyTaxRate = 0.06
// 今日利润计算
// 今日营收
todayRevenue := revenueStats.TodayAmount
// 今日佣金
todayCommissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("del_state = ? AND status != ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 3, todayStart, todayEnd)
todayCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, todayCommissionBuilder, "amount")
if err != nil {
return stats, err
}
// 今日返利
todayRebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ? AND status != ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 3, todayStart, todayEnd)
todayRebate, err := l.svcCtx.AgentRebateModel.FindSum(l.ctx, todayRebateBuilder, "rebate_amount")
if err != nil {
return stats, err
}
// 今日公司交税订单金额的6%
todayCompanyTax := todayRevenue * companyTaxRate
// 今日平台收入税agent_withdrawal_tax表中tax_status=2的tax_amount总和
todayTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 2, todayStart, todayEnd)
todayTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, todayTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 今日利润 = 营收 - 佣金 - 返利 - 公司交税 + 平台收入税
stats.TodayProfit = todayRevenue - todayCommission - todayRebate - todayCompanyTax + todayTaxIncome
if todayRevenue > 0 {
stats.TodayProfitRate = stats.TodayProfit / todayRevenue * 100
}
// 当月利润计算
// 当月营收
monthRevenue := revenueStats.MonthAmount
// 当月佣金
monthCommissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("del_state = ? AND status != ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 3, monthStart, monthEnd)
monthCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, monthCommissionBuilder, "amount")
if err != nil {
return stats, err
}
// 当月返利
monthRebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ? AND status != ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 3, monthStart, monthEnd)
monthRebate, err := l.svcCtx.AgentRebateModel.FindSum(l.ctx, monthRebateBuilder, "rebate_amount")
if err != nil {
return stats, err
}
// 当月公司交税
monthCompanyTax := monthRevenue * companyTaxRate
// 当月平台收入税
monthTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ? AND create_time >= ? AND create_time < ?", globalkey.DelStateNo, 2, monthStart, monthEnd)
monthTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, monthTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 当月利润
stats.MonthProfit = monthRevenue - monthCommission - monthRebate - monthCompanyTax + monthTaxIncome
if monthRevenue > 0 {
stats.MonthProfitRate = stats.MonthProfit / monthRevenue * 100
}
// 总利润计算
// 总营收
totalRevenue := revenueStats.TotalAmount
// 总佣金
totalCommissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("del_state = ? AND status != ?", globalkey.DelStateNo, 3)
totalCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, totalCommissionBuilder, "amount")
if err != nil {
return stats, err
}
// 总返利
totalRebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("del_state = ? AND status != ?", globalkey.DelStateNo, 3)
totalRebate, err := l.svcCtx.AgentRebateModel.FindSum(l.ctx, totalRebateBuilder, "rebate_amount")
if err != nil {
return stats, err
}
// 总公司交税
totalCompanyTax := totalRevenue * companyTaxRate
// 总平台收入税
totalTaxIncomeBuilder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("del_state = ? AND tax_status = ?", globalkey.DelStateNo, 2)
totalTaxIncome, err := l.svcCtx.AgentWithdrawalTaxModel.FindSum(l.ctx, totalTaxIncomeBuilder, "tax_amount")
if err != nil {
return stats, err
}
// 总利润
stats.TotalProfit = totalRevenue - totalCommission - totalRebate - totalCompanyTax + totalTaxIncome
if totalRevenue > 0 {
stats.TotalProfitRate = stats.TotalProfit / totalRevenue * 100
}
return stats, nil
}
// calculateOrderTrend 计算订单趋势最近7天
func (l *AdminGetDashboardStatisticsLogic) calculateOrderTrend(now time.Time, loc *time.Location) ([]types.AdminTrendData, error) {
var trend []types.AdminTrendData
// 计算最近7天的日期
for i := 6; i >= 0; i-- {
date := now.AddDate(0, 0, -i)
dateStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, loc)
dateEnd := dateStart.AddDate(0, 0, 1)
// 查询当天的订单数
builder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", dateStart, dateEnd)
count, err := l.svcCtx.OrderModel.FindCount(l.ctx, builder, "id")
if err != nil {
return nil, err
}
trend = append(trend, types.AdminTrendData{
Date: date.Format("01-02"),
Value: float64(count),
})
}
return trend, nil
}
// calculateRevenueTrend 计算营收趋势最近7天
func (l *AdminGetDashboardStatisticsLogic) calculateRevenueTrend(now time.Time, loc *time.Location) ([]types.AdminTrendData, error) {
var trend []types.AdminTrendData
// 计算最近7天的日期
for i := 6; i >= 0; i-- {
date := now.AddDate(0, 0, -i)
dateStart := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, loc)
dateEnd := dateStart.AddDate(0, 0, 1)
// 查询当天的营收
builder := l.svcCtx.OrderModel.SelectBuilder().
Where("status = ? AND create_time >= ? AND create_time < ?", "paid", dateStart, dateEnd)
amount, err := l.svcCtx.OrderModel.FindSum(l.ctx, builder, "amount")
if err != nil {
return nil, err
}
trend = append(trend, types.AdminTrendData{
Date: date.Format("01-02"),
Value: amount,
})
}
return trend, nil
}

View File

@@ -34,6 +34,10 @@ func (l *AdminCreateFeatureLogic) AdminCreateFeature(req *types.AdminCreateFeatu
ApiId: req.ApiId,
Name: req.Name,
}
// 设置白名单屏蔽价格
if req.WhitelistPrice != nil {
data.WhitelistPrice = *req.WhitelistPrice
}
// 2. 数据库操作
result, err := l.svcCtx.FeatureModel.Insert(l.ctx, nil, data)

View File

@@ -35,11 +35,12 @@ func (l *AdminGetFeatureDetailLogic) AdminGetFeatureDetail(req *types.AdminGetFe
// 2. 构建响应
resp = &types.AdminGetFeatureDetailResp{
Id: record.Id,
ApiId: record.ApiId,
Name: record.Name,
CreateTime: record.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: record.UpdateTime.Format("2006-01-02 15:04:05"),
Id: record.Id,
ApiId: record.ApiId,
Name: record.Name,
WhitelistPrice: record.WhitelistPrice,
CreateTime: record.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: record.UpdateTime.Format("2006-01-02 15:04:05"),
}
return resp, nil

View File

@@ -49,11 +49,12 @@ func (l *AdminGetFeatureListLogic) AdminGetFeatureList(req *types.AdminGetFeatur
items := make([]types.FeatureListItem, 0, len(list))
for _, item := range list {
listItem := types.FeatureListItem{
Id: item.Id,
ApiId: item.ApiId,
Name: item.Name,
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
Id: item.Id,
ApiId: item.ApiId,
Name: item.Name,
WhitelistPrice: item.WhitelistPrice,
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
}
items = append(items, listItem)
}

View File

@@ -46,6 +46,9 @@ func (l *AdminUpdateFeatureLogic) AdminUpdateFeature(req *types.AdminUpdateFeatu
if req.Name != nil && *req.Name != "" {
record.Name = *req.Name
}
if req.WhitelistPrice != nil {
record.WhitelistPrice = *req.WhitelistPrice
}
// 4. 执行更新操作
err = l.svcCtx.FeatureModel.UpdateWithVersion(l.ctx, nil, record)

View File

@@ -6,7 +6,6 @@ import (
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
@@ -84,7 +83,6 @@ func (l *AdminGetOrderDetailLogic) AdminGetOrderDetail(req *types.AdminGetOrderD
// 查询清理日志
cleanupBuilder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
Where("order_id = ?", order.Id).
Where("del_state = ?", globalkey.DelStateNo).
OrderBy("create_time DESC").
Limit(1)
cleanupDetails, err := l.svcCtx.QueryCleanupDetailModel.FindAll(l.ctx, cleanupBuilder, "")

View File

@@ -7,7 +7,6 @@ import (
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/Masterminds/squirrel"
@@ -139,7 +138,6 @@ func (l *AdminGetOrderListLogic) AdminGetOrderList(req *types.AdminGetOrderListR
// 查询清理日志
cleanupBuilder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
Where(squirrel.Eq{"order_id": notFoundOrderIds}).
Where("del_state = ?", globalkey.DelStateNo).
OrderBy("create_time DESC")
cleanupDetails, err := l.svcCtx.QueryCleanupDetailModel.FindAll(l.ctx, cleanupBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {

View File

@@ -1,27 +1,30 @@
package admin_order
import (
"context"
"database/sql"
"fmt"
"time"
"context"
"database/sql"
"fmt"
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/google/uuid"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/smartwalle/alipay/v3"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
const (
PaymentPlatformAlipay = "alipay"
PaymentPlatformWechat = "wechat"
OrderStatusPaid = "paid"
RefundNoPrefix = "refund-"
PaymentPlatformAlipay = "alipay"
PaymentPlatformWechat = "wechat"
PaymentPlatformTest = "test"
PaymentPlatformTestEmpty = "test_empty"
OrderStatusPaid = "paid"
RefundNoPrefix = "refund-"
)
type AdminRefundOrderLogic struct {
@@ -44,6 +47,11 @@ func (l *AdminRefundOrderLogic) AdminRefundOrder(req *types.AdminRefundOrderReq)
return nil, err
}
// 检查是否为测试支付平台test 或 test_empty如果是则模拟退款成功
if order.PaymentPlatform == PaymentPlatformTest || order.PaymentPlatform == PaymentPlatformTestEmpty {
return l.handleTestRefund(order, req)
}
// 根据支付平台处理退款
switch order.PaymentPlatform {
case PaymentPlatformAlipay:
@@ -75,29 +83,39 @@ func (l *AdminRefundOrderLogic) getAndValidateOrder(orderId string, refundAmount
return order, nil
}
// handleTestRefund 处理测试支付平台退款(模拟退款成功)
func (l *AdminRefundOrderLogic) handleTestRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
refundNo := l.generateRefundNo(order.OrderNo)
// 创建模拟的平台退款ID
platformRefundId := fmt.Sprintf("MOCK_REFUND_%s_%d", order.OrderNo, time.Now().Unix())
logx.Infof("测试支付平台模拟退款:订单 %s退款金额 %.2f,跳过实际退款接口调用", order.OrderNo, req.RefundAmount)
// 直接标记为退款成功
err := l.createRefundRecordAndUpdateOrder(order, req, refundNo, platformRefundId, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
}
// handleAlipayRefund 处理支付宝退款
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
refundNo := l.generateRefundNo(order.OrderNo)
// 调用支付宝退款接口
var refundResp *alipay.TradeRefundRsp
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.OrderNo, req.RefundAmount)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 支付宝退款失败 err: %v", err)
}
refundNo := l.generateRefundNo(order.OrderNo)
if refundResp.IsSuccess() {
// 支付宝退款成功,创建成功记录
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
} else {
if !refundResp.IsSuccess() {
// 支付宝退款失败,创建失败记录但不更新订单状态
err = l.createRefundRecordOnly(order, req, refundNo, refundResp.TradeNo, model.OrderRefundStatusFailed)
if err != nil {
@@ -105,10 +123,24 @@ func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *type
}
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("退款失败: %v", refundResp.Msg)), "AdminRefundOrder, 支付宝退款失败")
}
// 支付宝退款成功,创建成功记录
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
if err != nil {
return nil, err
}
return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded,
RefundNo: refundNo,
Amount: req.RefundAmount,
}, nil
}
// handleWechatRefund 处理微信退款
func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
refundNo := l.generateRefundNo(order.OrderNo)
// 调用微信退款接口
err := l.svcCtx.WechatPayService.WeChatRefund(l.ctx, order.OrderNo, req.RefundAmount, order.Amount)
if err != nil {
@@ -116,7 +148,6 @@ func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *type
}
// 微信退款是异步的创建pending状态的退款记录
refundNo := l.generateRefundNo(order.OrderNo)
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", model.OrderStatusRefunding, model.OrderRefundStatusPending)
if err != nil {
return nil, err
@@ -131,20 +162,20 @@ func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *type
// createRefundRecordAndUpdateOrder 创建退款记录并更新订单状态
func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, orderStatus, refundStatus string) error {
return l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
err := l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建退款记录
refund := &model.OrderRefund{
Id: uuid.NewString(),
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus, // 使用传入的状态,不再硬编码
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
refund := &model.OrderRefund{
Id: uuid.NewString(),
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus, // 使用传入的状态,不再硬编码
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
if _, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund); err != nil {
return fmt.Errorf("创建退款记录失败: %v", err)
@@ -158,22 +189,43 @@ func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Or
return nil
})
if err != nil {
return err
}
// 退款成功后,检查并处理代理订单(在事务外执行,避免影响退款流程)
if refundStatus == model.OrderRefundStatusSuccess {
// 检查代理订单是否已处理
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(l.ctx, order.Id)
if err == nil && agentOrder != nil && agentOrder.ProcessStatus == 1 {
// 代理订单已处理,需要撤销收益
if cancelErr := l.svcCtx.AgentService.CancelAgentCommission(l.ctx, order.Id); cancelErr != nil {
logx.Errorf("撤销代理收益失败订单ID: %s, 错误: %v", order.Id, cancelErr)
// 不阻断退款流程,只记录日志(退款已成功,不能回滚)
} else {
logx.Infof("成功撤销代理收益订单ID: %s", order.Id)
}
}
}
return nil
}
// createRefundRecordOnly 仅创建退款记录,不更新订单状态(用于退款失败的情况)
func (l *AdminRefundOrderLogic) createRefundRecordOnly(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, refundStatus string) error {
refund := &model.OrderRefund{
Id: uuid.NewString(),
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus,
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
refund := &model.OrderRefund{
Id: uuid.NewString(),
RefundNo: refundNo,
PlatformRefundId: l.createNullString(platformRefundId),
OrderId: order.Id,
UserId: order.UserId,
ProductId: order.ProductId,
RefundAmount: req.RefundAmount,
RefundReason: l.createNullString(req.RefundReason),
Status: refundStatus,
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
}
_, err := l.svcCtx.OrderRefundModel.Insert(l.ctx, nil, refund)
if err != nil {

View File

@@ -4,7 +4,6 @@ import (
"context"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
@@ -27,8 +26,7 @@ func NewAdminGetQueryCleanupConfigListLogic(ctx context.Context, svcCtx *svc.Ser
func (l *AdminGetQueryCleanupConfigListLogic) AdminGetQueryCleanupConfigList(req *types.AdminGetQueryCleanupConfigListReq) (resp *types.AdminGetQueryCleanupConfigListResp, err error) {
// 构建查询条件
builder := l.svcCtx.QueryCleanupConfigModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.QueryCleanupConfigModel.SelectBuilder()
if req.Status > 0 {
builder = builder.Where("status = ?", req.Status)

View File

@@ -6,7 +6,6 @@ import (
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
@@ -40,8 +39,7 @@ func (l *AdminGetQueryCleanupDetailListLogic) AdminGetQueryCleanupDetailList(req
// 2. 构建查询条件
builder := l.svcCtx.QueryCleanupDetailModel.SelectBuilder().
Where("cleanup_log_id = ?", req.LogId).
Where("del_state = ?", globalkey.DelStateNo)
Where("cleanup_log_id = ?", req.LogId)
// 3. 并发获取总数和列表
var total int64

View File

@@ -5,7 +5,6 @@ import (
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"github.com/pkg/errors"
@@ -29,8 +28,7 @@ func NewAdminGetQueryCleanupLogListLogic(ctx context.Context, svcCtx *svc.Servic
func (l *AdminGetQueryCleanupLogListLogic) AdminGetQueryCleanupLogList(req *types.AdminGetQueryCleanupLogListReq) (resp *types.AdminGetQueryCleanupLogListResp, err error) {
// 构建查询条件
builder := l.svcCtx.QueryCleanupLogModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo)
builder := l.svcCtx.QueryCleanupLogModel.SelectBuilder()
if req.Status > 0 {
builder = builder.Where("status = ?", req.Status)

View File

@@ -6,7 +6,6 @@ import (
"time"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/globalkey"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/lzUtils"
@@ -62,53 +61,133 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
}
// 3. 验证提现金额
// 3. 验证提现方式
if req.WithdrawalType != 1 && req.WithdrawalType != 2 {
return nil, errors.Wrapf(xerr.NewErrMsg("提现方式无效"), "")
}
// 4. 验证提现信息
if req.WithdrawalType == 1 {
// 支付宝提现:验证支付宝账号和姓名
if req.PayeeAccount == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("请输入支付宝账号"), "")
}
if req.PayeeName == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("请输入收款人姓名"), "")
}
} else if req.WithdrawalType == 2 {
// 银行卡提现:验证银行卡号、开户行和姓名
if req.BankCardNo == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("请输入银行卡号"), "")
}
if req.BankName == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("请输入开户行名称"), "")
}
if req.PayeeName == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("请输入收款人姓名"), "")
}
}
// 5. 验证提现金额
if req.Amount <= 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("提现金额必须大于0"), "")
}
// 4. 获取钱包信息
// 6. 获取钱包信息
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败, %v", err)
}
// 5. 验证余额
// 7. 验证余额(包括检查是否为负数)
if wallet.Balance < 0 {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("账户存在欠款,请先补足欠款后再申请提现,当前余额:%.2f", wallet.Balance)), "")
}
if wallet.Balance < req.Amount {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("余额不足,当前余额:%.2f", wallet.Balance)), "")
}
// 6. 计算税费
// 8. 支付宝月度提现额度校验(仅针对支付宝提现)
if req.WithdrawalType == 1 {
now := time.Now()
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
nextMonthStart := monthStart.AddDate(0, 1, 0)
// 8.1 获取支付宝月度额度配置(默认 800 元)
alipayQuota := 800.0
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, "alipay_month_quota"); cfgErr == nil {
if parsed, parseErr := l.parseFloat(cfg.ConfigValue); parseErr == nil && parsed > 0 {
alipayQuota = parsed
}
}
// 8.2 统计本月已申请/成功的支付宝提现金额status IN (1,5)),避免多次申请占用超额
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
Where("agent_id = ? AND withdrawal_type = ? AND status IN (1,5) AND create_time >= ? AND create_time < ?",
agent.Id, 1, monthStart, nextMonthStart)
usedAmount, sumErr := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, withdrawBuilder, "amount")
if sumErr != nil {
return nil, errors.Wrapf(sumErr, "查询本月支付宝提现额度使用情况失败")
}
remainQuota := alipayQuota - usedAmount
if remainQuota <= 0 {
return nil, errors.Wrapf(
xerr.NewErrMsg(fmt.Sprintf("本月支付宝提现额度已用完(额度:%.2f 元),请使用银行卡提现", alipayQuota)),
"",
)
}
if req.Amount > remainQuota {
return nil, errors.Wrapf(
xerr.NewErrMsg(fmt.Sprintf("本月支付宝最高可提现 %.2f 元,请调整提现金额或使用银行卡提现", remainQuota)),
"",
)
}
}
// 9. 计算税费
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
taxInfo, err := l.calculateTax(l.ctx, agent.Id, req.Amount, yearMonth)
if err != nil {
return nil, errors.Wrapf(err, "计算税费失败")
}
// 7. 生成提现单号
withdrawNo := fmt.Sprintf("WD%d%d", time.Now().Unix(), agent.Id)
// 10. 生成提现单号
withdrawNo := fmt.Sprintf("WD%d%s", time.Now().Unix(), agent.Id)
// 8. 使用事务处理提现申请
// 11. 使用事务处理提现申请
var withdrawalId string
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
// 8.1 冻结余额
// 11.1 冻结余额
wallet.FrozenBalance += req.Amount
wallet.Balance -= req.Amount
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
return errors.Wrapf(err, "冻结余额失败")
}
// 8.2 创建提现记录
// 11.2 创建提现记录
withdrawal := &model.AgentWithdrawal{
Id: uuid.New().String(),
AgentId: agent.Id,
WithdrawNo: withdrawNo,
PayeeAccount: req.PayeeAccount,
PayeeName: req.PayeeName,
Amount: req.Amount,
ActualAmount: taxInfo.ActualAmount,
TaxAmount: taxInfo.TaxAmount,
Status: 1, // 处理中(待审核)
Id: uuid.New().String(),
AgentId: agent.Id,
WithdrawNo: withdrawNo,
WithdrawalType: req.WithdrawalType,
PayeeAccount: req.PayeeAccount,
PayeeName: req.PayeeName,
Amount: req.Amount,
ActualAmount: taxInfo.ActualAmount,
TaxAmount: taxInfo.TaxAmount,
Status: 1, // 待审核
}
// 如果是银行卡提现,设置银行卡相关字段
if req.WithdrawalType == 2 {
withdrawal.BankCardNo = lzUtils.StringToNullString(req.BankCardNo)
withdrawal.BankName = lzUtils.StringToNullString(req.BankName)
// 银行卡提现时payee_account 可以存储银行卡号(便于查询),也可以留空
if req.PayeeAccount == "" {
withdrawal.PayeeAccount = req.BankCardNo
}
}
_, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
@@ -117,7 +196,7 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
}
withdrawalId = withdrawal.Id
// 8.3 创建扣税记录
// 11.3 创建扣税记录
taxRecord := &model.AgentWithdrawalTax{
AgentId: agent.Id,
WithdrawalId: withdrawalId,
@@ -167,8 +246,10 @@ func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId string,
}
// 查询本月已提现金额
// 注意FindAll 方法会自动添加 del_state = ? 条件,所以这里不需要手动添加
// 这里对 year_month 使用反引号包裹,避免与某些数据库版本/SQL 模式下的关键字冲突
builder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
Where("agent_id = ? AND year_month = ? AND del_state = ?", agentId, yearMonth, globalkey.DelStateNo)
Where("agent_id = ? AND `year_month` = ?", agentId, yearMonth)
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(err, "查询月度提现记录失败")

View File

@@ -0,0 +1,91 @@
package agent
import (
"context"
"strings"
"ycc-server/app/main/model"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type CheckFeatureWhitelistStatusLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCheckFeatureWhitelistStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckFeatureWhitelistStatusLogic {
return &CheckFeatureWhitelistStatusLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CheckFeatureWhitelistStatusLogic) CheckFeatureWhitelistStatus(req *types.CheckFeatureWhitelistStatusReq) (resp *types.CheckFeatureWhitelistStatusResp, err error) {
// 1. 验证参数
if req.IdCard == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "")
}
if req.FeatureApiId == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("模块API标识不能为空"), "")
}
// 2. 提取主模块ID去掉下划线后的部分
// 例如JRZQ7F1A_BigDataReport -> JRZQ7F1A
mainApiId := req.FeatureApiId
if idx := strings.Index(req.FeatureApiId, "_"); idx > 0 {
mainApiId = req.FeatureApiId[:idx]
}
// 3. 查询feature信息使用主模块ID
feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, mainApiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("模块不存在"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块信息失败, %v", err)
}
// 4. 检查是否在白名单中使用主模块ID
whitelistBuilder := l.svcCtx.UserFeatureWhitelistModel.SelectBuilder().
Where("id_card = ? AND feature_api_id = ? AND status = ?", req.IdCard, mainApiId, 1)
whitelists, err := l.svcCtx.UserFeatureWhitelistModel.FindAll(l.ctx, whitelistBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单记录失败, %v", err)
}
isWhitelisted := len(whitelists) > 0
// 5. 如果提供了 queryId检查当前报告数据是否已删除
dataDeleted := false
if req.QueryId != "" {
containsFeature, err := l.svcCtx.WhitelistService.CheckQueryDataContainsFeature(l.ctx, req.QueryId, req.FeatureApiId)
if err != nil {
// 检查失败不影响主流程,记录日志即可
logx.Errorf("检查报告数据是否包含模块失败:查询记录 %s模块 %s错误%v", req.QueryId, req.FeatureApiId, err)
// 默认认为数据已删除(保守处理)
dataDeleted = true
} else {
// 如果数据中不包含该模块,说明数据已删除
dataDeleted = !containsFeature
}
} else {
// 未提供 queryId无法判断数据是否已删除默认认为已删除保守处理
dataDeleted = true
}
return &types.CheckFeatureWhitelistStatusResp{
IsWhitelisted: isWhitelisted,
WhitelistPrice: feature.WhitelistPrice,
FeatureId: feature.Id,
DataDeleted: dataDeleted,
}, nil
}

View File

@@ -0,0 +1,169 @@
package agent
import (
"context"
"fmt"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type CreateWhitelistOrderLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCreateWhitelistOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateWhitelistOrderLogic {
return &CreateWhitelistOrderLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *CreateWhitelistOrderLogic) CreateWhitelistOrder(req *types.CreateWhitelistOrderReq) (resp *types.CreateWhitelistOrderResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 获取代理信息并验证是否为钻石代理
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
}
// 只有钻石代理可以操作
if agent.Level != 3 {
return nil, errors.Wrapf(xerr.NewErrMsg("只有钻石代理可以操作白名单"), "")
}
// 2. 验证参数
if req.IdCard == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("身份证号不能为空"), "")
}
if len(req.FeatureIds) == 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("请至少选择一个模块"), "")
}
// 3. 查询feature信息并计算总金额
var totalAmount float64
var orderItems []struct {
FeatureId string
FeatureApiId string
FeatureName string
Price float64
}
for _, featureId := range req.FeatureIds {
feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, featureId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("模块不存在: %s", featureId)), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询模块信息失败, %v", err)
}
// 直接使用feature的WhitelistPrice字段
whitelistPrice := feature.WhitelistPrice
if whitelistPrice <= 0 {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("模块 %s 不支持白名单屏蔽", feature.Name)), "")
}
// 检查该身份证号+feature是否已经存在白名单记录
// 注意系统会自动处理del_state不需要手动添加
whitelistBuilder := l.svcCtx.UserFeatureWhitelistModel.SelectBuilder().
Where("id_card = ? AND feature_id = ?", req.IdCard, featureId)
existing, err := l.svcCtx.UserFeatureWhitelistModel.FindAll(l.ctx, whitelistBuilder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单记录失败, %v", err)
}
// 检查是否有生效的记录status=1
for _, item := range existing {
if item.Status == 1 {
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("身份证号 %s 的模块 %s 已经加入白名单", req.IdCard, feature.Name)), "")
}
}
totalAmount += whitelistPrice
orderItems = append(orderItems, struct {
FeatureId string
FeatureApiId string
FeatureName string
Price float64
}{
FeatureId: feature.Id,
FeatureApiId: feature.ApiId,
FeatureName: feature.Name,
Price: whitelistPrice,
})
}
// 4. 生成订单号(白名单订单前缀 W_限制长度不超过32
base := l.svcCtx.AlipayService.GenerateOutTradeNo()
orderNo := "W_" + base
if len(orderNo) > 32 {
orderNo = orderNo[:32]
}
// 5. 使用事务创建订单和订单明细
var orderId string
err = l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 创建订单
order := &model.WhitelistOrder{
Id: uuid.NewString(),
OrderNo: orderNo,
UserId: userID,
IdCard: req.IdCard,
TotalAmount: totalAmount,
Status: 1, // 待支付
}
_, err := l.svcCtx.WhitelistOrderModel.Insert(ctx, session, order)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建订单失败, %v", err)
}
orderId = order.Id
// 创建订单明细
for _, item := range orderItems {
orderItem := &model.WhitelistOrderItem{
Id: uuid.NewString(),
OrderId: orderId,
FeatureId: item.FeatureId,
FeatureApiId: item.FeatureApiId,
FeatureName: item.FeatureName,
Price: item.Price,
}
_, err := l.svcCtx.WhitelistOrderItemModel.Insert(ctx, session, orderItem)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建订单明细失败, %v", err)
}
}
return nil
})
if err != nil {
return nil, err
}
return &types.CreateWhitelistOrderResp{
OrderId: orderId,
OrderNo: orderNo,
TotalAmount: totalAmount,
}, nil
}

View File

@@ -185,7 +185,6 @@ func (l *GetConversionRateLogic) calculateConversionRate(agentId string, subordi
func (l *GetConversionRateLogic) calculatePeriodConversion(agentId string, subordinateIds []string, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
// 构建 agent_order 查询条件
agentOrderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo).
Where("create_time >= ? AND create_time < ?", startTime, endTime)
if agentId != "" {
@@ -252,8 +251,7 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId string, subor
// 查询订单信息
orderBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where(squirrel.Eq{"id": orderIds}).
Where("del_state = ?", globalkey.DelStateNo)
Where(squirrel.Eq{"id": orderIds})
orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, orderBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
@@ -305,9 +303,9 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId string, subor
// 2. 付费量和金额通过agent_rebate表统计只有付费的订单才会产生返佣
func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgentId string, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
// 1. 查询agent_rebate表获取所有曾经给当前用户产生返佣的source_agent_id这些代理在某个时间点是下级
// 不限制时间,获取所有历史返佣记录,用于确定哪些代理曾经是下级
// 不限制时间,获取所有历史返佣记录,用于确定哪些代理曾经是下级(排除已取消的记录 status=3
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", parentAgentId, globalkey.DelStateNo).
Where("agent_id = ? AND del_state = ? AND status != ?", parentAgentId, globalkey.DelStateNo, 3).
Where("source_agent_id != ?", parentAgentId)
allRebates, err := l.svcCtx.AgentRebateModel.FindAll(l.ctx, rebateBuilder, "")
@@ -360,7 +358,6 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
// 2. 查询agent_order表统计这些代理在时间段内的所有订单包括未付费的
agentOrderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().
Where("del_state = ?", globalkey.DelStateNo).
Where("create_time >= ? AND create_time < ?", startTime, endTime).
Where(squirrel.Eq{"agent_id": sourceAgentIds})
@@ -413,8 +410,7 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
// 4. 查询订单信息
orderBuilder := l.svcCtx.OrderModel.SelectBuilder().
Where(squirrel.Eq{"id": orderIds}).
Where("del_state = ?", globalkey.DelStateNo)
Where(squirrel.Eq{"id": orderIds})
orders, err := l.svcCtx.OrderModel.FindAll(l.ctx, orderBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
@@ -429,9 +425,9 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
}
}
// 5. 查询时间段内的返佣记录,获取已付费订单的金额
// 5. 查询时间段内的返佣记录,获取已付费订单的金额(排除已取消的记录 status=3
rebateBuilder = l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", parentAgentId, globalkey.DelStateNo).
Where("agent_id = ? AND del_state = ? AND status != ?", parentAgentId, globalkey.DelStateNo, 3).
Where("source_agent_id != ?", parentAgentId).
Where("create_time >= ? AND create_time < ?", startTime, endTime).
Where(squirrel.Eq{"order_id": orderIds})

View File

@@ -0,0 +1,95 @@
package agent
import (
"context"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetLastWithdrawalInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetLastWithdrawalInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLastWithdrawalInfoLogic {
return &GetLastWithdrawalInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetLastWithdrawalInfoLogic) GetLastWithdrawalInfo(req *types.GetLastWithdrawalInfoReq) (resp *types.GetLastWithdrawalInfoResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 获取代理信息
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 验证提现方式
if req.WithdrawalType != 1 && req.WithdrawalType != 2 {
return nil, errors.Wrapf(xerr.NewErrMsg("提现方式无效"), "")
}
// 3. 查询该代理最近一次该类型的提现记录
// 注意FindAll 方法会自动添加 del_state = ? 条件,所以这里不需要手动添加
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
Where("agent_id = ? AND withdrawal_type = ?", agent.Id, req.WithdrawalType).
OrderBy("create_time DESC").
Limit(1)
withdrawals, err := l.svcCtx.AgentWithdrawalModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上次提现记录失败, %v", err)
}
// 4. 如果没有找到记录,返回空信息
if len(withdrawals) == 0 {
return &types.GetLastWithdrawalInfoResp{
WithdrawalType: req.WithdrawalType,
PayeeAccount: "",
PayeeName: "",
BankCardNo: "",
BankName: "",
}, nil
}
// 5. 组装响应
lastWithdrawal := withdrawals[0]
resp = &types.GetLastWithdrawalInfoResp{
WithdrawalType: lastWithdrawal.WithdrawalType,
PayeeAccount: lastWithdrawal.PayeeAccount,
PayeeName: lastWithdrawal.PayeeName,
BankCardNo: "",
BankName: "",
}
// 如果是银行卡提现,填充银行卡信息
if lastWithdrawal.WithdrawalType == 2 {
if lastWithdrawal.BankCardNo.Valid {
resp.BankCardNo = lastWithdrawal.BankCardNo.String
}
if lastWithdrawal.BankName.Valid {
resp.BankName = lastWithdrawal.BankName.String
}
}
return resp, nil
}

View File

@@ -117,6 +117,7 @@ func (l *GetRebateListLogic) GetRebateList(req *types.GetRebateListReq) (resp *t
OrderNo: orderNo,
RebateType: rebate.RebateType,
Amount: rebate.RebateAmount,
Status: rebate.Status,
CreateTime: rebate.CreateTime.Format("2006-01-02 15:04:05"),
})
}

View File

@@ -2,6 +2,7 @@ package agent
import (
"context"
"fmt"
"time"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
@@ -57,26 +58,27 @@ func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp,
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
// 本月开始时间1号 00:00:00
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
nextMonthStart := monthStart.AddDate(0, 1, 0)
// 3. 统计佣金总额(从 agent_commission 表统计)
// 3. 统计佣金总额(从 agent_commission 表统计,排除已取消的记录 status=3
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo)
Where("agent_id = ? AND del_state = ? AND status != ?", agent.Id, globalkey.DelStateNo, 3)
commissionTotal, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionBuilder, "amount")
// 3.1 统计佣金今日收益
// 3.1 统计佣金今日收益(排除已取消的记录 status=3
commissionTodayBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, todayStart)
Where("agent_id = ? AND del_state = ? AND status != ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, 3, todayStart)
commissionToday, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionTodayBuilder, "amount")
// 3.2 统计佣金本月收益
// 3.2 统计佣金本月收益(排除已取消的记录 status=3
commissionMonthBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, monthStart)
Where("agent_id = ? AND del_state = ? AND status != ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, 3, monthStart)
commissionMonth, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionMonthBuilder, "amount")
// 4. 统计返佣总额(包括推广返佣和升级返佣)
// 4.1 统计推广返佣(从 agent_rebate 表)
// 4. 统计返佣总额(包括推广返佣和升级返佣,排除已取消的记录 status=3
// 4.1 统计推广返佣(从 agent_rebate 表,排除已取消的记录 status=3
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo)
Where("agent_id = ? AND del_state = ? AND status != ?", agent.Id, globalkey.DelStateNo, 3)
promoteRebateTotal, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder, "rebate_amount")
// 4.2 统计升级返佣(从 agent_upgrade 表,查询 rebate_agent_id = 当前代理ID 且 status = 2已完成且 upgrade_type = 1自主付费的记录
@@ -88,10 +90,10 @@ func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp,
rebateTotal := promoteRebateTotal + upgradeRebateTotal
// 4.3 统计返佣今日收益
// 4.3 统计返佣今日收益(排除已取消的记录 status=3
// 推广返佣今日
promoteRebateTodayBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, todayStart)
Where("agent_id = ? AND del_state = ? AND status != ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, 3, todayStart)
promoteRebateToday, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, promoteRebateTodayBuilder, "rebate_amount")
// 升级返佣今日
upgradeRebateTodayBuilder := l.svcCtx.AgentUpgradeModel.SelectBuilder().
@@ -100,10 +102,10 @@ func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp,
upgradeRebateToday, _ := l.svcCtx.AgentUpgradeModel.FindSum(l.ctx, upgradeRebateTodayBuilder, "rebate_amount")
rebateToday := promoteRebateToday + upgradeRebateToday
// 4.4 统计返佣本月收益
// 4.4 统计返佣本月收益(排除已取消的记录 status=3
// 推广返佣本月
promoteRebateMonthBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, monthStart)
Where("agent_id = ? AND del_state = ? AND status != ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, 3, monthStart)
promoteRebateMonth, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, promoteRebateMonthBuilder, "rebate_amount")
// 升级返佣本月
upgradeRebateMonthBuilder := l.svcCtx.AgentUpgradeModel.SelectBuilder().
@@ -112,16 +114,43 @@ func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp,
upgradeRebateMonth, _ := l.svcCtx.AgentUpgradeModel.FindSum(l.ctx, upgradeRebateMonthBuilder, "rebate_amount")
rebateMonth := promoteRebateMonth + upgradeRebateMonth
// 5. 统计本月支付宝提现额度使用情况
// 5.1 获取配置的支付宝月度额度(默认 800 元)
alipayQuota := 800.0
if cfg, cfgErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, "alipay_month_quota"); cfgErr == nil {
if parsed, parseErr := parseFloatConfig(cfg.ConfigValue); parseErr == nil && parsed > 0 {
alipayQuota = parsed
}
}
// 5.2 统计本月已申请/成功的支付宝提现金额status IN (1,5)),用于前端展示额度占用
withdrawBuilder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
Where("agent_id = ? AND withdrawal_type = ? AND status IN (1,5) AND create_time >= ? AND create_time < ?",
agent.Id, 1, monthStart, nextMonthStart)
alipayUsed, _ := l.svcCtx.AgentWithdrawalModel.FindSum(l.ctx, withdrawBuilder, "amount")
if alipayUsed < 0 {
alipayUsed = 0
}
return &types.GetRevenueInfoResp{
Balance: wallet.Balance,
FrozenBalance: wallet.FrozenBalance,
TotalEarnings: wallet.TotalEarnings,
WithdrawnAmount: wallet.WithdrawnAmount,
CommissionTotal: commissionTotal, // 佣金累计总收益(推广订单获得的佣金)
CommissionToday: commissionToday, // 佣金今日收益
CommissionMonth: commissionMonth, // 佣金本月收益
RebateTotal: rebateTotal, // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday: rebateToday, // 返佣今日收益
RebateMonth: rebateMonth, // 返佣本月收益
Balance: wallet.Balance,
FrozenBalance: wallet.FrozenBalance,
TotalEarnings: wallet.TotalEarnings,
WithdrawnAmount: wallet.WithdrawnAmount,
CommissionTotal: commissionTotal, // 佣金累计总收益(推广订单获得的佣金)
CommissionToday: commissionToday, // 佣金今日收益
CommissionMonth: commissionMonth, // 佣金本月收益
RebateTotal: rebateTotal, // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday: rebateToday, // 返佣今日收益
RebateMonth: rebateMonth, // 返佣本月收益
AlipayMonthQuota: alipayQuota, // 支付宝每月提现总额度
AlipayMonthUsed: alipayUsed, // 本月已使用的支付宝提现额度
}, nil
}
// parseFloatConfig 解析配置中的浮点数
func parseFloatConfig(s string) (float64, error) {
var result float64
_, err := fmt.Sscanf(s, "%f", &result)
return result, err
}

View File

@@ -88,10 +88,10 @@ func (l *GetSubordinateContributionDetailLogic) GetSubordinateContributionDetail
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 7. 统计订单数据(仅统计有返佣给当前用户的订单)
// 7. 统计订单数据(仅统计有返佣给当前用户的订单,排除已取消的记录 status=3
// 通过 agent_rebate 表统计 DISTINCT order_id
rebateBaseBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agent.Id, req.SubordinateId, globalkey.DelStateNo)
Where("agent_id = ? AND source_agent_id = ? AND del_state = ? AND status != ?", agent.Id, req.SubordinateId, globalkey.DelStateNo, 3)
// 总订单量去重订单ID
totalOrders := l.countDistinctOrders(l.ctx, rebateBaseBuilder)
@@ -104,7 +104,7 @@ func (l *GetSubordinateContributionDetailLogic) GetSubordinateContributionDetail
monthRebateBuilder := rebateBaseBuilder.Where("create_time >= ?", monthStart)
monthOrders := l.countDistinctOrders(l.ctx, monthRebateBuilder)
// 8. 统计返佣金额
// 8. 统计返佣金额(排除已取消的记录 status=3
totalRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBaseBuilder, "rebate_amount")
todayRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, todayRebateBuilder, "rebate_amount")
monthRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, monthRebateBuilder, "rebate_amount")
@@ -227,12 +227,12 @@ func (l *GetSubordinateContributionDetailLogic) countDistinctOrders(ctx context.
func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context, agentId, subordinateId string, page, pageSize int64) ([]types.OrderItem, int64, error) {
// 1. 查询所有返佣记录
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agentId, subordinateId, globalkey.DelStateNo).
Where("agent_id = ? AND source_agent_id = ? AND del_state = ? AND status != ?", agentId, subordinateId, globalkey.DelStateNo, 3).
OrderBy("create_time DESC")
// 查询总数去重订单ID
totalBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agentId, subordinateId, globalkey.DelStateNo)
Where("agent_id = ? AND source_agent_id = ? AND del_state = ? AND status != ?", agentId, subordinateId, globalkey.DelStateNo, 3)
total := l.countDistinctOrders(ctx, totalBuilder)
// 查询所有返佣记录

View File

@@ -109,8 +109,7 @@ func (l *GetTeamListLogic) GetTeamList(req *types.GetTeamListReq) (resp *types.G
// 7. 查询所有下级代理信息(不进行手机号过滤,因为需要模糊搜索)
builder := l.svcCtx.AgentModel.SelectBuilder().
Where(squirrel.Eq{"id": subordinateIds}).
Where("del_state = ?", globalkey.DelStateNo)
Where(squirrel.Eq{"id": subordinateIds})
allTeamMembers, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "")
if err != nil {
@@ -207,10 +206,10 @@ func (l *GetTeamListLogic) calculateTeamStatistics(agentId string, subordinateId
stats.TodayNewMembers = todayNewCount
stats.MonthNewMembers = monthNewCount
// 统计团队总查询量仅统计有返佣给当前用户的订单通过agent_rebate表去重统计order_id
// 统计团队总查询量仅统计有返佣给当前用户的订单通过agent_rebate表去重统计order_id,排除已取消的记录 status=3
if len(subordinateIds) > 0 {
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agentId, globalkey.DelStateNo).
Where("agent_id = ? AND del_state = ? AND status != ?", agentId, globalkey.DelStateNo, 3).
Where("source_agent_id != ?", agentId). // 明确排除自己
Where(squirrel.Eq{"source_agent_id": subordinateIds})
@@ -229,14 +228,15 @@ func (l *GetTeamListLogic) calculateTeamStatistics(agentId string, subordinateId
stats.MonthPromotions = monthPromotions
}
// 统计收益:只统计从下级获得的返佣(依靠团队得到的收益)
// 统计收益:只统计从下级获得的返佣(依靠团队得到的收益,排除已取消的记录 status=3
// 返佣从agent_rebate表统计从下级获得的返佣
// agent_id = 当前代理ID获得返佣的代理
// source_agent_id IN 下级ID列表来源代理即产生订单的下级代理
// 明确排除自己source_agent_id != agentId确保不统计自己的数据
// 排除已取消status != 3确保不统计已撤销的返佣
if len(subordinateIds) > 0 {
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND del_state = ?", agentId, globalkey.DelStateNo).
Where("agent_id = ? AND del_state = ? AND status != ?", agentId, globalkey.DelStateNo, 3).
Where("source_agent_id != ?", agentId). // 明确排除自己
Where(squirrel.Eq{"source_agent_id": subordinateIds})
totalRebate, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder, "rebate_amount")
@@ -304,16 +304,16 @@ func (l *GetTeamListLogic) buildTeamMemberItem(agentId string, member *model.Age
}
}
// 统计查询量仅统计有返佣给当前用户的订单通过agent_rebate表去重统计order_id
// 统计查询量仅统计有返佣给当前用户的订单通过agent_rebate表去重统计order_id,排除已取消的记录 status=3
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agentId, member.Id, globalkey.DelStateNo)
Where("agent_id = ? AND source_agent_id = ? AND del_state = ? AND status != ?", agentId, member.Id, globalkey.DelStateNo, 3)
totalQueries := l.countDistinctOrdersForMember(l.ctx, rebateBuilder)
todayRebateBuilder := rebateBuilder.Where("create_time >= ?", todayStart)
todayQueries := l.countDistinctOrdersForMember(l.ctx, todayRebateBuilder)
monthRebateBuilder := rebateBuilder.Where("create_time >= ?", monthStart)
monthQueries := l.countDistinctOrdersForMember(l.ctx, monthRebateBuilder)
// 统计返佣给我的金额从agent_rebate表source_agent_id = member.Id, agent_id = agentId
// 统计返佣给我的金额从agent_rebate表source_agent_id = member.Id, agent_id = agentId,排除已取消的记录 status=3
totalRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder, "rebate_amount")
todayRebateAmount, _ := l.svcCtx.AgentRebateModel.FindSum(l.ctx, rebateBuilder.Where("create_time >= ?", todayStart), "rebate_amount")

View File

@@ -47,12 +47,12 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
}
// 2. 递归查询所有下级(直接+间接)
allSubordinateIds := make(map[string]bool)
directSubordinateIds := make(map[string]bool)
allSubordinateIds := make(map[string]bool)
directSubordinateIds := make(map[string]bool)
// 递归函数收集所有下级ID
var collectSubordinates func(string) error
collectSubordinates = func(parentId string) error {
var collectSubordinates func(string) error
collectSubordinates = func(parentId string) error {
// 查询直接下级
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo)
@@ -63,23 +63,23 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
for _, relation := range relations {
// 如果是第一层,标记为直接下级
if parentId == agent.Id {
directSubordinateIds[relation.ChildId] = true
}
// 添加到所有下级集合
allSubordinateIds[relation.ChildId] = true
// 递归查询下级的下级
if err := collectSubordinates(relation.ChildId); err != nil {
return err
}
}
return nil
}
if parentId == agent.Id {
directSubordinateIds[relation.ChildId] = true
}
// 添加到所有下级集合
allSubordinateIds[relation.ChildId] = true
// 递归查询下级的下级
if err := collectSubordinates(relation.ChildId); err != nil {
return err
}
}
return nil
}
// 开始递归收集所有下级
if err := collectSubordinates(agent.Id); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级关系失败, %v", err)
}
if err := collectSubordinates(agent.Id); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级关系失败, %v", err)
}
// 3. 获取当前时间用于统计今日和本月新增
now := time.Now()
@@ -100,15 +100,14 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
}
// 5. 将下级ID转换为切片用于查询
subordinateIds := make([]string, 0, len(allSubordinateIds))
for id := range allSubordinateIds {
subordinateIds = append(subordinateIds, id)
}
subordinateIds := make([]string, 0, len(allSubordinateIds))
for id := range allSubordinateIds {
subordinateIds = append(subordinateIds, id)
}
// 6. 查询所有下级代理信息
builder := l.svcCtx.AgentModel.SelectBuilder().
Where(squirrel.Eq{"id": subordinateIds}).
Where("del_state = ?", globalkey.DelStateNo)
Where(squirrel.Eq{"id": subordinateIds})
teamMembers, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "")
if err != nil {

View File

@@ -0,0 +1,57 @@
package agent
import (
"context"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetWhitelistFeaturesLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetWhitelistFeaturesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWhitelistFeaturesLogic {
return &GetWhitelistFeaturesLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetWhitelistFeaturesLogic) GetWhitelistFeatures(req *types.GetWhitelistFeaturesReq) (resp *types.GetWhitelistFeaturesResp, err error) {
// 使用SelectBuilder查询所有feature然后筛选whitelist_price > 0的
// 注意系统会自动处理del_state不需要手动添加
builder := l.svcCtx.FeatureModel.SelectBuilder().
Where("whitelist_price > 0").
OrderBy("name ASC")
features, err := l.svcCtx.FeatureModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询feature列表失败, %v", err)
}
// 组装响应直接使用feature的WhitelistPrice字段
list := make([]types.WhitelistFeatureItem, 0, len(features))
for _, f := range features {
if f.WhitelistPrice > 0 {
list = append(list, types.WhitelistFeatureItem{
FeatureId: f.Id,
FeatureApiId: f.ApiId,
FeatureName: f.Name,
WhitelistPrice: f.WhitelistPrice,
})
}
}
return &types.GetWhitelistFeaturesResp{
List: list,
}, nil
}

View File

@@ -0,0 +1,117 @@
package agent
import (
"context"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetWhitelistListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetWhitelistListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWhitelistListLogic {
return &GetWhitelistListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetWhitelistListLogic) GetWhitelistList(req *types.GetWhitelistListReq) (resp *types.GetWhitelistListResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
// 1. 验证是否为代理(查询白名单列表需要是代理)
_, err = l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
}
// 2. 构建查询条件
// 注意系统会自动处理del_state不需要手动添加
builder := l.svcCtx.UserFeatureWhitelistModel.SelectBuilder().
Where("user_id = ?", userID)
// 如果指定了身份证号,添加筛选条件
if req.IdCard != "" {
builder = builder.Where("id_card = ?", req.IdCard)
}
// 3. 分页查询
total, err := l.svcCtx.UserFeatureWhitelistModel.FindCount(l.ctx, builder, "id")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询总数失败, %v", err)
}
whitelists, err := l.svcCtx.UserFeatureWhitelistModel.FindPageListByPage(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单列表失败, %v", err)
}
// 4. 组装响应
list := make([]types.WhitelistItem, 0, len(whitelists))
for _, w := range whitelists {
statusText := "生效"
if w.Status == 2 {
statusText = "已失效"
}
list = append(list, types.WhitelistItem{
Id: w.Id,
IdCard: w.IdCard,
FeatureId: w.FeatureId,
FeatureApiId: w.FeatureApiId,
FeatureName: "", // 需要从feature表查询或者冗余存储
Amount: w.Amount,
Status: w.Status,
StatusText: statusText,
CreateTime: w.CreateTime.Format("2006-01-02 15:04:05"),
})
}
// 5. 查询feature名称批量查询优化
if len(list) > 0 {
featureIds := make([]string, 0, len(list))
featureMap := make(map[string]string) // feature_id -> feature_name
for _, item := range list {
featureIds = append(featureIds, item.FeatureId)
}
// 批量查询feature
for _, featureId := range featureIds {
feature, err := l.svcCtx.FeatureModel.FindOne(l.ctx, featureId)
if err == nil {
featureMap[featureId] = feature.Name
}
}
// 填充feature名称
for i := range list {
if name, ok := featureMap[list[i].FeatureId]; ok {
list[i].FeatureName = name
}
}
}
return &types.GetWhitelistListResp{
Total: total,
List: list,
}, nil
}

View File

@@ -81,18 +81,31 @@ func (l *GetWithdrawalListLogic) GetWithdrawalList(req *types.GetWithdrawalListR
remark = withdrawal.Remark.String
}
list = append(list, types.WithdrawalItem{
Id: withdrawal.Id,
WithdrawalNo: withdrawal.WithdrawNo,
Amount: withdrawal.Amount,
TaxAmount: withdrawal.TaxAmount,
ActualAmount: withdrawal.ActualAmount,
Status: withdrawal.Status,
PayeeAccount: withdrawal.PayeeAccount,
PayeeName: withdrawal.PayeeName,
Remark: remark,
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
})
item := types.WithdrawalItem{
Id: withdrawal.Id,
WithdrawalNo: withdrawal.WithdrawNo,
WithdrawalType: withdrawal.WithdrawalType,
Amount: withdrawal.Amount,
TaxAmount: withdrawal.TaxAmount,
ActualAmount: withdrawal.ActualAmount,
Status: withdrawal.Status,
PayeeAccount: withdrawal.PayeeAccount,
PayeeName: withdrawal.PayeeName,
Remark: remark,
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
}
// 如果是银行卡提现,填充银行卡信息
if withdrawal.WithdrawalType == 2 {
if withdrawal.BankCardNo.Valid {
item.BankCardNo = withdrawal.BankCardNo.String
}
if withdrawal.BankName.Valid {
item.BankName = withdrawal.BankName.String
}
}
list = append(list, item)
}
return &types.GetWithdrawalListResp{

View File

@@ -0,0 +1,174 @@
package agent
import (
"context"
"encoding/hex"
"encoding/json"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type OfflineFeatureLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewOfflineFeatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OfflineFeatureLogic {
return &OfflineFeatureLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *OfflineFeatureLogic) OfflineFeature(req *types.OfflineFeatureReq) (resp *types.OfflineFeatureResp, err error) {
// 1. 验证参数
if err := l.validateRequest(req); err != nil {
return nil, err
}
// 2. 获取用户ID并验证代理权限
userID, err := l.verifyDiamondAgent()
if err != nil {
return nil, err
}
// 3. 获取查询记录和身份证号
queryModel, idCard, err := l.getQueryInfo(req.QueryId)
if err != nil {
return nil, err
}
_ = queryModel // 避免未使用变量警告
// 4. 调用 WhitelistService 统一下架处理
needPay, amount, whitelistCreated, err := l.svcCtx.WhitelistService.ProcessOfflineFeature(
l.ctx,
nil, // 不使用事务,因为可能只是检查
idCard,
req.FeatureApiId,
userID,
req.QueryId,
)
if err != nil {
return nil, err
}
// 5. 如果已创建白名单,需要删除报告数据
if whitelistCreated {
if err := l.deleteQueryData(req.QueryId, req.FeatureApiId); err != nil {
// 删除报告数据失败不影响主流程,只记录日志
logx.Errorf("下架模块后删除报告数据失败:查询记录 %s模块 %s错误%v", req.QueryId, req.FeatureApiId, err)
}
}
return &types.OfflineFeatureResp{
Success: whitelistCreated,
NeedPay: needPay,
Amount: amount,
}, nil
}
// validateRequest 验证请求参数
func (l *OfflineFeatureLogic) validateRequest(req *types.OfflineFeatureReq) error {
if req.QueryId == "" {
return errors.Wrapf(xerr.NewErrMsg("查询记录ID不能为空"), "")
}
if req.FeatureApiId == "" {
return errors.Wrapf(xerr.NewErrMsg("模块API标识不能为空"), "")
}
return nil
}
// verifyDiamondAgent 验证是否为钻石代理并返回用户ID
func (l *OfflineFeatureLogic) verifyDiamondAgent() (string, error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
}
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
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 agent.Level != 3 {
return "", errors.Wrapf(xerr.NewErrMsg("只有钻石代理可以操作白名单下架"), "")
}
return userID, nil
}
// getQueryInfo 获取查询记录和身份证号
func (l *OfflineFeatureLogic) getQueryInfo(queryId string) (*model.Query, string, error) {
queryModel, err := l.svcCtx.QueryModel.FindOne(l.ctx, queryId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, "", errors.Wrapf(xerr.NewErrMsg("查询记录不存在"), "")
}
return nil, "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询报告记录失败, %v", err)
}
// 解密 QueryParams 获取 idCard
idCard, err := l.extractIdCardFromQueryParams(queryModel)
if err != nil {
return nil, "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取身份证号失败, %v", err)
}
if idCard == "" {
return nil, "", errors.Wrapf(xerr.NewErrMsg("查询参数中缺少身份证号"), "")
}
return queryModel, idCard, nil
}
// extractIdCardFromQueryParams 从 QueryParams 中提取身份证号
func (l *OfflineFeatureLogic) extractIdCardFromQueryParams(queryModel *model.Query) (string, error) {
if queryModel.QueryParams == "" {
return "", errors.New("查询参数为空")
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", errors.Wrap(decodeErr, "获取AES密钥失败")
}
decryptedParams, decryptErr := crypto.AesDecrypt(queryModel.QueryParams, key)
if decryptErr != nil {
return "", errors.Wrap(decryptErr, "解密查询参数失败")
}
var params map[string]interface{}
unmarshalErr := json.Unmarshal(decryptedParams, &params)
if unmarshalErr != nil {
return "", errors.Wrap(unmarshalErr, "解析查询参数失败")
}
idCard, ok := params["id_card"].(string)
if !ok || idCard == "" {
return "", errors.New("查询参数中缺少 id_card 或 id_card 为空")
}
return idCard, nil
}
// deleteQueryData 删除报告数据
func (l *OfflineFeatureLogic) deleteQueryData(queryId, featureApiId string) error {
return l.svcCtx.QueryModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
return l.svcCtx.WhitelistService.DeleteFeatureFromQueryData(ctx, session, queryId, featureApiId)
})
}

View File

@@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/hex"
"fmt"
"os"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
@@ -61,39 +62,42 @@ func (l *RealNameAuthLogic) RealNameAuth(req *types.RealNameAuthReq) (resp *type
return nil, errors.Wrapf(xerr.NewErrMsg("手机号与代理注册手机号不匹配"), "")
}
// 3. 验证验证码
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败, %v", err)
}
redisKey := fmt.Sprintf("realName:%s", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
if err != nil {
if errors.Is(err, redis.Nil) {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
// 开发环境下跳过验证码校验
if os.Getenv("ENV") != "development" {
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败, %v", err)
}
redisKey := fmt.Sprintf("realName:%s", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
if err != nil {
if errors.Is(err, redis.Nil) {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
}
if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
}
if cacheCode != req.Code {
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
}
// 4. 三要素核验(姓名、身份证号、手机号)
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(service.ThreeFactorVerificationRequest{
Name: req.Name,
IDCard: req.IdCard,
Mobile: req.Mobile,
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素核验失败: %v", err)
}
if !verification.Passed {
if verification.Err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg(verification.Err.Error()), "三要素核验不通过")
if os.Getenv("ENV") != "development" {
// 4. 三要素核验(姓名、身份证号、手机号)
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(service.ThreeFactorVerificationRequest{
Name: req.Name,
IDCard: req.IdCard,
Mobile: req.Mobile,
})
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素核验失败: %v", err)
}
if !verification.Passed {
if verification.Err != nil {
return nil, errors.Wrapf(xerr.NewErrMsg(verification.Err.Error()), "三要素核验不通过")
}
return nil, errors.Wrapf(xerr.NewErrMsg("三要素核验不通过"), "")
}
return nil, errors.Wrapf(xerr.NewErrMsg("三要素核验不通过"), "")
}
// 5. 检查是否已有实名认证记录
existingRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {

View File

@@ -45,6 +45,9 @@ func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Requ
} else if strings.HasPrefix(orderNo, "U_") {
// 代理升级订单处理
return l.handleAgentUpgradeOrderPayment(w, notification)
} else if strings.HasPrefix(orderNo, "W_") {
// 白名单下架订单处理
return l.handleWhitelistOrderPayment(w, notification)
} else if strings.HasPrefix(orderNo, "A_") {
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
// return l.handleAgentVipOrderPayment(w, notification)
@@ -110,6 +113,96 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not
return nil
}
// 处理白名单下架订单支付
func (l *AlipayCallbackLogic) handleWhitelistOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
orderNo := notification.OutTradeNo
// 1. 查找订单
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
if findOrderErr != nil {
logx.Errorf("支付宝白名单支付回调,查找订单失败: %+v", findOrderErr)
alipay.ACKNotification(w)
return nil
}
// 2. 验证金额
amount := lzUtils.ToAlipayAmount(order.Amount)
user, err := l.svcCtx.UserModel.FindOne(l.ctx, order.UserId)
if err == nil && user.Inside != 1 {
if amount != notification.TotalAmount {
logx.Errorf("支付宝白名单支付回调,金额不一致,订单号: %s", orderNo)
alipay.ACKNotification(w)
return nil
}
}
// 3. 检查订单状态
if order.Status != "pending" {
alipay.ACKNotification(w)
return nil
}
// 4. 查找白名单订单
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
if findWhitelistErr != nil {
logx.Errorf("支付宝白名单支付回调,查找白名单订单失败,订单号: %s, 错误: %+v", orderNo, findWhitelistErr)
alipay.ACKNotification(w)
return nil
}
if whitelistOrder.Status != 1 {
// 白名单订单状态不是待支付,直接返回成功
alipay.ACKNotification(w)
return nil
}
// 5. 处理支付状态
switch notification.TradeStatus {
case alipay.TradeStatusSuccess:
order.Status = "paid"
order.PayTime = lzUtils.TimeToNullTime(time.Now())
order.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
whitelistOrder.Status = 2 // 已支付
whitelistOrder.PayTime = lzUtils.TimeToNullTime(time.Now())
case alipay.TradeStatusClosed:
order.Status = "closed"
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
whitelistOrder.Status = 3 // 已取消
default:
alipay.ACKNotification(w)
return nil
}
// 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据
err = l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 6.1 更新订单状态
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); updateErr != nil {
return errors.Wrapf(updateErr, "支付宝白名单支付回调,更新订单状态失败")
}
// 6.2 更新白名单订单状态
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(ctx, session, whitelistOrder); updateErr != nil {
return errors.Wrapf(updateErr, "支付宝白名单支付回调,更新白名单订单状态失败")
}
// 6.3 如果支付成功,调用 WhitelistService 处理白名单和报告数据
if whitelistOrder.Status == 2 {
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(ctx, session, order, whitelistOrder); processErr != nil {
return errors.Wrapf(processErr, "支付宝白名单支付回调,处理白名单订单失败")
}
}
return nil
})
if err != nil {
logx.Errorf("支付宝白名单支付回调,事务处理失败: %+v", err)
alipay.ACKNotification(w)
return nil
}
alipay.ACKNotification(w)
return nil
}
// 处理代理升级订单支付
func (l *AlipayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *alipay.Notification) error {
orderNo := notification.OutTradeNo

View File

@@ -0,0 +1,240 @@
package pay
import (
"context"
"encoding/json"
"net/http"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/model"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/google/uuid"
"github.com/smartwalle/alipay/v3"
"github.com/zeromicro/go-zero/core/logx"
)
type AlipayFromLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAlipayFromLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AlipayFromLogic {
return &AlipayFromLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// TradeComplainContent 交易投诉通知的 biz_content 内容
type TradeComplainContent struct {
ComplainEventID string `json:"complain_event_id"` // 支付宝侧投诉单号
ComplainNotifyAppID string `json:"complain_notify_app_id"` // 投诉消息通知的应用ID
Status string `json:"status"` // 投诉单状态可能的枚举值如下用Go语法注释:
/*
MERCHANT_PROCESSING // 待处理
MERCHANT_FEEDBACKED // 已处理
FINISHED // 投诉完结
CANCELLED // 投诉关闭
PLATFORM_PROCESSING // 客服处理中
PLATFORM_FINISH // 客服处理完结
CLOSED // 投诉关闭
*/
BizType string `json:"biz_type"` // 业务类型
OrderID string `json:"order_id"` // 订单ID
}
// SecurityRiskComplaintsNotifyContent 安全风险投诉商户通知的 biz_content 内容
type SecurityRiskComplaintsNotifyContent struct {
ComplaintID string `json:"complaint_id"` // 投诉ID
MessageType string `json:"message_type"` // 消息类型可能的枚举值如下用Go语法注释:
/*
USER_REPLY // 用户回复
MERCHANT_REPLY // 商户回复
SYSTEM_NOTIFY // 系统通知
*/
ReplyContent string `json:"reply_content"` // 回复内容
ReplyTime string `json:"reply_time"` // 回复时间格式YYYY-MM-DD HH:mm:ss
}
func (l *AlipayFromLogic) AlipayFrom(w http.ResponseWriter, r *http.Request) error {
// 1. 解析表单
err := r.ParseForm()
if err != nil {
logx.Errorf("支付宝from消息回调解析表单失败: %v", err)
// 保存失败记录
l.saveCallbackRecord(r, "", "", "failed", "解析表单失败: "+err.Error())
return nil
}
// 获取基本信息
appID := r.Form.Get("app_id")
msgMethod := r.Form.Get("msg_method")
// 2. 验签
client := l.svcCtx.AlipayService.AlipayClient
if err := client.VerifySign(r.Form); err != nil {
logx.Errorf("支付宝from消息回调验签失败: %v", err)
// 保存失败记录
l.saveCallbackRecord(r, appID, msgMethod, "failed", "验签失败: "+err.Error())
return nil
}
// 3. 验证 app_id
if appID == "" {
logx.Errorf("支付宝from消息回调app_id为空")
// 保存失败记录
l.saveCallbackRecord(r, appID, msgMethod, "failed", "app_id为空")
return nil
}
if appID != l.svcCtx.Config.Alipay.AppID {
logx.Errorf("支付宝from消息回调app_id不匹配期望: %s, 实际: %s", l.svcCtx.Config.Alipay.AppID, appID)
// 保存失败记录
l.saveCallbackRecord(r, appID, msgMethod, "failed", "app_id不匹配")
return nil
}
// 4. 获取 msg_method 判断业务类型
if msgMethod == "" {
logx.Errorf("支付宝from消息回调msg_method为空")
// 保存失败记录
l.saveCallbackRecord(r, appID, msgMethod, "failed", "msg_method为空")
return nil
}
// 5. 先保存回调记录pending状态
callbackID := l.saveCallbackRecord(r, appID, msgMethod, "pending", "")
if callbackID == "" {
logx.Errorf("支付宝from消息回调保存回调记录失败")
// 即使保存失败也继续处理,避免影响业务
}
// 6. 根据 msg_method 路由到对应的处理函数
var handleErr error
switch msgMethod {
case "alipay.merchant.tradecomplain.changed":
// 交易投诉通知回调
handleErr = l.handleTradeComplainChanged(w, r, callbackID)
case "alipay.security.risk.complaints.merchants.notify":
// 安全风险投诉商户通知回调
handleErr = l.handleSecurityRiskComplaintsNotify(w, r, callbackID)
default:
logx.Infof("支付宝from消息回调未处理的msg_method: %s", msgMethod)
// 更新为已处理(未处理的也标记为已处理)
if callbackID != "" {
l.updateCallbackStatus(callbackID, "processed", "")
}
alipay.ACKNotification(w)
return nil
}
// 7. 根据处理结果更新状态
if callbackID != "" {
if handleErr != nil {
l.updateCallbackStatus(callbackID, "failed", handleErr.Error())
} else {
l.updateCallbackStatus(callbackID, "processed", "")
}
}
return handleErr
}
// saveCallbackRecord 保存回调记录到数据库
func (l *AlipayFromLogic) saveCallbackRecord(r *http.Request, appID, msgMethod, status, errorMsg string) string {
callback := &model.AlipayFromCallback{
Id: uuid.NewString(),
MsgMethod: msgMethod,
AppId: appID,
NotifyId: lzUtils.StringToNullString(r.Form.Get("notify_id")),
BizContent: r.Form.Get("biz_content"),
Status: status,
ErrorMessage: lzUtils.StringToNullString(errorMsg),
}
_, err := l.svcCtx.AlipayFromCallbackModel.Insert(l.ctx, nil, callback)
if err != nil {
logx.Errorf("保存支付宝from回调记录失败: %v", err)
return ""
}
return callback.Id
}
// updateCallbackStatus 更新回调记录状态
func (l *AlipayFromLogic) updateCallbackStatus(callbackID, status, errorMsg string) {
callback, err := l.svcCtx.AlipayFromCallbackModel.FindOne(l.ctx, callbackID)
if err != nil {
logx.Errorf("查找支付宝from回调记录失败: %v", err)
return
}
callback.Status = status
if errorMsg != "" {
callback.ErrorMessage = lzUtils.StringToNullString(errorMsg)
}
err = l.svcCtx.AlipayFromCallbackModel.UpdateWithVersion(l.ctx, nil, callback)
if err != nil {
logx.Errorf("更新支付宝from回调记录状态失败: %v", err)
}
}
// handleTradeComplainChanged 处理交易投诉通知回调
func (l *AlipayFromLogic) handleTradeComplainChanged(w http.ResponseWriter, r *http.Request, callbackID string) error {
// 获取 biz_content
bizContent := r.Form.Get("biz_content")
if bizContent == "" {
logx.Errorf("支付宝交易投诉通知回调biz_content为空")
return nil
}
// 解析 biz_content JSON
var content TradeComplainContent
if err := json.Unmarshal([]byte(bizContent), &content); err != nil {
logx.Errorf("支付宝交易投诉通知回调解析biz_content失败: %v, content: %s", err, bizContent)
return nil
}
// 记录日志
logx.Infof("支付宝交易投诉通知回调投诉事件ID: %s, 订单ID: %s, 状态: %s, 业务类型: %s",
content.ComplainEventID, content.OrderID, content.Status, content.BizType)
// TODO: 后续在这里添加具体的业务处理逻辑
// 例如根据订单ID查找订单更新订单状态发送通知等
alipay.ACKNotification(w)
return nil
}
// handleSecurityRiskComplaintsNotify 处理安全风险投诉商户通知回调
func (l *AlipayFromLogic) handleSecurityRiskComplaintsNotify(w http.ResponseWriter, r *http.Request, callbackID string) error {
// 获取 biz_content
bizContent := r.Form.Get("biz_content")
if bizContent == "" {
logx.Errorf("支付宝安全风险投诉商户通知回调biz_content为空")
return nil
}
// 解析 biz_content JSON
var content SecurityRiskComplaintsNotifyContent
if err := json.Unmarshal([]byte(bizContent), &content); err != nil {
logx.Errorf("支付宝安全风险投诉商户通知回调解析biz_content失败: %v, content: %s", err, bizContent)
return nil
}
// 记录日志
logx.Infof("支付宝安全风险投诉商户通知回调投诉ID: %s, 消息类型: %s, 回复内容: %s, 回复时间: %s",
content.ComplaintID, content.MessageType, content.ReplyContent, content.ReplyTime)
// 根据投诉ID查询详情并更新投诉记录
if err := l.svcCtx.AlipayComplaintService.QueryComplaintByTaskId(l.ctx, content.ComplaintID); err != nil {
logx.Errorf("查询并更新投诉记录失败, complaint_id: %s, error: %v", content.ComplaintID, err)
// 即使失败也返回成功,避免支付宝重复通知
}
alipay.ACKNotification(w)
return nil
}

View File

@@ -38,7 +38,19 @@ func (l *PaymentCheckLogic) PaymentCheck(req *types.PaymentCheckReq) (resp *type
Status: order.Status,
}, nil
}
if strings.HasPrefix(req.OrderNo, "W_") {
// 白名单订单
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单订单失败: %v", err)
}
return &types.PaymentCheckResp{
Type: "whitelist",
Status: order.Status,
}, nil
}
// 查询订单(包括代理订单)
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, req.OrderNo)
if err != nil {

View File

@@ -72,6 +72,11 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
if err != nil {
return err
}
case "whitelist":
paymentTypeResp, err = l.WhitelistOrderPayment(req, session)
if err != nil {
return err
}
}
// 开发环境测试支付模式:跳过实际支付流程
@@ -182,6 +187,37 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
} else {
logx.Infof("开发测试模式,代理升级成功,订单号: %s, 代理ID: %s", paymentTypeResp.outTradeNo, upgradeRecord.AgentId)
}
} else if strings.HasPrefix(paymentTypeResp.outTradeNo, "W_") {
// 白名单订单:更新白名单订单状态并处理白名单记录
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(context.Background(), paymentTypeResp.outTradeNo)
if findWhitelistErr != nil {
logx.Errorf("开发测试模式,查找白名单订单失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, findWhitelistErr)
return
}
// 更新白名单订单状态为已支付并处理白名单记录
err := l.svcCtx.WhitelistOrderModel.Trans(context.Background(), func(transCtx context.Context, session sqlx.Session) error {
// 更新白名单订单状态为已支付
whitelistOrder.Status = 2 // 已支付
now := time.Now()
whitelistOrder.PayTime = sql.NullTime{Time: now, Valid: true}
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(transCtx, session, whitelistOrder); updateErr != nil {
return errors.Wrapf(updateErr, "更新白名单订单状态失败")
}
// 调用 WhitelistService 处理白名单记录
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(transCtx, session, order, whitelistOrder); processErr != nil {
return errors.Wrapf(processErr, "处理白名单订单失败")
}
return nil
})
if err != nil {
logx.Errorf("开发测试模式,处理白名单订单失败,订单号: %s, 错误: %+v", paymentTypeResp.outTradeNo, err)
} else {
logx.Infof("开发测试模式,白名单订单支付成功,订单号: %s", paymentTypeResp.outTradeNo)
}
} else {
// 查询订单:发送支付成功通知任务,触发后续流程(生成报告和代理处理)
if sendErr := l.svcCtx.AsynqService.SendQueryTask(finalOrderID); sendErr != nil {
@@ -202,6 +238,181 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
}
}
// WhitelistOrderPayment 白名单下架支付订单
// PaymentReq.Id 支持两种格式:
// 1. "{idCard}|{featureApiId}" - 单个模块下架(创建新订单)
// 2. 订单号(以 W_ 开头) - 批量订单支付(使用已存在的订单)
func (l *PaymentLogic) WhitelistOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成白名单订单, 获取用户信息失败, %v", err)
}
// 判断是订单号还是 "{idCard}|{featureApiId}" 格式
var whitelistOrder *model.WhitelistOrder
var orderNo string
var amount float64
var description string
if strings.HasPrefix(req.Id, "W_") {
// 格式2订单号使用已存在的批量订单
orderNo = req.Id
whitelistOrder, err = l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("白名单订单不存在"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询白名单订单失败: %v", err)
}
// 验证订单状态和用户
if whitelistOrder.Status != 1 {
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法支付"), "")
}
if whitelistOrder.UserId != userID {
return nil, errors.Wrapf(xerr.NewErrMsg("无权支付此订单"), "")
}
amount = whitelistOrder.TotalAmount
description = "模块屏蔽(批量)"
// 获取用户信息(用于内部用户测试金额)
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户信息失败: %v", err)
}
if user.Inside == 1 {
amount = 0.01
}
// 检查是否已存在支付订单Order表
existingOrder, findErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
if findErr == nil && existingOrder != nil {
// 已存在支付订单,直接使用
return &PaymentTypeResp{
amount: amount,
outTradeNo: orderNo,
description: description,
orderID: existingOrder.Id,
}, nil
}
// 创建主订单记录(用于支付系统)
order := model.Order{
Id: uuid.NewString(),
OrderNo: orderNo,
UserId: userID,
ProductId: "", // 白名单订单没有具体产品ID
PaymentPlatform: req.PayMethod,
PaymentScene: "app", // 白名单订单,使用简短标识
Amount: amount,
Status: "pending",
}
if _, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order); insertOrderErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建支付订单失败: %+v", insertOrderErr)
}
return &PaymentTypeResp{
amount: amount,
outTradeNo: orderNo,
description: description,
orderID: order.Id,
}, nil
}
// 格式1"{idCard}|{featureApiId}" - 单个模块下架
parts := strings.SplitN(req.Id, "|", 2)
if len(parts) != 2 {
return nil, errors.Wrapf(xerr.NewErrMsg("参数错误id 格式不正确应为订单号W_开头或 {idCard}|{featureApiId}"), "")
}
idCard := parts[0]
featureApiId := parts[1]
if idCard == "" || featureApiId == "" {
return nil, errors.Wrapf(xerr.NewErrMsg("参数错误:身份证号或模块标识为空"), "")
}
// 查询 feature 信息
feature, err := l.svcCtx.FeatureModel.FindOneByApiId(l.ctx, featureApiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrapf(xerr.NewErrMsg("模块不存在"), "")
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 查询模块失败: %v", err)
}
amount = feature.WhitelistPrice
if amount <= 0 {
return nil, errors.Wrapf(xerr.NewErrMsg("该模块无需付费下架"), "")
}
// 获取用户信息(用于内部用户测试金额)
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 获取用户信息失败: %v", err)
}
if user.Inside == 1 {
amount = 0.01
}
// 生成订单号(白名单订单前缀 W_限制长度不超过32
base := l.svcCtx.AlipayService.GenerateOutTradeNo()
outTradeNo := "W_" + base
if len(outTradeNo) > 32 {
outTradeNo = outTradeNo[:32]
}
// 创建主订单记录(用于支付系统)
order := model.Order{
Id: uuid.NewString(),
OrderNo: outTradeNo,
UserId: userID,
ProductId: "", // 白名单订单没有具体产品ID
PaymentPlatform: req.PayMethod,
PaymentScene: "app", // 白名单订单,使用简短标识
Amount: amount,
Status: "pending",
}
if _, insertOrderErr := l.svcCtx.OrderModel.Insert(l.ctx, session, &order); insertOrderErr != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存订单失败: %+v", insertOrderErr)
}
// 创建白名单订单(业务订单,状态=待支付)
whitelistOrder = &model.WhitelistOrder{
Id: uuid.NewString(),
OrderNo: outTradeNo,
UserId: userID,
IdCard: idCard,
TotalAmount: amount,
Status: 1, // 待支付
}
if _, err := l.svcCtx.WhitelistOrderModel.Insert(l.ctx, session, whitelistOrder); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存白名单订单失败: %+v", err)
}
// 创建白名单订单明细
orderItem := &model.WhitelistOrderItem{
Id: uuid.NewString(),
OrderId: whitelistOrder.Id,
FeatureId: feature.Id,
FeatureApiId: feature.ApiId,
FeatureName: feature.Name,
Price: amount,
}
if _, err := l.svcCtx.WhitelistOrderItemModel.Insert(l.ctx, session, orderItem); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成白名单订单, 保存白名单订单明细失败: %+v", err)
}
description = fmt.Sprintf("模块下架:%s", feature.Name)
return &PaymentTypeResp{
amount: amount,
outTradeNo: outTradeNo,
description: description,
orderID: order.Id,
}, nil
}
func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
if getUidErr != nil {

View File

@@ -45,6 +45,9 @@ func (l *WechatPayCallbackLogic) WechatPayCallback(w http.ResponseWriter, r *htt
} else if strings.HasPrefix(orderNo, "U_") {
// 代理升级订单处理
return l.handleAgentUpgradeOrderPayment(w, notification)
} else if strings.HasPrefix(orderNo, "W_") {
// 白名单下架订单处理
return l.handleWhitelistOrderPayment(w, notification)
} else if strings.HasPrefix(orderNo, "A_") {
// 旧系统会员充值订单(已废弃,新系统使用升级功能)
// return l.handleAgentVipOrderPayment(w, notification)
@@ -109,6 +112,91 @@ func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter,
return nil
}
// 处理白名单下架订单支付
func (l *WechatPayCallbackLogic) handleWhitelistOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
orderNo := *notification.OutTradeNo
// 1. 查找订单
order, findOrderErr := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
if findOrderErr != nil {
logx.Errorf("微信白名单支付回调,查找订单信息失败: %+v", findOrderErr)
return nil
}
// 2. 验证金额
amount := lzUtils.ToWechatAmount(order.Amount)
if amount != *notification.Amount.Total {
logx.Errorf("微信白名单支付回调,金额不一致")
return nil
}
// 3. 检查订单状态
if order.Status != "pending" {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("success"))
return nil
}
// 4. 查找白名单订单
whitelistOrder, findWhitelistErr := l.svcCtx.WhitelistOrderModel.FindOneByOrderNo(l.ctx, orderNo)
if findWhitelistErr != nil {
logx.Errorf("微信白名单支付回调,查找白名单订单失败,订单号: %s, 错误: %+v", orderNo, findWhitelistErr)
return nil
}
if whitelistOrder.Status != 1 {
// 白名单订单状态不是待支付,直接返回成功
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("success"))
return nil
}
// 5. 处理支付状态
switch *notification.TradeState {
case service.TradeStateSuccess:
order.Status = "paid"
order.PayTime = lzUtils.TimeToNullTime(time.Now())
order.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
whitelistOrder.Status = 2 // 已支付
whitelistOrder.PayTime = lzUtils.TimeToNullTime(time.Now())
case service.TradeStateClosed:
order.Status = "closed"
order.CloseTime = lzUtils.TimeToNullTime(time.Now())
whitelistOrder.Status = 3 // 已取消
default:
return nil
}
// 6. 更新订单和白名单订单 + 创建白名单记录并删除报告数据
err := l.svcCtx.WhitelistOrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 6.1 更新订单状态
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); updateErr != nil {
return errors.Wrapf(updateErr, "微信白名单支付回调,更新订单状态失败")
}
// 6.2 更新白名单订单状态
if updateErr := l.svcCtx.WhitelistOrderModel.UpdateWithVersion(ctx, session, whitelistOrder); updateErr != nil {
return errors.Wrapf(updateErr, "微信白名单支付回调,更新白名单订单状态失败")
}
// 6.3 如果支付成功,调用 WhitelistService 处理白名单和报告数据
if whitelistOrder.Status == 2 {
if processErr := l.svcCtx.WhitelistService.ProcessPaidWhitelistOrder(ctx, session, order, whitelistOrder); processErr != nil {
return errors.Wrapf(processErr, "微信白名单支付回调,处理白名单订单失败")
}
}
return nil
})
if err != nil {
logx.Errorf("微信白名单支付回调,事务处理失败: %+v", err)
return nil
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("success"))
return nil
}
// 处理代理升级订单支付
func (l *WechatPayCallbackLogic) handleAgentUpgradeOrderPayment(w http.ResponseWriter, notification *payments.Transaction) error {
orderNo := *notification.OutTradeNo

View File

@@ -114,6 +114,21 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
return errors.Wrapf(err, "更新订单和退款记录失败: %s", orderNo)
}
// 退款成功后,检查并处理代理订单(在事务外执行,避免影响退款流程)
if status == refunddomestic.STATUS_SUCCESS {
// 检查代理订单是否已处理
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(l.ctx, order.Id)
if err == nil && agentOrder != nil && agentOrder.ProcessStatus == 1 {
// 代理订单已处理,需要撤销收益
if cancelErr := l.svcCtx.AgentService.CancelAgentCommission(l.ctx, order.Id); cancelErr != nil {
logx.Errorf("撤销代理收益失败订单ID: %s, 错误: %v", order.Id, cancelErr)
// 不阻断退款流程,只记录日志(退款已成功,不能回滚)
} else {
logx.Infof("成功撤销代理收益订单ID: %s", order.Id)
}
}
}
return nil
}

View File

@@ -8,7 +8,6 @@ import (
"time"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/model"
"ycc-server/common/globalkey"
"github.com/google/uuid"
"github.com/hibiken/asynq"
@@ -99,7 +98,6 @@ func (l *CleanQueryDataHandler) ProcessTask(ctx context.Context, t *asynq.Task)
// 先快速检查是否有数据需要清理(避免创建无用的日志记录)
checkBuilder := l.svcCtx.QueryModel.SelectBuilder().
Where("create_time < ?", cleanupBefore).
Where("del_state = ?", globalkey.DelStateNo).
Limit(1) // 只查询1条用于判断是否有数据
checkQueries, checkErr := l.svcCtx.QueryModel.FindAll(taskCtx, checkBuilder, "")
@@ -160,7 +158,6 @@ func (l *CleanQueryDataHandler) ProcessTask(ctx context.Context, t *asynq.Task)
// 1. 查询一批要删除的记录(添加排序确保一致性)
builder := l.svcCtx.QueryModel.SelectBuilder().
Where("create_time < ?", cleanupBefore).
Where("del_state = ?", globalkey.DelStateNo).
OrderBy("id ASC"). // 添加排序,确保处理顺序一致
Limit(uint64(batchSize))

View File

@@ -240,6 +240,15 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
logx.Errorf("更新订单状态失败订单ID: %s, 错误: %v", order.Id, updateOrderErr)
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
}
// 检查并处理代理订单(虽然查询失败时代理订单通常未处理,但为了完整性)
agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
if err == nil && agentOrder != nil && agentOrder.ProcessStatus == 1 {
if cancelErr := l.svcCtx.AgentService.CancelAgentCommission(ctx, order.Id); cancelErr != nil {
logx.Errorf("撤销代理收益失败订单ID: %s, 错误: %v", order.Id, cancelErr)
} else {
logx.Infof("成功撤销代理收益订单ID: %s", order.Id)
}
}
return asynq.SkipRetry
} else {
logx.Errorf("支付宝退款失败:%v", refundErr)

View File

@@ -39,6 +39,13 @@ func (l *CronJob) Register() *asynq.ServeMux {
panic(fmt.Sprintf("解冻佣金扫描定时任务注册失败:%v", err))
}
// 注册支付宝投诉同步定时任务每5分钟执行一次
syncComplaintTask := asynq.NewTask(types.MsgSyncAlipayComplaint, nil, nil)
_, err = scheduler.Register("*/5 * * * *", syncComplaintTask) // 每5分钟执行一次
if err != nil {
panic(fmt.Sprintf("支付宝投诉同步定时任务注册失败:%v", err))
}
scheduler.Start()
fmt.Println("定时任务启动!!!")
@@ -48,6 +55,7 @@ func (l *CronJob) Register() *asynq.ServeMux {
mux.Handle(types.MsgAgentProcess, NewAgentProcessHandler(l.svcCtx))
mux.Handle(types.MsgUnfreezeCommission, NewUnfreezeCommissionHandler(l.svcCtx))
mux.Handle(types.MsgUnfreezeCommissionScan, NewUnfreezeCommissionScanHandler(l.svcCtx))
mux.Handle(types.MsgSyncAlipayComplaint, NewSyncAlipayComplaintHandler(l.svcCtx))
return mux
}

View File

@@ -0,0 +1,130 @@
package queue
import (
"context"
"os"
"time"
"ycc-server/app/main/api/internal/svc"
"github.com/hibiken/asynq"
"github.com/pkg/errors"
"github.com/smartwalle/alipay/v3"
"github.com/zeromicro/go-zero/core/logx"
)
// SyncAlipayComplaintHandler 支付宝投诉同步任务处理器
type SyncAlipayComplaintHandler struct {
svcCtx *svc.ServiceContext
}
func NewSyncAlipayComplaintHandler(svcCtx *svc.ServiceContext) *SyncAlipayComplaintHandler {
return &SyncAlipayComplaintHandler{
svcCtx: svcCtx,
}
}
// ProcessTask 处理投诉同步任务
func (l *SyncAlipayComplaintHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
if os.Getenv("ENV") == "development" {
return nil
}
startTime := time.Now()
logx.Infof("开始同步支付宝投诉数据,当前时间: %v", startTime)
// 1. 查询数据库中最新投诉的投诉时间
latestComplainTime, err := l.svcCtx.AlipayComplaintService.GetLatestComplainTime(ctx)
if err != nil {
logx.Errorf("查询最新投诉时间失败: %v", err)
return errors.Wrapf(err, "查询最新投诉时间失败")
}
// 2. 如果数据库为空查询最近7天的投诉否则查询最新投诉时间之后的投诉
var gmtComplainStart string
if latestComplainTime.IsZero() {
// 数据库为空查询最近7天
sevenDaysAgo := time.Now().AddDate(0, 0, -7)
gmtComplainStart = sevenDaysAgo.Format("2006-01-02 15:04:05")
logx.Infof("数据库为空查询最近7天的投诉起始时间: %s", gmtComplainStart)
} else {
// 查询最新投诉时间之后的投诉往前推1分钟避免边界问题
gmtComplainStart = latestComplainTime.Add(-1 * time.Minute).Format("2006-01-02 15:04:05")
logx.Infof("查询最新投诉时间之后的投诉,起始时间: %s (最新投诉时间: %v)", gmtComplainStart, latestComplainTime)
}
// 3. 分页查询投诉列表
pageNum := int64(1)
pageSize := int64(10)
totalSynced := 0
for {
// 调用支付宝接口查询投诉列表
req := alipay.SecurityRiskComplaintInfoBatchQueryReq{
CurrentPageNum: pageNum,
PageSize: pageSize,
GmtComplaintStart: gmtComplainStart,
GmtComplaintEnd: time.Now().Format("2006-01-02 15:04:05"),
}
resp, err := l.svcCtx.AlipayService.AlipayClient.SecurityRiskComplaintInfoBatchQuery(ctx, req)
if err != nil {
logx.Errorf("查询支付宝投诉列表失败: %v", err)
return errors.Wrapf(err, "查询支付宝投诉列表失败")
}
// 检查响应是否成功
if !resp.IsSuccess() {
logx.Errorf("支付宝返回错误: code=%s, msg=%s", resp.Code, resp.Msg)
return errors.Errorf("支付宝返回错误: %s-%s", resp.Code, resp.Msg)
}
// 如果没有数据,退出循环
if len(resp.ComplaintList) == 0 {
logx.Infof("第 %d 页没有数据,同步完成", pageNum)
break
}
// 4. 保存投诉数据到数据库
synced, err := l.saveComplaints(ctx, resp.ComplaintList)
if err != nil {
logx.Errorf("保存投诉数据失败: %v", err)
return errors.Wrapf(err, "保存投诉数据失败")
}
totalSynced += synced
logx.Infof("第 %d 页同步完成,本页同步 %d 条,累计同步 %d 条", pageNum, synced, totalSynced)
// 5. 检查是否还有下一页
if pageNum >= resp.CurrentPage {
// 当前页已经是最后一页
if len(resp.ComplaintList) < int(pageSize) {
// 当前页数据不足一页,说明没有更多数据了
break
}
}
// 继续查询下一页
pageNum++
}
duration := time.Since(startTime)
logx.Infof("支付宝投诉数据同步完成,共同步 %d 条投诉,耗时: %v", totalSynced, duration)
return nil
}
// saveComplaints 保存投诉列表到数据库
func (l *SyncAlipayComplaintHandler) saveComplaints(ctx context.Context, complaintList []alipay.SecurityRiskComplaintInfo) (int, error) {
synced := 0
for _, complaint := range complaintList {
// 使用 service 保存投诉数据
if err := l.svcCtx.AlipayComplaintService.SaveComplaint(ctx, &complaint); err != nil {
logx.Errorf("保存投诉失败, task_id: %s, error: %v", complaint.TaskId, err)
continue // 继续处理下一条
}
synced++
}
return synced, nil
}

View File

@@ -630,18 +630,18 @@ func (s *AgentService) getRebateConfigFloat(ctx context.Context, configKey strin
// giveRebate 发放返佣
func (s *AgentService) giveRebate(ctx context.Context, session sqlx.Session, agentId, sourceAgentId, orderId, productId string, amount float64, levelBonus int64, rebateType int64) error {
// 1. 创建返佣记录
rebate := &model.AgentRebate{
Id: uuid.NewString(),
AgentId: agentId,
SourceAgentId: sourceAgentId,
OrderId: orderId,
ProductId: productId,
RebateType: rebateType,
LevelBonus: float64(levelBonus), // 等级加成金额
RebateAmount: amount,
Status: 1, // 已发放
}
// 1. 创建返佣记录
rebate := &model.AgentRebate{
Id: uuid.NewString(),
AgentId: agentId,
SourceAgentId: sourceAgentId,
OrderId: orderId,
ProductId: productId,
RebateType: rebateType,
LevelBonus: float64(levelBonus), // 等级加成金额
RebateAmount: amount,
Status: 1, // 已发放
}
if _, err := s.AgentRebateModel.Insert(ctx, session, rebate); err != nil {
return errors.Wrapf(err, "创建返佣记录失败")
}
@@ -971,12 +971,12 @@ func (s *AgentService) reconnectToGrandparent(ctx context.Context, session sqlx.
}
// 创建新的关系连接到原上级的上级
relation := &model.AgentRelation{
Id: uuid.NewString(),
ParentId: grandparent.Id,
ChildId: agentId,
RelationType: 1, // 直接关系
}
relation := &model.AgentRelation{
Id: uuid.NewString(),
ParentId: grandparent.Id,
ChildId: agentId,
RelationType: 1, // 直接关系
}
if _, err := s.AgentRelationModel.Insert(ctx, session, relation); err != nil {
return errors.Wrapf(err, "创建新关系失败")
}
@@ -1073,3 +1073,201 @@ func (s *AgentService) GetUpgradeRebate(ctx context.Context, fromLevel, toLevel
}
return 0, nil
}
// CancelAgentCommission 撤销代理佣金和返佣(订单退款时调用)
// 功能:收回已发放的代理佣金和返佣,处理冻结任务
// 余额不足时允许余额为负数(欠款),通过提现限制控制
func (s *AgentService) CancelAgentCommission(ctx context.Context, orderId string) error {
// 1. 查找代理订单
agentOrder, err := s.AgentOrderModel.FindOneByOrderId(ctx, orderId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 不是代理订单,直接返回
return nil
}
return errors.Wrapf(err, "查询代理订单失败, orderId: %s", orderId)
}
// 2. 检查是否已处理(只有已处理的订单才需要收回)
if agentOrder.ProcessStatus != 1 {
logx.Infof("代理订单未处理,无需撤销佣金, orderId: %s, processStatus: %d", orderId, agentOrder.ProcessStatus)
return nil
}
// 3. 查找所有佣金记录
builder := s.AgentCommissionModel.SelectBuilder().
Where("order_id = ? AND del_state = ?", orderId, globalkey.DelStateNo)
commissions, err := s.AgentCommissionModel.FindAll(ctx, builder, "")
if err != nil {
return errors.Wrapf(err, "查询佣金记录失败, orderId: %s", orderId)
}
// 4. 查找所有返佣记录
rebateBuilder := s.AgentRebateModel.SelectBuilder().
Where("order_id = ? AND del_state = ?", orderId, globalkey.DelStateNo)
rebates, err := s.AgentRebateModel.FindAll(ctx, rebateBuilder, "")
if err != nil {
return errors.Wrapf(err, "查询返佣记录失败, orderId: %s", orderId)
}
// 5. 查找相关冻结任务
freezeBuilder := s.AgentFreezeTaskModel.SelectBuilder().
Where("order_id = ? AND del_state = ?", orderId, globalkey.DelStateNo)
freezeTasks, err := s.AgentFreezeTaskModel.FindAll(ctx, freezeBuilder, "")
if err != nil {
return errors.Wrapf(err, "查询冻结任务失败, orderId: %s", orderId)
}
// 6. 使用事务处理收回逻辑
return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
// 6.1 收回代理佣金
for _, commission := range commissions {
// 只处理已发放的佣金Status=1
if commission.Status != 1 {
logx.Infof("佣金记录状态不是已发放,跳过, commissionId: %s, status: %d", commission.Id, commission.Status)
continue
}
// 查找冻结任务(如果有)
var freezeTask *model.AgentFreezeTask
for _, task := range freezeTasks {
if task.CommissionId == commission.Id && task.Status == 1 {
freezeTask = task
break
}
}
// 扣除代理钱包余额
if err := s.deductCommissionFromWallet(transCtx, session, commission.AgentId, commission.Amount, freezeTask); err != nil {
return errors.Wrapf(err, "扣除代理佣金失败, agentId: %s, amount: %.2f", commission.AgentId, commission.Amount)
}
// 更新佣金记录状态为已撤销
commission.Status = 3 // 3=已取消
if err := s.AgentCommissionModel.UpdateWithVersion(transCtx, session, commission); err != nil {
return errors.Wrapf(err, "更新佣金记录状态失败, commissionId: %s", commission.Id)
}
// 如果有冻结任务,更新为已撤销
if freezeTask != nil {
freezeTask.Status = 3 // 3=已取消
if err := s.AgentFreezeTaskModel.UpdateWithVersion(transCtx, session, freezeTask); err != nil {
return errors.Wrapf(err, "更新冻结任务状态失败, freezeTaskId: %s", freezeTask.Id)
}
}
}
// 6.2 收回返佣
for _, rebate := range rebates {
// 只处理已发放的返佣Status=1
if rebate.Status != 1 {
logx.Infof("返佣记录状态不是已发放,跳过, rebateId: %s, status: %d", rebate.Id, rebate.Status)
continue
}
// 扣除上级代理钱包余额
if err := s.deductRebateFromWallet(transCtx, session, rebate.AgentId, rebate.RebateAmount); err != nil {
return errors.Wrapf(err, "扣除返佣失败, agentId: %s, amount: %.2f", rebate.AgentId, rebate.RebateAmount)
}
// 更新返佣记录状态为已撤销
rebate.Status = 3 // 3=已取消
if err := s.AgentRebateModel.UpdateWithVersion(transCtx, session, rebate); err != nil {
return errors.Wrapf(err, "更新返佣记录状态失败, rebateId: %s", rebate.Id)
}
}
// 6.3 更新代理订单备注(可选,记录撤销信息)
agentOrder.ProcessRemark = sql.NullString{
String: "订单退款,佣金和返佣已撤销",
Valid: true,
}
if err := s.AgentOrderModel.UpdateWithVersion(transCtx, session, agentOrder); err != nil {
logx.Errorf("更新代理订单备注失败, orderId: %s, err: %v", orderId, err)
// 不阻断流程,只记录日志
}
return nil
})
}
// deductCommissionFromWallet 从钱包扣除佣金(支持余额不足,允许负数)
func (s *AgentService) deductCommissionFromWallet(ctx context.Context, session sqlx.Session, agentId string, amount float64, freezeTask *model.AgentFreezeTask) error {
wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, agentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败, agentId: %s", agentId)
}
// 计算可用余额
availableBalance := wallet.Balance + wallet.FrozenBalance
// 如果有冻结任务,需要先解冻
if freezeTask != nil && freezeTask.Status == 1 {
freezeAmount := freezeTask.FreezeAmount
// 解冻:从冻结余额转回可用余额
if wallet.FrozenBalance >= freezeAmount {
wallet.FrozenBalance -= freezeAmount
wallet.Balance += freezeAmount
} else {
// 冻结余额不足(异常情况),记录日志
logx.Errorf("冻结余额不足,异常情况, agentId: %s, frozenBalance: %.2f, freezeAmount: %.2f", agentId, wallet.FrozenBalance, freezeAmount)
wallet.Balance += wallet.FrozenBalance
wallet.FrozenBalance = 0
}
}
// 扣除佣金金额
if availableBalance >= amount {
// 余额充足:优先扣除冻结余额,再扣除可用余额
if wallet.FrozenBalance >= amount {
wallet.FrozenBalance -= amount
} else {
remaining := amount - wallet.FrozenBalance
wallet.FrozenBalance = 0
wallet.Balance -= remaining
}
} else {
// 余额不足:扣除所有可用余额,允许负数
wallet.Balance = wallet.Balance - (amount - wallet.FrozenBalance)
wallet.FrozenBalance = 0
// 记录日志:欠款金额
logx.Errorf("代理余额不足,产生欠款: agentId=%s, orderId=%s, debt=%.2f, balance=%.2f",
agentId, "", -wallet.Balance, wallet.Balance)
}
// TotalEarnings 始终减少(即使余额为负数)
wallet.TotalEarnings -= amount
if wallet.TotalEarnings < 0 {
wallet.TotalEarnings = 0 // 累计收益不能为负数
}
return s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
}
// deductRebateFromWallet 从钱包扣除返佣(支持余额不足,允许负数)
func (s *AgentService) deductRebateFromWallet(ctx context.Context, session sqlx.Session, agentId string, amount float64) error {
wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, agentId)
if err != nil {
return errors.Wrapf(err, "查询钱包失败, agentId: %s", agentId)
}
// 扣除返佣金额
if wallet.Balance >= amount {
// 余额充足:正常扣除
wallet.Balance -= amount
} else {
// 余额不足:扣除所有余额,允许负数
wallet.Balance -= amount
// 记录日志:欠款金额
logx.Errorf("代理返佣余额不足,产生欠款: agentId=%s, debt=%.2f, balance=%.2f",
agentId, -wallet.Balance, wallet.Balance)
}
// TotalEarnings 始终减少(即使余额为负数)
wallet.TotalEarnings -= amount
if wallet.TotalEarnings < 0 {
wallet.TotalEarnings = 0 // 累计收益不能为负数
}
return s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
}

View File

@@ -0,0 +1,541 @@
package service
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"ycc-server/app/main/api/internal/config"
"ycc-server/app/main/model"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/smartwalle/alipay/v3"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
// AlipayComplaintService 支付宝投诉服务,集中处理投诉相关的业务逻辑
type AlipayComplaintService struct {
config config.Config
AlipayClient *alipay.Client
ComplaintMainModel model.ComplaintMainModel
ComplaintAlipayModel model.ComplaintAlipayModel
ComplaintAlipayTradeModel model.ComplaintAlipayTradeModel
OrderModel model.OrderModel // 用于订单关联
}
// NewAlipayComplaintService 创建支付宝投诉服务
func NewAlipayComplaintService(
c config.Config,
alipayClient *alipay.Client,
complaintMainModel model.ComplaintMainModel,
complaintAlipayModel model.ComplaintAlipayModel,
complaintAlipayTradeModel model.ComplaintAlipayTradeModel,
orderModel model.OrderModel,
) *AlipayComplaintService {
return &AlipayComplaintService{
config: c,
AlipayClient: alipayClient,
ComplaintMainModel: complaintMainModel,
ComplaintAlipayModel: complaintAlipayModel,
ComplaintAlipayTradeModel: complaintAlipayTradeModel,
OrderModel: orderModel,
}
}
// QueryComplaintDetail 查询投诉详情
func (s *AlipayComplaintService) QueryComplaintDetail(ctx context.Context, complainId int64) (*alipay.SecurityRiskComplaintInfo, error) {
req := alipay.SecurityRiskComplaintInfoQueryReq{
ComplainId: complainId,
}
resp, err := s.AlipayClient.SecurityRiskComplaintInfoQuery(ctx, req)
if err != nil {
return nil, errors.Wrapf(err, "查询投诉详情失败, complain_id: %d", complainId)
}
if !resp.IsSuccess() {
return nil, errors.Errorf("支付宝返回错误: %s-%s", resp.Code, resp.Msg)
}
return &resp.SecurityRiskComplaintInfo, nil
}
// QueryComplaintByTaskId 根据 task_id 查询投诉详情并更新记录
func (s *AlipayComplaintService) QueryComplaintByTaskId(ctx context.Context, taskId string) error {
// 1. 通过 task_id 查找数据库中的投诉记录
complaint, err := s.ComplaintAlipayModel.FindOneByTaskId(ctx, taskId)
if err != nil {
if err == model.ErrNotFound {
logx.Infof("投诉记录不存在, task_id: %s可能是新投诉将在定时任务中同步", taskId)
return nil // 新投诉会在定时任务中同步,这里不处理
}
return errors.Wrapf(err, "查找投诉记录失败, task_id: %s", taskId)
}
// 2. 使用 alipay_id 查询投诉详情
detail, err := s.QueryComplaintDetail(ctx, complaint.AlipayId)
if err != nil {
return errors.Wrapf(err, "查询投诉详情失败, task_id: %s, alipay_id: %d", taskId, complaint.AlipayId)
}
logx.Infof("查询投诉详情成功, task_id: %s, alipay_id: %d, status: %s", taskId, complaint.AlipayId, detail.Status)
// 3. 更新投诉记录
return s.UpdateComplaintFromDetail(ctx, complaint, detail)
}
// UpdateComplaintFromDetail 根据查询详情更新投诉记录
func (s *AlipayComplaintService) UpdateComplaintFromDetail(ctx context.Context, complaint *model.ComplaintAlipay, detail *alipay.SecurityRiskComplaintInfo) error {
return s.ComplaintAlipayModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
// 1. 更新或创建投诉主表记录
var complaintMain *model.ComplaintMain
var err error
if complaint.ComplaintId != "" {
// 查找主表记录
complaintMain, err = s.ComplaintMainModel.FindOne(transCtx, complaint.ComplaintId)
if err != nil {
return errors.Wrapf(err, "查找投诉主表失败, complaint_id: %s", complaint.ComplaintId)
}
// 更新主表信息
complaintMain.Name = lzUtils.StringToNullString(detail.OppositeName) // 使用被投诉方名称作为投诉人姓名
complaintMain.Contact = lzUtils.StringToNullString(detail.Contact)
complaintMain.Content = lzUtils.StringToNullString(detail.ComplainContent)
complaintMain.Status = lzUtils.StringToNullString(detail.Status)
complaintMain.StatusDescription = lzUtils.StringToNullString(detail.StatusDescription)
// 如果主表没有订单ID尝试关联订单
if !complaintMain.OrderId.Valid {
if detail.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, detail.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
}
}
// 尝试从交易信息中查找
if !complaintMain.OrderId.Valid && len(detail.ComplaintTradeInfoList) > 0 {
for _, tradeInfo := range detail.ComplaintTradeInfoList {
if tradeInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, tradeInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
break
}
}
if tradeInfo.OutNo != "" {
order, err := s.OrderModel.FindOneByOrderNo(transCtx, tradeInfo.OutNo)
if err == nil && order != nil {
complaintMain.OrderId = lzUtils.StringToNullString(order.Id)
break
}
}
}
}
}
if err := s.ComplaintMainModel.UpdateWithVersion(transCtx, session, complaintMain); err != nil {
return errors.Wrapf(err, "更新投诉主表失败, complaint_id: %s", complaint.ComplaintId)
}
} else {
// 创建新的主表记录
complaintMain = &model.ComplaintMain{
Id: uuid.NewString(),
Type: "alipay",
Name: lzUtils.StringToNullString(detail.OppositeName),
Contact: lzUtils.StringToNullString(detail.Contact),
Content: lzUtils.StringToNullString(detail.ComplainContent),
Status: lzUtils.StringToNullString(detail.Status),
StatusDescription: lzUtils.StringToNullString(detail.StatusDescription),
}
// 如果有交易单号,尝试关联订单
if detail.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, detail.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
}
}
// 如果主表没有订单ID尝试从交易信息中查找
if !complaintMain.OrderId.Valid && len(detail.ComplaintTradeInfoList) > 0 {
for _, tradeInfo := range detail.ComplaintTradeInfoList {
if tradeInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, tradeInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
break
}
}
// 也可以尝试通过 out_no商家订单号查找
if tradeInfo.OutNo != "" {
order, err := s.OrderModel.FindOneByOrderNo(transCtx, tradeInfo.OutNo)
if err == nil && order != nil {
complaintMain.OrderId = lzUtils.StringToNullString(order.Id)
break
}
}
}
}
if _, err := s.ComplaintMainModel.Insert(transCtx, session, complaintMain); err != nil {
return errors.Wrapf(err, "创建投诉主表失败, task_id: %s", complaint.TaskId)
}
complaint.ComplaintId = complaintMain.Id
}
// 2. 更新支付宝投诉表信息
complaint.OppositePid = lzUtils.StringToNullString(detail.OppositePid)
complaint.OppositeName = lzUtils.StringToNullString(detail.OppositeName)
if detail.ComplainAmount != "" {
amount, err := s.parseDecimal(detail.ComplainAmount)
if err == nil {
complaint.ComplainAmount = lzUtils.Float64ToNullFloat64(amount)
}
}
complaint.GmtComplain = s.parseTime(detail.GmtComplain)
complaint.GmtProcess = s.parseTime(detail.GmtProcess)
complaint.GmtOverdue = s.parseTime(detail.GmtOverdue)
complaint.ComplainContent = lzUtils.StringToNullString(detail.ComplainContent)
complaint.TradeNo = lzUtils.StringToNullString(detail.TradeNo)
complaint.Status = lzUtils.StringToNullString(detail.Status)
complaint.StatusDescription = lzUtils.StringToNullString(detail.StatusDescription)
complaint.ProcessCode = lzUtils.StringToNullString(detail.ProcessCode)
complaint.ProcessMessage = lzUtils.StringToNullString(detail.ProcessMessage)
complaint.ProcessRemark = lzUtils.StringToNullString(detail.ProcessRemark)
complaint.GmtRiskFinishTime = s.parseTime(detail.GmtRiskFinishTime)
complaint.ComplainUrl = lzUtils.StringToNullString(detail.ComplainUrl)
// 处理图片列表
if len(detail.ProcessImgUrlList) > 0 {
imgJson, _ := json.Marshal(detail.ProcessImgUrlList)
complaint.ProcessImgUrlList = lzUtils.StringToNullString(string(imgJson))
}
if len(detail.CertifyInfo) > 0 {
certifyJson, _ := json.Marshal(detail.CertifyInfo)
complaint.CertifyInfo = lzUtils.StringToNullString(string(certifyJson))
}
// 更新支付宝投诉表
if err := s.ComplaintAlipayModel.UpdateWithVersion(transCtx, session, complaint); err != nil {
return errors.Wrapf(err, "更新支付宝投诉表失败, task_id: %s", complaint.TaskId)
}
// 3. 更新交易信息:先删除旧的,再插入新的
oldTrades, _ := s.ComplaintAlipayTradeModel.FindAll(transCtx,
s.ComplaintAlipayTradeModel.SelectBuilder().
Where("complaint_id = ? AND del_state = ?", complaint.Id, 0), "")
for _, oldTrade := range oldTrades {
oldTrade.DelState = 1
oldTrade.DeleteTime = lzUtils.TimeToNullTime(time.Now())
s.ComplaintAlipayTradeModel.UpdateWithVersion(transCtx, session, oldTrade)
}
// 插入新的交易信息
for _, tradeInfo := range detail.ComplaintTradeInfoList {
trade := &model.ComplaintAlipayTrade{
Id: uuid.NewString(),
ComplaintId: complaint.Id,
AlipayTradeId: lzUtils.Int64ToNullInt64(tradeInfo.Id),
AlipayComplaintRecordId: lzUtils.Int64ToNullInt64(tradeInfo.ComplaintRecordId),
TradeNo: lzUtils.StringToNullString(tradeInfo.TradeNo),
OutNo: lzUtils.StringToNullString(tradeInfo.OutNo),
GmtTrade: s.parseTime(tradeInfo.GmtTrade),
GmtRefund: s.parseTime(tradeInfo.GmtRefund),
Status: lzUtils.StringToNullString(tradeInfo.Status),
StatusDescription: lzUtils.StringToNullString(tradeInfo.StatusDescription),
}
if tradeInfo.Amount != "" {
amount, err := s.parseDecimal(tradeInfo.Amount)
if err == nil {
trade.Amount = lzUtils.Float64ToNullFloat64(amount)
}
}
if _, err := s.ComplaintAlipayTradeModel.Insert(transCtx, session, trade); err != nil {
return errors.Wrapf(err, "插入投诉交易信息失败, task_id: %s", complaint.TaskId)
}
}
return nil
})
}
// SaveComplaint 保存投诉数据到数据库(用于定时任务同步)
func (s *AlipayComplaintService) SaveComplaint(ctx context.Context, complaintInfo *alipay.SecurityRiskComplaintInfo) error {
// 检查投诉是否已存在(通过 task_id
existing, err := s.ComplaintAlipayModel.FindOneByTaskId(ctx, complaintInfo.TaskId)
if err != nil && err != model.ErrNotFound {
return errors.Wrapf(err, "查询投诉失败, task_id: %s", complaintInfo.TaskId)
}
return s.ComplaintAlipayModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
var complaintRecord *model.ComplaintAlipay
var complaintMain *model.ComplaintMain
var isUpdate bool
if existing != nil {
// 更新现有投诉
complaintRecord = existing
isUpdate = true
// 查找主表记录
if complaintRecord.ComplaintId != "" {
complaintMain, err = s.ComplaintMainModel.FindOne(transCtx, complaintRecord.ComplaintId)
if err != nil {
return errors.Wrapf(err, "查找投诉主表失败, complaint_id: %s", complaintRecord.ComplaintId)
}
}
} else {
// 创建新投诉
complaintRecord = &model.ComplaintAlipay{
Id: uuid.NewString(),
}
isUpdate = false
}
// 1. 创建或更新投诉主表记录
if complaintMain == nil {
// 创建新的主表记录
complaintMain = &model.ComplaintMain{
Id: uuid.NewString(),
Type: "alipay",
Name: lzUtils.StringToNullString(complaintInfo.OppositeName),
Contact: lzUtils.StringToNullString(complaintInfo.Contact),
Content: lzUtils.StringToNullString(complaintInfo.ComplainContent),
Status: lzUtils.StringToNullString(complaintInfo.Status),
StatusDescription: lzUtils.StringToNullString(complaintInfo.StatusDescription),
}
// 如果有交易单号,尝试关联订单
if complaintInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, complaintInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
}
}
// 如果主表没有订单ID尝试从交易信息中查找
if !complaintMain.OrderId.Valid && len(complaintInfo.ComplaintTradeInfoList) > 0 {
for _, tradeInfo := range complaintInfo.ComplaintTradeInfoList {
if tradeInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, tradeInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
break
}
}
// 也可以尝试通过 out_no商家订单号查找
if tradeInfo.OutNo != "" {
order, err := s.OrderModel.FindOneByOrderNo(transCtx, tradeInfo.OutNo)
if err == nil && order != nil {
complaintMain.OrderId = lzUtils.StringToNullString(order.Id)
break
}
}
}
}
if _, err := s.ComplaintMainModel.Insert(transCtx, session, complaintMain); err != nil {
return errors.Wrapf(err, "创建投诉主表失败, task_id: %s", complaintInfo.TaskId)
}
complaintRecord.ComplaintId = complaintMain.Id
} else {
// 更新主表信息
complaintMain.Name = lzUtils.StringToNullString(complaintInfo.OppositeName)
complaintMain.Contact = lzUtils.StringToNullString(complaintInfo.Contact)
complaintMain.Content = lzUtils.StringToNullString(complaintInfo.ComplainContent)
complaintMain.Status = lzUtils.StringToNullString(complaintInfo.Status)
complaintMain.StatusDescription = lzUtils.StringToNullString(complaintInfo.StatusDescription)
// 如果主表没有订单ID尝试关联订单
if !complaintMain.OrderId.Valid {
if complaintInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, complaintInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
}
}
// 尝试从交易信息中查找
if !complaintMain.OrderId.Valid && len(complaintInfo.ComplaintTradeInfoList) > 0 {
for _, tradeInfo := range complaintInfo.ComplaintTradeInfoList {
if tradeInfo.TradeNo != "" {
orderId := s.findOrderByPlatformOrderId(transCtx, tradeInfo.TradeNo)
if orderId != "" {
complaintMain.OrderId = lzUtils.StringToNullString(orderId)
break
}
}
if tradeInfo.OutNo != "" {
order, err := s.OrderModel.FindOneByOrderNo(transCtx, tradeInfo.OutNo)
if err == nil && order != nil {
complaintMain.OrderId = lzUtils.StringToNullString(order.Id)
break
}
}
}
}
}
if err := s.ComplaintMainModel.UpdateWithVersion(transCtx, session, complaintMain); err != nil {
return errors.Wrapf(err, "更新投诉主表失败, task_id: %s", complaintInfo.TaskId)
}
}
// 2. 填充支付宝投诉表数据
complaintRecord.AlipayId = complaintInfo.Id
complaintRecord.TaskId = complaintInfo.TaskId
complaintRecord.OppositePid = lzUtils.StringToNullString(complaintInfo.OppositePid)
complaintRecord.OppositeName = lzUtils.StringToNullString(complaintInfo.OppositeName)
if complaintInfo.ComplainAmount != "" {
amount, err := s.parseDecimal(complaintInfo.ComplainAmount)
if err == nil {
complaintRecord.ComplainAmount = lzUtils.Float64ToNullFloat64(amount)
}
}
complaintRecord.GmtComplain = s.parseTime(complaintInfo.GmtComplain)
complaintRecord.GmtProcess = s.parseTime(complaintInfo.GmtProcess)
complaintRecord.GmtOverdue = s.parseTime(complaintInfo.GmtOverdue)
complaintRecord.ComplainContent = lzUtils.StringToNullString(complaintInfo.ComplainContent)
complaintRecord.TradeNo = lzUtils.StringToNullString(complaintInfo.TradeNo)
complaintRecord.Status = lzUtils.StringToNullString(complaintInfo.Status)
complaintRecord.StatusDescription = lzUtils.StringToNullString(complaintInfo.StatusDescription)
complaintRecord.ProcessCode = lzUtils.StringToNullString(complaintInfo.ProcessCode)
complaintRecord.ProcessMessage = lzUtils.StringToNullString(complaintInfo.ProcessMessage)
complaintRecord.ProcessRemark = lzUtils.StringToNullString(complaintInfo.ProcessRemark)
complaintRecord.GmtRiskFinishTime = s.parseTime(complaintInfo.GmtRiskFinishTime)
complaintRecord.ComplainUrl = lzUtils.StringToNullString(complaintInfo.ComplainUrl)
// 处理图片列表转换为JSON字符串
if len(complaintInfo.ProcessImgUrlList) > 0 {
imgJson, _ := json.Marshal(complaintInfo.ProcessImgUrlList)
complaintRecord.ProcessImgUrlList = lzUtils.StringToNullString(string(imgJson))
}
if len(complaintInfo.CertifyInfo) > 0 {
certifyJson, _ := json.Marshal(complaintInfo.CertifyInfo)
complaintRecord.CertifyInfo = lzUtils.StringToNullString(string(certifyJson))
}
// 保存或更新支付宝投诉表
if isUpdate {
if err := s.ComplaintAlipayModel.UpdateWithVersion(transCtx, session, complaintRecord); err != nil {
return errors.Wrapf(err, "更新支付宝投诉表失败, task_id: %s", complaintInfo.TaskId)
}
} else {
if _, err := s.ComplaintAlipayModel.Insert(transCtx, session, complaintRecord); err != nil {
return errors.Wrapf(err, "插入支付宝投诉表失败, task_id: %s", complaintInfo.TaskId)
}
}
// 3. 保存交易信息
if len(complaintInfo.ComplaintTradeInfoList) > 0 {
// 先删除旧的交易信息(如果存在)
if isUpdate {
oldTrades, _ := s.ComplaintAlipayTradeModel.FindAll(transCtx,
s.ComplaintAlipayTradeModel.SelectBuilder().
Where("complaint_id = ? AND del_state = ?", complaintRecord.Id, 0), "")
for _, oldTrade := range oldTrades {
oldTrade.DelState = 1
oldTrade.DeleteTime = lzUtils.TimeToNullTime(time.Now())
s.ComplaintAlipayTradeModel.UpdateWithVersion(transCtx, session, oldTrade)
}
}
// 插入新的交易信息
for _, tradeInfo := range complaintInfo.ComplaintTradeInfoList {
trade := &model.ComplaintAlipayTrade{
Id: uuid.NewString(),
ComplaintId: complaintRecord.Id,
AlipayTradeId: lzUtils.Int64ToNullInt64(tradeInfo.Id),
AlipayComplaintRecordId: lzUtils.Int64ToNullInt64(tradeInfo.ComplaintRecordId),
TradeNo: lzUtils.StringToNullString(tradeInfo.TradeNo),
OutNo: lzUtils.StringToNullString(tradeInfo.OutNo),
GmtTrade: s.parseTime(tradeInfo.GmtTrade),
GmtRefund: s.parseTime(tradeInfo.GmtRefund),
Status: lzUtils.StringToNullString(tradeInfo.Status),
StatusDescription: lzUtils.StringToNullString(tradeInfo.StatusDescription),
}
if tradeInfo.Amount != "" {
amount, err := s.parseDecimal(tradeInfo.Amount)
if err == nil {
trade.Amount = lzUtils.Float64ToNullFloat64(amount)
}
}
if _, err := s.ComplaintAlipayTradeModel.Insert(transCtx, session, trade); err != nil {
return errors.Wrapf(err, "插入投诉交易信息失败, task_id: %s", complaintInfo.TaskId)
}
}
}
return nil
})
}
// GetLatestComplainTime 查询数据库中最新投诉的投诉时间
func (s *AlipayComplaintService) GetLatestComplainTime(ctx context.Context) (time.Time, error) {
// 从支付宝投诉表查询最新投诉时间(因为主表没有 gmt_complain 字段)
builder := s.ComplaintAlipayModel.SelectBuilder().
Where("del_state = ?", 0).
OrderBy("gmt_complain DESC").
Limit(1)
complaints, err := s.ComplaintAlipayModel.FindAll(ctx, builder, "")
if err != nil {
return time.Time{}, errors.Wrapf(err, "查询最新投诉时间失败")
}
if len(complaints) == 0 {
return time.Time{}, nil // 数据库为空
}
// 返回最新投诉的投诉时间
if complaints[0].GmtComplain.Valid {
return complaints[0].GmtComplain.Time, nil
}
return time.Time{}, nil
}
// parseTime 解析时间字符串
func (s *AlipayComplaintService) parseTime(timeStr string) sql.NullTime {
if timeStr == "" {
return sql.NullTime{Valid: false}
}
// 尝试多种时间格式
formats := []string{
"2006-01-02 15:04:05",
"2006-01-02T15:04:05",
"2006-01-02",
}
for _, format := range formats {
if t, err := time.Parse(format, timeStr); err == nil {
return sql.NullTime{Time: t, Valid: true}
}
}
return sql.NullTime{Valid: false}
}
// parseDecimal 解析金额字符串
func (s *AlipayComplaintService) parseDecimal(amountStr string) (float64, error) {
if amountStr == "" {
return 0, nil
}
// 使用 fmt.Sscanf 解析
var amount float64
_, err := fmt.Sscanf(amountStr, "%f", &amount)
return amount, err
}
// findOrderByPlatformOrderId 根据支付宝交易单号查找订单ID
func (s *AlipayComplaintService) findOrderByPlatformOrderId(ctx context.Context, platformOrderId string) string {
if platformOrderId == "" {
return ""
}
// 通过 PlatformOrderId 查找订单
builder := s.OrderModel.SelectBuilder().
Where("platform_order_id = ? AND del_state = ?", platformOrderId, 0).
Limit(1)
orders, err := s.OrderModel.FindAll(ctx, builder, "")
if err != nil || len(orders) == 0 {
return ""
}
return orders[0].Id
}

View File

@@ -63,7 +63,6 @@ func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, out
NotifyURL: a.config.NotifyUrl, // 异步回调通知地址
},
}
// 获取APP支付字符串这里会签名
payStr, err := client.TradeAppPay(p)
if err != nil {

View File

@@ -33,19 +33,22 @@ func generateAuthDateRange() string {
}
type ApiRequestService struct {
config config.Config
featureModel model.FeatureModel
productFeatureModel model.ProductFeatureModel
tianyuanapi *tianyuanapi.Client
config config.Config
featureModel model.FeatureModel
productFeatureModel model.ProductFeatureModel
userFeatureWhitelistModel model.UserFeatureWhitelistModel
whitelistService *WhitelistService
tianyuanapi *tianyuanapi.Client
}
// NewApiRequestService 是一个构造函数,用于初始化 ApiRequestService
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, tianyuanapi *tianyuanapi.Client) *ApiRequestService {
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, userFeatureWhitelistModel model.UserFeatureWhitelistModel, tianyuanapi *tianyuanapi.Client) *ApiRequestService {
return &ApiRequestService{
config: c,
featureModel: featureModel,
productFeatureModel: productFeatureModel,
tianyuanapi: tianyuanapi,
config: c,
featureModel: featureModel,
productFeatureModel: productFeatureModel,
userFeatureWhitelistModel: userFeatureWhitelistModel,
tianyuanapi: tianyuanapi,
}
}
@@ -61,6 +64,13 @@ type APIResponseData struct {
func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]byte, error) {
var ctx, cancel = context.WithCancel(context.Background())
defer cancel()
// 从params中提取id_card用于白名单检查
idCard := gjson.GetBytes(params, "id_card").String()
// 查询白名单如果提供了id_card集中由 WhitelistService 处理
whitelistedFeatureApiIds, _ := a.whitelistService.GetWhitelistedFeatureApisByIdCard(ctx, idCard)
build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{
"product_id": productID,
})
@@ -109,6 +119,18 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID string) ([]
Success: false,
}
timestamp := time.Now().Format("2006-01-02 15:04:05")
// 检查是否在白名单中
if whitelistedFeatureApiIds[feature.ApiId] {
// 在白名单中返回空结构data 为 null保持与正常返回结构一致
// 直接设置 data 为 null 的 JSON 字节
result.Data = []byte("null")
result.Success = true
result.Timestamp = timestamp
resultsCh <- result
return
}
var (
resp json.RawMessage
preprocessErr error

View File

@@ -173,7 +173,7 @@ func (s *AuthorizationService) generatePDFContent(userInfo map[string]interface{
用户声明与承诺:
本人在授权签署前,已通过实名认证及动态验证码验证(或其他身份验证手段),确认本授权行为为本人真实意思表示,平台已履行身份验证义务。
本人在此声明已充分理解上述授权条款含义,知晓并自愿承担因授权数据使用可能带来的后果,包括但不限于影响个人信用评分、生活行为等。本人确认授权范围内的相关信息由本人提供并真实有效。
本人在此声明已充分理解上述授权条款含义,知晓并自愿承担因授权数据使用可能带来的后果,包括但不限于影响个人风险评分、生活行为等。本人确认授权范围内的相关信息由本人提供并真实有效。
若用户冒名签署或提供虚假信息,由用户自行承担全部法律责任,平台不承担任何后果。
特别提示:

View File

@@ -0,0 +1,560 @@
package service
import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"strings"
"ycc-server/app/main/api/internal/config"
"ycc-server/app/main/model"
"ycc-server/pkg/lzkit/crypto"
"ycc-server/pkg/lzkit/lzUtils"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
// WhitelistService 白名单领域服务,集中处理白名单相关的业务逻辑
type WhitelistService struct {
config config.Config
UserFeatureWhitelistModel model.UserFeatureWhitelistModel
WhitelistOrderModel model.WhitelistOrderModel
WhitelistOrderItemModel model.WhitelistOrderItemModel
QueryModel model.QueryModel
FeatureModel model.FeatureModel
}
// NewWhitelistService 创建白名单服务
func NewWhitelistService(
c config.Config,
userFeatureWhitelistModel model.UserFeatureWhitelistModel,
whitelistOrderModel model.WhitelistOrderModel,
whitelistOrderItemModel model.WhitelistOrderItemModel,
queryModel model.QueryModel,
featureModel model.FeatureModel,
) *WhitelistService {
return &WhitelistService{
config: c,
UserFeatureWhitelistModel: userFeatureWhitelistModel,
WhitelistOrderModel: whitelistOrderModel,
WhitelistOrderItemModel: whitelistOrderItemModel,
QueryModel: queryModel,
FeatureModel: featureModel,
}
}
// EnsureFreeWhitelist 免费下架:如果还没有生效白名单,则创建一条免费白名单记录
func (s *WhitelistService) EnsureFreeWhitelist(
ctx context.Context,
session sqlx.Session,
idCard string,
feature *model.Feature,
userId string,
orderId string,
) error {
// 检查是否已存在生效白名单
builder := s.UserFeatureWhitelistModel.SelectBuilder().
Where("id_card = ? AND feature_id = ?", idCard, feature.Id)
records, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "")
if err != nil {
return errors.Wrap(err, "查询白名单记录失败")
}
for _, r := range records {
if r.Status == 1 {
// 已经下架,直接返回
return nil
}
}
wl := &model.UserFeatureWhitelist{
Id: uuid.NewString(),
IdCard: idCard,
FeatureId: feature.Id,
FeatureApiId: feature.ApiId,
UserId: userId,
OrderId: lzUtils.StringToNullString(orderId),
WhitelistOrderId: lzUtils.StringToNullString(""),
Amount: 0,
Status: 1,
}
_, err = s.UserFeatureWhitelistModel.Insert(ctx, session, wl)
if err != nil {
return errors.Wrap(err, "创建免费白名单记录失败")
}
return nil
}
// CreateWhitelistByPaidOrder 根据已支付的白名单订单,创建对应的白名单记录
func (s *WhitelistService) CreateWhitelistByPaidOrder(
ctx context.Context,
session sqlx.Session,
order *model.Order,
whitelistOrder *model.WhitelistOrder,
) error {
if whitelistOrder.Status != 2 {
// 只处理已支付状态
return nil
}
itemBuilder := s.WhitelistOrderItemModel.SelectBuilder().
Where("order_id = ?", whitelistOrder.Id)
items, err := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "")
if err != nil {
return errors.Wrap(err, "查询白名单订单明细失败")
}
for _, item := range items {
wl := &model.UserFeatureWhitelist{
Id: uuid.NewString(),
IdCard: whitelistOrder.IdCard,
FeatureId: item.FeatureId,
FeatureApiId: item.FeatureApiId,
UserId: whitelistOrder.UserId,
OrderId: lzUtils.StringToNullString(order.Id),
WhitelistOrderId: lzUtils.StringToNullString(whitelistOrder.Id),
Amount: item.Price,
Status: 1,
}
if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil {
return errors.Wrap(err, "创建白名单记录失败")
}
}
return nil
}
// GetWhitelistedFeatureApisByIdCard 获取某个身份证号已下架的 feature_api_id 集合
func (s *WhitelistService) GetWhitelistedFeatureApisByIdCard(
ctx context.Context,
idCard string,
) (map[string]bool, error) {
result := make(map[string]bool)
if idCard == "" {
return result, nil
}
builder := s.UserFeatureWhitelistModel.SelectBuilder().
Where("id_card = ? AND status = ?", idCard, 1)
list, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "")
if err != nil {
return nil, errors.Wrap(err, "查询白名单失败")
}
for _, wl := range list {
result[wl.FeatureApiId] = true
}
return result, nil
}
// CheckWhitelistExists 检查指定身份证号和模块是否已有生效的白名单记录
func (s *WhitelistService) CheckWhitelistExists(
ctx context.Context,
idCard string,
featureId string,
) (bool, error) {
if idCard == "" || featureId == "" {
return false, nil
}
builder := s.UserFeatureWhitelistModel.SelectBuilder().
Where("id_card = ? AND feature_id = ? AND status = ?", idCard, featureId, 1)
list, err := s.UserFeatureWhitelistModel.FindAll(ctx, builder, "")
if err != nil {
return false, errors.Wrap(err, "查询白名单记录失败")
}
return len(list) > 0, nil
}
// ProcessOfflineFeature 统一下架处理:处理免费下架或检查付费下架
// 返回needPay(是否需要支付), amount(金额), whitelistCreated(是否已创建白名单)
func (s *WhitelistService) ProcessOfflineFeature(
ctx context.Context,
session sqlx.Session,
idCard string,
featureApiId string,
userId string,
orderId string,
) (needPay bool, amount float64, whitelistCreated bool, err error) {
// 1. 提取主模块ID并查询feature信息
mainApiId := s.extractMainApiId(featureApiId)
feature, err := s.getFeatureByApiId(ctx, mainApiId)
if err != nil {
return false, 0, false, err
}
// 2. 检查是否已有白名单
exists, err := s.CheckWhitelistExists(ctx, idCard, feature.Id)
if err != nil {
return false, 0, false, err
}
if exists {
// 已有白名单,直接返回成功
return false, 0, true, nil
}
price := feature.WhitelistPrice
// 3. 免费下架:直接创建白名单记录
if price <= 0 {
if err := s.EnsureFreeWhitelist(ctx, session, idCard, feature, userId, orderId); err != nil {
return false, 0, false, err
}
return false, 0, true, nil
}
// 4. 付费下架:检查是否已有支付成功的订单
paidOrderId, err := s.findPaidWhitelistOrder(ctx, userId, idCard, feature.Id)
if err != nil {
return false, 0, false, err
}
// 5. 如果已有支付成功订单,补创建白名单记录
if paidOrderId != "" {
if err := s.createWhitelistFromPaidOrder(ctx, session, idCard, feature, userId, orderId, paidOrderId, price); err != nil {
return false, 0, false, err
}
return false, price, true, nil
}
// 6. 需要支付
return true, price, false, nil
}
// extractMainApiId 提取主模块ID去掉下划线后的部分
func (s *WhitelistService) extractMainApiId(featureApiId string) string {
if idx := strings.Index(featureApiId, "_"); idx > 0 {
return featureApiId[:idx]
}
return featureApiId
}
// getFeatureByApiId 根据API ID查询feature信息
func (s *WhitelistService) getFeatureByApiId(ctx context.Context, apiId string) (*model.Feature, error) {
feature, err := s.FeatureModel.FindOneByApiId(ctx, apiId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return nil, errors.Wrap(err, "模块不存在")
}
return nil, errors.Wrap(err, "查询模块信息失败")
}
return feature, nil
}
// findPaidWhitelistOrder 查找已支付的白名单订单中是否包含指定feature
// 返回paidOrderId如果找到已支付订单error
func (s *WhitelistService) findPaidWhitelistOrder(
ctx context.Context,
userId string,
idCard string,
featureId string,
) (string, error) {
orderBuilder := s.WhitelistOrderModel.SelectBuilder().
Where("user_id = ? AND id_card = ? AND status = ?", userId, idCard, 2) // 2表示已支付
orders, err := s.WhitelistOrderModel.FindAll(ctx, orderBuilder, "")
if err != nil {
return "", errors.Wrap(err, "查询白名单订单失败")
}
// 查找已支付订单中是否包含该feature
for _, order := range orders {
itemBuilder := s.WhitelistOrderItemModel.SelectBuilder().
Where("order_id = ? AND feature_id = ?", order.Id, featureId)
items, itemErr := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "")
if itemErr != nil {
return "", errors.Wrap(itemErr, "查询白名单订单明细失败")
}
if len(items) > 0 {
return order.Id, nil
}
}
return "", nil
}
// createWhitelistFromPaidOrder 根据已支付订单创建白名单记录
func (s *WhitelistService) createWhitelistFromPaidOrder(
ctx context.Context,
session sqlx.Session,
idCard string,
feature *model.Feature,
userId string,
orderId string,
paidOrderId string,
price float64,
) error {
wl := &model.UserFeatureWhitelist{
Id: uuid.NewString(),
IdCard: idCard,
FeatureId: feature.Id,
FeatureApiId: feature.ApiId,
UserId: userId,
OrderId: lzUtils.StringToNullString(orderId),
WhitelistOrderId: lzUtils.StringToNullString(paidOrderId),
Amount: price,
Status: 1, // 生效
}
if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil {
return errors.Wrap(err, "根据已支付订单创建白名单记录失败")
}
return nil
}
// DeleteFeatureFromQueryData 从报告数据中删除指定模块的数据
// queryId: 查询记录IDQuery表的ID
// featureApiId: 要删除的模块API标识
func (s *WhitelistService) DeleteFeatureFromQueryData(
ctx context.Context,
session sqlx.Session,
queryId string,
featureApiId string,
) error {
// 1. 获取查询记录
queryModel, err := s.getQueryModel(ctx, queryId)
if err != nil {
return err
}
if queryModel == nil {
// 报告不存在或数据为空,直接返回成功
return nil
}
// 2. 解密并解析报告数据
mainApiId := s.extractMainApiId(featureApiId)
dataArray, key, err := s.decryptQueryData(queryModel)
if err != nil {
return err
}
// 3. 清空对应模块的数据(将 data 字段设置为 null
modifiedArray, hasModified := s.clearFeatureData(dataArray, mainApiId)
if !hasModified {
logx.Infof("删除报告数据:查询记录 %s 中未找到模块 %s 的数据,跳过删除", queryId, featureApiId)
return nil
}
// 4. 重新加密并更新数据库
if err := s.updateQueryData(ctx, session, queryModel, modifiedArray, key); err != nil {
return err
}
logx.Infof("删除报告数据成功:查询记录 %s模块 %s已将对应模块的 data 字段设置为 null", queryId, featureApiId)
return nil
}
// getQueryModel 获取查询记录如果不存在或数据为空则返回nil
func (s *WhitelistService) getQueryModel(ctx context.Context, queryId string) (*model.Query, error) {
queryModel, err := s.QueryModel.FindOne(ctx, queryId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
logx.Infof("删除报告数据:查询记录 %s 不存在,跳过删除", queryId)
return nil, nil
}
return nil, errors.Wrap(err, "查询报告记录失败")
}
// 如果报告数据为空,直接返回
if !queryModel.QueryData.Valid || queryModel.QueryData.String == "" {
logx.Infof("删除报告数据:查询记录 %s 对应的报告数据为空,跳过删除", queryId)
return nil, nil
}
return queryModel, nil
}
// decryptQueryData 解密并解析报告数据
func (s *WhitelistService) decryptQueryData(queryModel *model.Query) ([]map[string]interface{}, []byte, error) {
// 获取加密密钥
secretKey := s.config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return nil, nil, errors.Wrap(decodeErr, "获取AES密钥失败")
}
// 解密报告数据
decryptedData, decryptErr := crypto.AesDecrypt(queryModel.QueryData.String, key)
if decryptErr != nil {
return nil, nil, errors.Wrap(decryptErr, "解密报告数据失败")
}
// 解析JSON数组
var dataArray []map[string]interface{}
unmarshalErr := json.Unmarshal(decryptedData, &dataArray)
if unmarshalErr != nil {
return nil, nil, errors.Wrap(unmarshalErr, "解析报告数据失败")
}
return dataArray, key, nil
}
// clearFeatureData 清空指定模块的数据(将 data 字段设置为 null
// 返回修改后的数组和是否进行了修改
func (s *WhitelistService) clearFeatureData(dataArray []map[string]interface{}, mainApiId string) ([]map[string]interface{}, bool) {
modifiedArray := make([]map[string]interface{}, 0, len(dataArray))
hasModified := false
for _, item := range dataArray {
// 深拷贝 item避免修改原数据
newItem := make(map[string]interface{})
for k, v := range item {
newItem[k] = v
}
apiID, ok := item["apiID"].(string)
if !ok {
// 如果apiID不存在或类型不对保留原样
modifiedArray = append(modifiedArray, newItem)
continue
}
// 提取主模块ID进行比较
itemMainApiId := s.extractMainApiId(apiID)
// 如果主模块ID匹配将 data 字段设置为 null
if itemMainApiId == mainApiId {
newItem["data"] = nil
hasModified = true
}
modifiedArray = append(modifiedArray, newItem)
}
return modifiedArray, hasModified
}
// updateQueryData 重新加密并更新数据库
func (s *WhitelistService) updateQueryData(
ctx context.Context,
session sqlx.Session,
queryModel *model.Query,
filteredArray []map[string]interface{},
key []byte,
) error {
// 重新序列化
filteredBytes, marshalErr := json.Marshal(filteredArray)
if marshalErr != nil {
return errors.Wrap(marshalErr, "序列化过滤后的报告数据失败")
}
// 重新加密
encryptedData, encryptErr := crypto.AesEncrypt(filteredBytes, key)
if encryptErr != nil {
return errors.Wrap(encryptErr, "加密过滤后的报告数据失败")
}
// 更新数据库
queryModel.QueryData = sql.NullString{
String: encryptedData,
Valid: true,
}
updateErr := s.QueryModel.UpdateWithVersion(ctx, session, queryModel)
if updateErr != nil {
return errors.Wrap(updateErr, "更新报告数据失败")
}
return nil
}
// CheckQueryDataContainsFeature 检查报告数据中是否包含指定的模块
// 返回 true 表示包含该模块false 表示不包含(已删除)
func (s *WhitelistService) CheckQueryDataContainsFeature(
ctx context.Context,
queryId string,
featureApiId string,
) (bool, error) {
// 1. 获取查询记录
queryModel, err := s.getQueryModel(ctx, queryId)
if err != nil {
return false, err
}
if queryModel == nil {
// 报告不存在,认为数据已删除
return false, nil
}
// 2. 解密并解析报告数据
mainApiId := s.extractMainApiId(featureApiId)
dataArray, _, err := s.decryptQueryData(queryModel)
if err != nil {
return false, err
}
// 3. 检查数据中是否包含该模块(且 data 不为 null
for _, item := range dataArray {
apiID, ok := item["apiID"].(string)
if !ok {
continue
}
// 提取主模块ID进行比较
itemMainApiId := s.extractMainApiId(apiID)
if itemMainApiId == mainApiId {
// 找到了该模块,检查 data 字段
dataValue, exists := item["data"]
if !exists || dataValue == nil {
// data 字段不存在或为 null认为数据已删除
return false, nil
}
// data 字段存在且不为 null认为数据存在
return true, nil
}
}
// 未找到该模块的数据,说明已删除
return false, nil
}
// ProcessPaidWhitelistOrder 处理已支付的白名单订单:创建白名单记录并删除报告数据
// order: 支付订单Order表
// whitelistOrder: 白名单订单WhitelistOrder表
func (s *WhitelistService) ProcessPaidWhitelistOrder(
ctx context.Context,
session sqlx.Session,
order *model.Order,
whitelistOrder *model.WhitelistOrder,
) error {
if whitelistOrder.Status != 2 {
// 只处理已支付状态
return nil
}
// 查询订单明细
itemBuilder := s.WhitelistOrderItemModel.SelectBuilder().
Where("order_id = ?", whitelistOrder.Id)
items, err := s.WhitelistOrderItemModel.FindAll(ctx, itemBuilder, "")
if err != nil {
return errors.Wrap(err, "查询白名单订单明细失败")
}
// 为每个明细创建白名单记录并删除报告数据
for _, item := range items {
// 创建白名单记录
wl := &model.UserFeatureWhitelist{
Id: uuid.NewString(),
IdCard: whitelistOrder.IdCard,
FeatureId: item.FeatureId,
FeatureApiId: item.FeatureApiId,
UserId: whitelistOrder.UserId,
OrderId: lzUtils.StringToNullString(""), // 查询订单ID如果有的话会在后续步骤中设置
WhitelistOrderId: lzUtils.StringToNullString(whitelistOrder.Id),
Amount: item.Price,
Status: 1, // 生效
}
if _, err := s.UserFeatureWhitelistModel.Insert(ctx, session, wl); err != nil {
return errors.Wrap(err, "创建白名单记录失败")
}
// 尝试删除报告数据
// 注意由于支付回调时可能不知道具体的查询订单ID这里先尝试根据 id_card 查找
// 如果找不到对应的报告,就跳过删除步骤(不影响主流程)
// 实际的报告数据删除应该在 OfflineFeature 接口中完成(如果提供了 orderId
// 这里暂时不删除,因为无法确定是哪个具体的查询订单
logx.Infof("白名单订单支付成功:订单 %s模块 %s已创建白名单记录。如需删除报告数据请在 OfflineFeature 接口中提供查询订单ID", whitelistOrder.OrderNo, item.FeatureApiId)
}
return nil
}

View File

@@ -34,6 +34,11 @@ type ServiceContext struct {
FeatureModel model.FeatureModel
ProductFeatureModel model.ProductFeatureModel
// 白名单相关模型
UserFeatureWhitelistModel model.UserFeatureWhitelistModel
WhitelistOrderModel model.WhitelistOrderModel
WhitelistOrderItemModel model.WhitelistOrderItemModel
// 订单相关模型
OrderModel model.OrderModel
OrderRefundModel model.OrderRefundModel
@@ -76,20 +81,28 @@ type ServiceContext struct {
ExampleModel model.ExampleModel
GlobalNotificationsModel model.GlobalNotificationsModel
AuthorizationDocumentModel model.AuthorizationDocumentModel
AlipayFromCallbackModel model.AlipayFromCallbackModel
// 投诉系统模型
ComplaintMainModel model.ComplaintMainModel
ComplaintAlipayModel model.ComplaintAlipayModel
ComplaintAlipayTradeModel model.ComplaintAlipayTradeModel
ComplaintManualModel model.ComplaintManualModel
// 服务
AlipayService *service.AliPayService
WechatPayService *service.WechatPayService
ApplePayService *service.ApplePayService
ApiRequestService *service.ApiRequestService
AsynqServer *asynq.Server
AsynqService *service.AsynqService
VerificationService *service.VerificationService
AgentService *service.AgentService
UserService *service.UserService
DictService *service.DictService
ImageService *service.ImageService
AuthorizationService *service.AuthorizationService
AlipayService *service.AliPayService
WechatPayService *service.WechatPayService
ApplePayService *service.ApplePayService
ApiRequestService *service.ApiRequestService
WhitelistService *service.WhitelistService
AsynqServer *asynq.Server
AsynqService *service.AsynqService
VerificationService *service.VerificationService
AgentService *service.AgentService
UserService *service.UserService
DictService *service.DictService
ImageService *service.ImageService
AuthorizationService *service.AuthorizationService
AlipayComplaintService *service.AlipayComplaintService
}
// NewServiceContext 创建服务上下文
@@ -115,6 +128,11 @@ func NewServiceContext(c config.Config) *ServiceContext {
featureModel := model.NewFeatureModel(db, cacheConf)
productFeatureModel := model.NewProductFeatureModel(db, cacheConf)
// ============================== 白名单相关模型 ==============================
userFeatureWhitelistModel := model.NewUserFeatureWhitelistModel(db, cacheConf)
whitelistOrderModel := model.NewWhitelistOrderModel(db, cacheConf)
whitelistOrderItemModel := model.NewWhitelistOrderItemModel(db, cacheConf)
// ============================== 订单相关模型 ==============================
orderModel := model.NewOrderModel(db, cacheConf)
queryModel := model.NewQueryModel(db, cacheConf)
@@ -156,6 +174,12 @@ func NewServiceContext(c config.Config) *ServiceContext {
exampleModel := model.NewExampleModel(db, cacheConf)
globalNotificationsModel := model.NewGlobalNotificationsModel(db, cacheConf)
authorizationDocumentModel := model.NewAuthorizationDocumentModel(db, cacheConf)
alipayFromCallbackModel := model.NewAlipayFromCallbackModel(db, cacheConf)
// 投诉系统模型
complaintMainModel := model.NewComplaintMainModel(db, cacheConf)
complaintAlipayModel := model.NewComplaintAlipayModel(db, cacheConf)
complaintAlipayTradeModel := model.NewComplaintAlipayTradeModel(db, cacheConf)
complaintManualModel := model.NewComplaintManualModel(db, cacheConf)
// ============================== 第三方服务初始化 ==============================
tianyuanapi, err := tianyuanapi.NewClient(tianyuanapi.Config{
@@ -172,7 +196,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
alipayService := service.NewAliPayService(c)
wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey)
applePayService := service.NewApplePayService(c)
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, tianyuanapi)
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, userFeatureWhitelistModel, tianyuanapi)
whitelistService := service.NewWhitelistService(c, userFeatureWhitelistModel, whitelistOrderModel, whitelistOrderItemModel, queryModel, featureModel)
verificationService := service.NewVerificationService(c, tianyuanapi, apiRequestService)
asynqService := service.NewAsynqService(c)
agentService := service.NewAgentService(c, orderModel, agentModel, agentWalletModel,
@@ -183,6 +208,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel)
imageService := service.NewImageService()
authorizationService := service.NewAuthorizationService(c, authorizationDocumentModel)
alipayComplaintService := service.NewAlipayComplaintService(c, alipayService.AlipayClient, complaintMainModel, complaintAlipayModel, complaintAlipayTradeModel, orderModel)
// ============================== 异步任务服务 ==============================
asynqServer := asynq.NewServer(
@@ -214,6 +240,11 @@ func NewServiceContext(c config.Config) *ServiceContext {
FeatureModel: featureModel,
ProductFeatureModel: productFeatureModel,
// 白名单相关模型
UserFeatureWhitelistModel: userFeatureWhitelistModel,
WhitelistOrderModel: whitelistOrderModel,
WhitelistOrderItemModel: whitelistOrderItemModel,
// 订单相关模型
OrderModel: orderModel,
QueryModel: queryModel,
@@ -256,20 +287,28 @@ func NewServiceContext(c config.Config) *ServiceContext {
ExampleModel: exampleModel,
GlobalNotificationsModel: globalNotificationsModel,
AuthorizationDocumentModel: authorizationDocumentModel,
AlipayFromCallbackModel: alipayFromCallbackModel,
// 投诉系统模型
ComplaintMainModel: complaintMainModel,
ComplaintAlipayModel: complaintAlipayModel,
ComplaintAlipayTradeModel: complaintAlipayTradeModel,
ComplaintManualModel: complaintManualModel,
// 服务
AlipayService: alipayService,
WechatPayService: wechatPayService,
ApplePayService: applePayService,
ApiRequestService: apiRequestService,
AsynqServer: asynqServer,
AsynqService: asynqService,
VerificationService: verificationService,
AgentService: agentService,
UserService: userService,
DictService: dictService,
ImageService: imageService,
AuthorizationService: authorizationService,
AlipayService: alipayService,
WechatPayService: wechatPayService,
ApplePayService: applePayService,
ApiRequestService: apiRequestService,
WhitelistService: whitelistService,
AsynqServer: asynqServer,
AsynqService: asynqService,
VerificationService: verificationService,
AgentService: agentService,
UserService: userService,
DictService: dictService,
ImageService: imageService,
AuthorizationService: authorizationService,
AlipayComplaintService: alipayComplaintService,
}
}

View File

@@ -90,6 +90,7 @@ type AdminGetAgentOrderListReq struct {
AgentId *string `form:"agent_id,optional"` // 代理ID可选
OrderId *string `form:"order_id,optional"` // 订单ID可选
ProcessStatus *int64 `form:"process_status,optional"` // 处理状态(可选)
OrderStatus *string `form:"order_status,optional"` // 订单状态可选pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
}
type AdminGetAgentOrderListResp struct {
@@ -127,6 +128,7 @@ type AdminGetAgentRebateListReq struct {
AgentId *string `form:"agent_id,optional"` // 代理ID可选
SourceAgentId *string `form:"source_agent_id,optional"` // 来源代理ID可选
RebateType *int64 `form:"rebate_type,optional"` // 返佣类型(可选)
Status *int64 `form:"status,optional"` // 状态可选1=已发放2=已冻结3=已取消(已退款)
}
type AdminGetAgentRebateListResp struct {

View File

@@ -0,0 +1,63 @@
// Code generated by goctl. DO NOT EDIT.
package types
type AdminGetComplaintDetailReq struct {
Id string `path:"id"` // 投诉ID
}
type AdminGetComplaintDetailResp struct {
Id string `json:"id"` // 投诉ID
Type string `json:"type"` // 投诉类型alipay-支付宝投诉manual-主动投诉
OrderId string `json:"order_id"` // 关联订单ID
Name string `json:"name"` // 投诉人姓名
Contact string `json:"contact"` // 联系方式
Content string `json:"content"` // 投诉内容
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 状态描述
Remark string `json:"remark"` // 处理备注
HandlerId string `json:"handler_id"` // 处理人ID
HandleTime string `json:"handle_time"` // 处理时间
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
AlipayComplaint *AlipayComplaintDetail `json:"alipay_complaint,optional"` // 支付宝投诉详情
ManualComplaint *ManualComplaintDetail `json:"manual_complaint,optional"` // 主动投诉详情
}
type AdminGetComplaintListReq struct {
Page int64 `form:"page,default=1"` // 页码
PageSize int64 `form:"pageSize,default=20"` // 每页数量
Type string `form:"type,optional"` // 投诉类型alipay-支付宝投诉manual-主动投诉
Status string `form:"status,optional"` // 投诉状态pending-待处理processing-处理中resolved-已解决closed-已关闭
Name string `form:"name,optional"` // 投诉人姓名
Contact string `form:"contact,optional"` // 联系方式
OrderId string `form:"order_id,optional"` // 关联订单ID
CreateTimeStart string `form:"create_time_start,optional"` // 创建时间开始
CreateTimeEnd string `form:"create_time_end,optional"` // 创建时间结束
HandleTimeStart string `form:"handle_time_start,optional"` // 处理时间开始
HandleTimeEnd string `form:"handle_time_end,optional"` // 处理时间结束
}
type AdminGetComplaintListResp struct {
Total int64 `json:"total"` // 总数
Items []ComplaintListItem `json:"items"` // 列表
}
type AdminUpdateComplaintRemarkReq struct {
Id string `path:"id"` // 投诉ID
Remark string `json:"remark"` // 处理备注
}
type AdminUpdateComplaintRemarkResp struct {
Success bool `json:"success"` // 是否成功
}
type AdminUpdateComplaintStatusReq struct {
Id string `path:"id"` // 投诉ID
Status string `json:"status"` // 投诉状态pending-待处理processing-处理中resolved-已解决closed-已关闭
StatusDescription string `json:"status_description,optional"` // 状态描述
HandlerId string `json:"handler_id,optional"` // 处理人ID
}
type AdminUpdateComplaintStatusResp struct {
Success bool `json:"success"` // 是否成功
}

View File

@@ -0,0 +1,11 @@
// Code generated by goctl. DO NOT EDIT.
package types
type AdminGetDashboardStatisticsResp struct {
OrderStats AdminOrderStatistics `json:"order_stats"`
RevenueStats AdminRevenueStatistics `json:"revenue_stats"`
AgentStats AdminAgentStatistics `json:"agent_stats"`
ProfitStats AdminProfitStatistics `json:"profit_stats"`
OrderTrend []AdminTrendData `json:"order_trend"`
RevenueTrend []AdminTrendData `json:"revenue_trend"`
}

View File

@@ -11,8 +11,9 @@ type AdminConfigFeatureExampleResp struct {
}
type AdminCreateFeatureReq struct {
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
WhitelistPrice *float64 `json:"whitelist_price,optional"` // 白名单屏蔽价格(单位:元)
}
type AdminCreateFeatureResp struct {
@@ -32,11 +33,12 @@ type AdminGetFeatureDetailReq struct {
}
type AdminGetFeatureDetailResp struct {
Id string `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
Id string `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
WhitelistPrice float64 `json:"whitelist_price"` // 白名单屏蔽价格(单位:元)
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
}
type AdminGetFeatureExampleReq struct {
@@ -65,9 +67,10 @@ type AdminGetFeatureListResp struct {
}
type AdminUpdateFeatureReq struct {
Id string `path:"id"` // 功能ID
ApiId *string `json:"api_id,optional"` // API标识
Name *string `json:"name,optional"` // 描述
Id string `path:"id"` // 功能ID
ApiId *string `json:"api_id,optional"` // API标识
Name *string `json:"name,optional"` // 描述
WhitelistPrice *float64 `json:"whitelist_price,optional"` // 白名单屏蔽价格(单位:元)
}
type AdminUpdateFeatureResp struct {

View File

@@ -54,9 +54,12 @@ type ApplyUpgradeResp struct {
}
type ApplyWithdrawalReq struct {
Amount float64 `json:"amount"` // 提现金额
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Amount float64 `json:"amount"` // 提现金额
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no,optional"` // 银行卡号(银行卡提现必填)
BankName string `json:"bank_name,optional"` // 开户行名称(银行卡提现必填)
}
type ApplyWithdrawalResp struct {
@@ -64,11 +67,36 @@ type ApplyWithdrawalResp struct {
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
}
type CheckFeatureWhitelistStatusReq struct {
IdCard string `form:"id_card"` // 身份证号
FeatureApiId string `form:"feature_api_id"` // Feature的API标识
QueryId string `form:"query_id,optional"` // 查询记录ID可选用于检查报告数据是否已删除
}
type CheckFeatureWhitelistStatusResp struct {
IsWhitelisted bool `json:"is_whitelisted"` // 是否在白名单中
WhitelistPrice float64 `json:"whitelist_price"` // 屏蔽价格单位如果为0表示不支持下架
FeatureId string `json:"feature_id"` // Feature的UUID
DataDeleted bool `json:"data_deleted"` // 报告数据是否已删除仅当提供了query_id时有效
}
type ConversionRateResp struct {
MyConversionRate ConversionRateData `json:"my_conversion_rate"` // 我的转化率
SubordinateConversionRate ConversionRateData `json:"subordinate_conversion_rate"` // 我的下级转化率
}
type CreateWhitelistOrderReq struct {
IdCard string `json:"id_card"` // 身份证号(查询对象标识)
FeatureIds []string `json:"feature_ids"` // 要屏蔽的feature ID列表
OrderId string `json:"order_id,optional"` // 关联的查询订单ID可选
}
type CreateWhitelistOrderResp struct {
OrderId string `json:"order_id"` // 订单ID
OrderNo string `json:"order_no"` // 订单号
TotalAmount float64 `json:"total_amount"` // 总金额
}
type DeleteInviteCodeReq struct {
Id string `json:"id"` // 邀请码ID
}
@@ -116,6 +144,18 @@ type GetInviteLinkResp struct {
InviteLink string `json:"invite_link"` // 邀请链接
}
type GetLastWithdrawalInfoReq struct {
WithdrawalType int64 `form:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
}
type GetLastWithdrawalInfoResp struct {
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号
BankName string `json:"bank_name"` // 开户行名称
}
type GetLevelPrivilegeResp struct {
Levels []LevelPrivilegeItem `json:"levels"`
UpgradeToGoldFee float64 `json:"upgrade_to_gold_fee"`
@@ -162,16 +202,18 @@ type GetRebateListResp struct {
}
type GetRevenueInfoResp struct {
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
Balance float64 `json:"balance"` // 可用余额
FrozenBalance float64 `json:"frozen_balance"` // 冻结余额
TotalEarnings float64 `json:"total_earnings"` // 累计收益(钱包总收益)
WithdrawnAmount float64 `json:"withdrawn_amount"` // 累计提现
CommissionTotal float64 `json:"commission_total"` // 佣金累计总收益(推广订单获得的佣金)
CommissionToday float64 `json:"commission_today"` // 佣金今日收益
CommissionMonth float64 `json:"commission_month"` // 佣金本月收益
RebateTotal float64 `json:"rebate_total"` // 返佣累计总收益(包括推广返佣和升级返佣)
RebateToday float64 `json:"rebate_today"` // 返佣今日收益
RebateMonth float64 `json:"rebate_month"` // 返佣本月收益
AlipayMonthQuota float64 `json:"alipay_month_quota"` // 支付宝每月提现总额度
AlipayMonthUsed float64 `json:"alipay_month_used"` // 本月已使用的支付宝提现额度
}
type GetSubordinateContributionDetailReq struct {
@@ -236,6 +278,24 @@ type GetUpgradeRebateListResp struct {
List []UpgradeRebateItem `json:"list"` // 列表
}
type GetWhitelistFeaturesReq struct {
}
type GetWhitelistFeaturesResp struct {
List []WhitelistFeatureItem `json:"list"` // 可屏蔽的feature列表
}
type GetWhitelistListReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数量
IdCard string `form:"id_card,optional"` // 身份证号(可选,用于筛选)
}
type GetWhitelistListResp struct {
Total int64 `json:"total"` // 总数
List []WhitelistItem `json:"list"` // 列表
}
type GetWithdrawalListReq struct {
Page int64 `form:"page"` // 页码
PageSize int64 `form:"page_size"` // 每页数量
@@ -246,6 +306,17 @@ type GetWithdrawalListResp struct {
List []WithdrawalItem `json:"list"` // 列表
}
type OfflineFeatureReq struct {
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
QueryId string `json:"query_id"` // 查询记录IDQuery表的ID必选
}
type OfflineFeatureResp struct {
Success bool `json:"success"` // 是否已完成下架
NeedPay bool `json:"need_pay"` // 是否需要发起支付
Amount float64 `json:"amount"` // 需要支付的金额单位0表示无需支付
}
type RealNameAuthReq struct {
Name string `json:"name"` // 姓名
IdCard string `json:"id_card"` // 身份证号

View File

@@ -18,7 +18,7 @@ type PaymentCheckResp struct {
type PaymentReq struct {
Id string `json:"id"`
PayMethod string `json:"pay_method"` // 支付方式: wechat, alipay, appleiap, test(仅开发环境), test_empty(仅开发环境-空报告模式)
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade"`
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip agent_upgrade whitelist"`
}
type PaymentResp struct {

View File

@@ -5,3 +5,4 @@ const MsgCleanQueryData = "msg:clean_query_data"
const MsgAgentProcess = "msg:agent:process"
const MsgUnfreezeCommission = "msg:unfreeze:commission"
const MsgUnfreezeCommissionScan = "msg:unfreeze:commission:scan"
const MsgSyncAlipayComplaint = "msg:sync:alipay:complaint"

View File

@@ -1,6 +1,12 @@
// Code generated by goctl. DO NOT EDIT.
package types
type AdminAgentStatistics struct {
TotalCount int64 `json:"total_count"` // 代理总数
TodayNew int64 `json:"today_new"` // 今日新增
MonthNew int64 `json:"month_new"` // 当月新增
}
type AdminApiInfo struct {
Id string `json:"id"`
ApiName string `json:"api_name"`
@@ -23,11 +29,36 @@ type AdminAuditRealNameResp struct {
Success bool `json:"success"`
}
type AdminOrderStatistics struct {
TodayCount int64 `json:"today_count"` // 今日订单数
MonthCount int64 `json:"month_count"` // 当月订单数
TotalCount int64 `json:"total_count"` // 总订单数
YesterdayCount int64 `json:"yesterday_count"` // 昨日订单数
ChangeRate float64 `json:"change_rate"` // 变化率(百分比)
}
type AdminProfitStatistics struct {
TodayProfit float64 `json:"today_profit"` // 今日利润
MonthProfit float64 `json:"month_profit"` // 当月利润
TotalProfit float64 `json:"total_profit"` // 总利润
TodayProfitRate float64 `json:"today_profit_rate"` // 今日利润率
MonthProfitRate float64 `json:"month_profit_rate"` // 当月利润率
TotalProfitRate float64 `json:"total_profit_rate"` // 总利润率
}
type AdminQueryItem struct {
Feature interface{} `json:"feature"`
Data interface{} `json:"data"` // 这里可以是 map 或 具体的 struct
}
type AdminRevenueStatistics struct {
TodayAmount float64 `json:"today_amount"` // 今日营收
MonthAmount float64 `json:"month_amount"` // 当月营收
TotalAmount float64 `json:"total_amount"` // 总营收
YesterdayAmount float64 `json:"yesterday_amount"` // 昨日营收
ChangeRate float64 `json:"change_rate"` // 变化率(百分比)
}
type AdminRoleApiInfo struct {
Id string `json:"id"`
RoleId string `json:"role_id"`
@@ -40,6 +71,11 @@ type AdminRoleApiInfo struct {
Description string `json:"description"`
}
type AdminTrendData struct {
Date string `json:"date"` // 日期格式MM-DD
Value float64 `json:"value"` // 数值
}
type AdminUserListItem struct {
Id string `json:"id"` // 用户ID
Username string `json:"username"` // 用户名
@@ -99,7 +135,8 @@ type AgentOrderListItem struct {
ActualBasePrice float64 `json:"actual_base_price"` // 实际底价
PriceCost float64 `json:"price_cost"` // 提价成本
AgentProfit float64 `json:"agent_profit"` // 代理收益
ProcessStatus int64 `json:"process_status"` // 处理状态
ProcessStatus int64 `json:"process_status"` // 处理状态(保留用于筛选,前端不显示)
OrderStatus string `json:"order_status"` // 订单状态pending-待支付paid-已支付refunded-已退款closed-已关闭failed-支付失败
CreateTime string `json:"create_time"` // 创建时间
}
@@ -133,6 +170,7 @@ type AgentRebateListItem struct {
OrderId string `json:"order_id"` // 订单ID
RebateType int64 `json:"rebate_type"` // 返佣类型
Amount float64 `json:"amount"` // 金额
Status int64 `json:"status"` // 状态1=已发放2=已冻结3=已取消(已退款)
CreateTime string `json:"create_time"` // 创建时间
}
@@ -149,17 +187,57 @@ type AgentUpgradeListItem struct {
}
type AgentWithdrawalListItem struct {
Id string `json:"id"` // 主键
AgentId string `json:"agent_id"` // 代理ID
WithdrawNo string `json:"withdraw_no"` // 提现单号
Amount float64 `json:"amount"` // 金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
Id string `json:"id"` // 主键
AgentId string `json:"agent_id"` // 代理ID
WithdrawNo string `json:"withdraw_no"` // 提现单号
Amount float64 `json:"amount"` // 金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时使用)
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时使用)
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}
type AlipayComplaintDetail struct {
Id string `json:"id"` // 支付宝投诉表ID
AlipayId int64 `json:"alipay_id"` // 支付宝投诉主表的主键id
TaskId string `json:"task_id"` // 投诉单号id
OppositePid string `json:"opposite_pid"` // 被投诉人pid
OppositeName string `json:"opposite_name"` // 被投诉方名称
ComplainAmount string `json:"complain_amount"` // 投诉单涉及交易总金额
GmtComplain string `json:"gmt_complain"` // 投诉时间
GmtProcess string `json:"gmt_process"` // 处理时间
GmtOverdue string `json:"gmt_overdue"` // 过期时间
ComplainContent string `json:"complain_content"` // 用户投诉内容
TradeNo string `json:"trade_no"` // 投诉交易单号
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 投诉单状态枚举值描述
ProcessCode string `json:"process_code"` // 商家处理结果码
ProcessMessage string `json:"process_message"` // 商家处理结果码对应描述
ProcessRemark string `json:"process_remark"` // 商家处理备注
ProcessImgUrlList []string `json:"process_img_url_list"` // 商家处理备注图片url列表
GmtRiskFinishTime string `json:"gmt_risk_finish_time"` // 推送时间
ComplainUrl string `json:"complain_url"` // 投诉网址
CertifyInfo []string `json:"certify_info"` // 投诉凭证图片信息
TradeInfoList []AlipayComplaintTradeInfo `json:"trade_info_list"` // 交易信息列表
}
type AlipayComplaintTradeInfo struct {
Id string `json:"id"` // 交易信息表ID
AlipayTradeId string `json:"alipay_trade_id"` // 交易信息表主键id
AlipayComplaintRecordId string `json:"alipay_complaint_record_id"` // 投诉主表id
TradeNo string `json:"trade_no"` // 支付宝交易单号
OutNo string `json:"out_no"` // 商家订单号
GmtTrade string `json:"gmt_trade"` // 交易时间
GmtRefund string `json:"gmt_refund"` // 退款时间
Status string `json:"status"` // 交易投诉状态
StatusDescription string `json:"status_description"` // 交易投诉状态描述
Amount string `json:"amount"` // 交易单金额
}
type AuthorizationDocumentInfo struct {
@@ -191,6 +269,29 @@ type CommissionItem struct {
CreateTime string `json:"create_time"` // 创建时间
}
type ComplaintListItem struct {
Id string `json:"id"` // 投诉ID
Type string `json:"type"` // 投诉类型alipay-支付宝投诉manual-主动投诉
OrderId string `json:"order_id"` // 关联订单ID
Name string `json:"name"` // 投诉人姓名
Contact string `json:"contact"` // 联系方式
Content string `json:"content"` // 投诉内容
Status string `json:"status"` // 投诉状态
StatusDescription string `json:"status_description"` // 状态描述
Remark string `json:"remark"` // 处理备注
HandlerId string `json:"handler_id"` // 处理人ID
HandleTime string `json:"handle_time"` // 处理时间
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
TaskId string `json:"task_id"` // 支付宝投诉单号
TradeNo string `json:"trade_no"` // 支付宝交易单号
ComplainAmount string `json:"complain_amount"` // 投诉金额
GmtComplain string `json:"gmt_complain"` // 投诉时间
Subject string `json:"subject"` // 投诉主题
Priority string `json:"priority"` // 优先级
Source string `json:"source"` // 投诉来源
}
type ConversionRateData struct {
Daily []PeriodConversionData `json:"daily"` // 日统计(今日、昨日、前日)
Weekly []PeriodConversionData `json:"weekly"` // 周统计(这周、前一周、前两周)
@@ -210,11 +311,12 @@ type Feature struct {
}
type FeatureListItem struct {
Id string `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
Id string `json:"id"` // 功能ID
ApiId string `json:"api_id"` // API标识
Name string `json:"name"` // 描述
WhitelistPrice float64 `json:"whitelist_price"` // 白名单屏蔽价格(单位:元)
CreateTime string `json:"create_time"` // 创建时间
UpdateTime string `json:"update_time"` // 更新时间
}
type InviteCodeItem struct {
@@ -279,6 +381,15 @@ type LevelPrivilegeItem struct {
InviteDiamondReward float64 `json:"invite_diamond_reward"` // 邀请钻石代理奖励金额(元)
}
type ManualComplaintDetail struct {
Id string `json:"id"` // 主动投诉表ID
UserId string `json:"user_id"` // 关联用户ID
Subject string `json:"subject"` // 投诉主题
Priority string `json:"priority"` // 优先级low-低medium-中high-高urgent-紧急
Source string `json:"source"` // 投诉来源web-网站phone-电话email-邮件other-其他
AttachmentUrls []string `json:"attachment_urls"` // 附件URL列表
}
type Notification struct {
Title string `json:"title"` // 通知标题
Content string `json:"content"` // 通知内容 (富文本)
@@ -473,6 +584,7 @@ type RebateItem struct {
OrderNo string `json:"order_no"` // 订单号
RebateType int64 `json:"rebate_type"` // 返佣类型1=直接上级2=钻石上级3=黄金上级
Amount float64 `json:"amount"` // 返佣金额
Status int64 `json:"status"` // 状态1=已发放2=已冻结3=已取消(已退款)
CreateTime string `json:"create_time"` // 创建时间
}
@@ -572,15 +684,37 @@ type User struct {
UserType int64 `json:"userType"`
}
type WithdrawalItem struct {
Id string `json:"id"` // 记录ID
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
Amount float64 `json:"amount"` // 提现金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态1=待审核2=审核通过3=审核拒绝4=提现中5=提现成功6=提现失败
PayeeAccount string `json:"payee_account"` // 收款账户
PayeeName string `json:"payee_name"` // 收款人姓名
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
type WhitelistFeatureItem struct {
FeatureId string `json:"feature_id"` // Feature的UUID
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
FeatureName string `json:"feature_name"` // Feature的名称
WhitelistPrice float64 `json:"whitelist_price"` // 屏蔽价格(单位:元)
}
type WhitelistItem struct {
Id string `json:"id"` // 白名单记录ID
IdCard string `json:"id_card"` // 身份证号
FeatureId string `json:"feature_id"` // Feature的UUID
FeatureApiId string `json:"feature_api_id"` // Feature的API标识
FeatureName string `json:"feature_name"` // Feature的名称
Amount float64 `json:"amount"` // 费用
Status int64 `json:"status"` // 状态1=生效2=已失效
StatusText string `json:"status_text"` // 状态文本
CreateTime string `json:"create_time"` // 创建时间
}
type WithdrawalItem struct {
Id string `json:"id"` // 记录ID
WithdrawalNo string `json:"withdrawal_no"` // 提现单号
Amount float64 `json:"amount"` // 提现金额
TaxAmount float64 `json:"tax_amount"` // 税费金额
ActualAmount float64 `json:"actual_amount"` // 实际到账金额
Status int64 `json:"status"` // 状态1=待审核2=审核通过3=审核拒绝4=提现中5=提现成功6=提现失败
WithdrawalType int64 `json:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
PayeeAccount string `json:"payee_account"` // 收款账户(支付宝账号或银行卡号)
PayeeName string `json:"payee_name"` // 收款人姓名
BankCardNo string `json:"bank_card_no"` // 银行卡号(银行卡提现时填写)
BankName string `json:"bank_name"` // 开户行名称(银行卡提现时填写)
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}

View File

@@ -58,21 +58,24 @@ type (
}
AgentWithdrawal struct {
Id string `db:"id"`
AgentId string `db:"agent_id"`
WithdrawNo string `db:"withdraw_no"` // 提现单号
PayeeAccount string `db:"payee_account"` // 收款账户
PayeeName string `db:"payee_name"` // 收款人姓名
Amount float64 `db:"amount"` // 提现金额
ActualAmount float64 `db:"actual_amount"` // 实际到账金额(扣除税费后
TaxAmount float64 `db:"tax_amount"` // 税费金额
Status int64 `db:"status"` // 状态1=处理中2=成功3=失败
Remark sql.NullString `db:"remark"` // 备注
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
Id string `db:"id"`
AgentId string `db:"agent_id"`
WithdrawalType int64 `db:"withdrawal_type"` // 提现方式1=支付宝2=银行卡
WithdrawNo string `db:"withdraw_no"` // 提现单号
PayeeAccount string `db:"payee_account"` // 收款账户
BankCardNo sql.NullString `db:"bank_card_no"` // 银行卡号(银行卡提现时使用)
BankName sql.NullString `db:"bank_name"` // 开户行名称(银行卡提现时使用
PayeeName string `db:"payee_name"` // 收款人姓名
Amount float64 `db:"amount"` // 提现金额
ActualAmount float64 `db:"actual_amount"` // 实际到账金额(扣除税费后)
TaxAmount float64 `db:"tax_amount"` // 税费金额
Status int64 `db:"status"` // 状态1=处理中2=成功3=失败
Remark sql.NullString `db:"remark"` // 备注
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
}
)
@@ -89,11 +92,11 @@ func (m *defaultAgentWithdrawalModel) Insert(ctx context.Context, session sqlx.S
yccAgentWithdrawalIdKey := fmt.Sprintf("%s%v", cacheYccAgentWithdrawalIdPrefix, data.Id)
yccAgentWithdrawalWithdrawNoKey := fmt.Sprintf("%s%v", cacheYccAgentWithdrawalWithdrawNoPrefix, data.WithdrawNo)
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentWithdrawalRowsExpectAutoSet)
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentWithdrawalRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawNo, data.PayeeAccount, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
return session.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawalType, data.WithdrawNo, data.PayeeAccount, data.BankCardNo, data.BankName, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
}
return conn.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawNo, data.PayeeAccount, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
return conn.ExecCtx(ctx, query, data.Id, data.AgentId, data.WithdrawalType, data.WithdrawNo, data.PayeeAccount, data.BankCardNo, data.BankName, data.PayeeName, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.Remark, data.DeleteTime, data.DelState, data.Version)
}, yccAgentWithdrawalIdKey, yccAgentWithdrawalWithdrawNoKey)
}
func (m *defaultAgentWithdrawalModel) insertUUID(data *AgentWithdrawal) {
@@ -160,9 +163,9 @@ func (m *defaultAgentWithdrawalModel) Update(ctx context.Context, session sqlx.S
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentWithdrawalRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
}
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
}, yccAgentWithdrawalIdKey, yccAgentWithdrawalWithdrawNoKey)
}
@@ -183,9 +186,9 @@ func (m *defaultAgentWithdrawalModel) UpdateWithVersion(ctx context.Context, ses
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentWithdrawalRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.PayeeAccount, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawalType, newData.WithdrawNo, newData.PayeeAccount, newData.BankCardNo, newData.BankName, newData.PayeeName, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
}, yccAgentWithdrawalIdKey, yccAgentWithdrawalWithdrawNoKey)
if err != nil {
return err

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 _ AlipayFromCallbackModel = (*customAlipayFromCallbackModel)(nil)
type (
// AlipayFromCallbackModel is an interface to be customized, add more methods here,
// and implement the added methods in customAlipayFromCallbackModel.
AlipayFromCallbackModel interface {
alipayFromCallbackModel
}
customAlipayFromCallbackModel struct {
*defaultAlipayFromCallbackModel
}
)
// NewAlipayFromCallbackModel returns a model for the database table.
func NewAlipayFromCallbackModel(conn sqlx.SqlConn, c cache.CacheConf) AlipayFromCallbackModel {
return &customAlipayFromCallbackModel{
defaultAlipayFromCallbackModel: newAlipayFromCallbackModel(conn, c),
}
}

View File

@@ -0,0 +1,391 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
alipayFromCallbackFieldNames = builder.RawFieldNames(&AlipayFromCallback{})
alipayFromCallbackRows = strings.Join(alipayFromCallbackFieldNames, ",")
alipayFromCallbackRowsExpectAutoSet = strings.Join(stringx.Remove(alipayFromCallbackFieldNames, "`create_time`", "`update_time`"), ",")
alipayFromCallbackRowsWithPlaceHolder = strings.Join(stringx.Remove(alipayFromCallbackFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccAlipayFromCallbackIdPrefix = "cache:ycc:alipayFromCallback:id:"
)
type (
alipayFromCallbackModel interface {
Insert(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) (sql.Result, error)
FindOne(ctx context.Context, id string) (*AlipayFromCallback, error)
Update(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) 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 *AlipayFromCallback) 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) ([]*AlipayFromCallback, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AlipayFromCallback, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AlipayFromCallback, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AlipayFromCallback, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AlipayFromCallback, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultAlipayFromCallbackModel struct {
sqlc.CachedConn
table string
}
AlipayFromCallback struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
MsgMethod string `db:"msg_method"` // 消息类型alipay.merchant.tradecomplain.changed, alipay.security.risk.complaints.merchants.notify
AppId string `db:"app_id"` // 应用ID
NotifyId sql.NullString `db:"notify_id"` // 通知ID支付宝返回的
BizContent string `db:"biz_content"` // 业务内容JSON字符串存储完整的biz_content
Status string `db:"status"` // 处理状态pending=待处理processed=已处理failed=处理失败
ErrorMessage sql.NullString `db:"error_message"` // 错误信息(如果处理失败)
}
)
func newAlipayFromCallbackModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAlipayFromCallbackModel {
return &defaultAlipayFromCallbackModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`alipay_from_callback`",
}
}
func (m *defaultAlipayFromCallbackModel) Insert(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccAlipayFromCallbackIdKey := fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, 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, alipayFromCallbackRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage)
}, yccAlipayFromCallbackIdKey)
}
func (m *defaultAlipayFromCallbackModel) insertUUID(data *AlipayFromCallback) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultAlipayFromCallbackModel) FindOne(ctx context.Context, id string) (*AlipayFromCallback, error) {
yccAlipayFromCallbackIdKey := fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, id)
var resp AlipayFromCallback
err := m.QueryRowCtx(ctx, &resp, yccAlipayFromCallbackIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", alipayFromCallbackRows, 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 *defaultAlipayFromCallbackModel) Update(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) (sql.Result, error) {
yccAlipayFromCallbackIdKey := fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, 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, alipayFromCallbackRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage, data.Id)
}, yccAlipayFromCallbackIdKey)
}
func (m *defaultAlipayFromCallbackModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccAlipayFromCallbackIdKey := fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, 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, alipayFromCallbackRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.MsgMethod, data.AppId, data.NotifyId, data.BizContent, data.Status, data.ErrorMessage, data.Id, oldVersion)
}, yccAlipayFromCallbackIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultAlipayFromCallbackModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AlipayFromCallback) 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 "), "AlipayFromCallbackModel delete err : %+v", err)
}
return nil
}
func (m *defaultAlipayFromCallbackModel) 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 *defaultAlipayFromCallbackModel) 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 *defaultAlipayFromCallbackModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AlipayFromCallback, error) {
builder = builder.Columns(alipayFromCallbackRows)
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 []*AlipayFromCallback
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAlipayFromCallbackModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AlipayFromCallback, error) {
builder = builder.Columns(alipayFromCallbackRows)
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 []*AlipayFromCallback
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAlipayFromCallbackModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AlipayFromCallback, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(alipayFromCallbackRows)
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 []*AlipayFromCallback
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultAlipayFromCallbackModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AlipayFromCallback, error) {
builder = builder.Columns(alipayFromCallbackRows)
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 []*AlipayFromCallback
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAlipayFromCallbackModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AlipayFromCallback, error) {
builder = builder.Columns(alipayFromCallbackRows)
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 []*AlipayFromCallback
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultAlipayFromCallbackModel) 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 *defaultAlipayFromCallbackModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultAlipayFromCallbackModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
yccAlipayFromCallbackIdKey := fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, 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)
}, yccAlipayFromCallbackIdKey)
return err
}
func (m *defaultAlipayFromCallbackModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccAlipayFromCallbackIdPrefix, primary)
}
func (m *defaultAlipayFromCallbackModel) 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", alipayFromCallbackRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultAlipayFromCallbackModel) tableName() string {
return 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 _ ComplaintAlipayModel = (*customComplaintAlipayModel)(nil)
type (
// ComplaintAlipayModel is an interface to be customized, add more methods here,
// and implement the added methods in customComplaintAlipayModel.
ComplaintAlipayModel interface {
complaintAlipayModel
}
customComplaintAlipayModel struct {
*defaultComplaintAlipayModel
}
)
// NewComplaintAlipayModel returns a model for the database table.
func NewComplaintAlipayModel(conn sqlx.SqlConn, c cache.CacheConf) ComplaintAlipayModel {
return &customComplaintAlipayModel{
defaultComplaintAlipayModel: newComplaintAlipayModel(conn, c),
}
}

View File

@@ -0,0 +1,470 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
complaintAlipayFieldNames = builder.RawFieldNames(&ComplaintAlipay{})
complaintAlipayRows = strings.Join(complaintAlipayFieldNames, ",")
complaintAlipayRowsExpectAutoSet = strings.Join(stringx.Remove(complaintAlipayFieldNames, "`create_time`", "`update_time`"), ",")
complaintAlipayRowsWithPlaceHolder = strings.Join(stringx.Remove(complaintAlipayFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccComplaintAlipayIdPrefix = "cache:ycc:complaintAlipay:id:"
cacheYccComplaintAlipayAlipayIdPrefix = "cache:ycc:complaintAlipay:alipayId:"
cacheYccComplaintAlipayTaskIdPrefix = "cache:ycc:complaintAlipay:taskId:"
)
type (
complaintAlipayModel interface {
Insert(ctx context.Context, session sqlx.Session, data *ComplaintAlipay) (sql.Result, error)
FindOne(ctx context.Context, id string) (*ComplaintAlipay, error)
FindOneByAlipayId(ctx context.Context, alipayId int64) (*ComplaintAlipay, error)
FindOneByTaskId(ctx context.Context, taskId string) (*ComplaintAlipay, error)
Update(ctx context.Context, session sqlx.Session, data *ComplaintAlipay) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintAlipay) 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 *ComplaintAlipay) 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) ([]*ComplaintAlipay, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipay, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipay, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintAlipay, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintAlipay, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultComplaintAlipayModel struct {
sqlc.CachedConn
table string
}
ComplaintAlipay struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
ComplaintId string `db:"complaint_id"` // 关联投诉主表ID关联complaint_main.id
AlipayId int64 `db:"alipay_id"` // 支付宝投诉主表的主键id支付宝返回的id
TaskId string `db:"task_id"` // 投诉单号id支付宝返回的task_id用于唯一标识
OppositePid sql.NullString `db:"opposite_pid"` // 被投诉人pid
OppositeName sql.NullString `db:"opposite_name"` // 投诉单中被投诉方的名称
ComplainAmount sql.NullFloat64 `db:"complain_amount"` // 投诉单涉及交易总金额(单位:人民币元)
GmtComplain sql.NullTime `db:"gmt_complain"` // 投诉时间
GmtProcess sql.NullTime `db:"gmt_process"` // 处理时间
GmtOverdue sql.NullTime `db:"gmt_overdue"` // 过期时间
ComplainContent sql.NullString `db:"complain_content"` // 用户投诉内容
TradeNo sql.NullString `db:"trade_no"` // 投诉交易单号
Status sql.NullString `db:"status"` // 投诉状态(支付宝返回的状态)
StatusDescription sql.NullString `db:"status_description"` // 投诉单状态枚举值描述
ProcessCode sql.NullString `db:"process_code"` // 商家处理结果码
ProcessMessage sql.NullString `db:"process_message"` // 商家处理结果码对应描述
ProcessRemark sql.NullString `db:"process_remark"` // 商家处理备注
ProcessImgUrlList sql.NullString `db:"process_img_url_list"` // 商家处理备注图片url列表JSON数组字符串
GmtRiskFinishTime sql.NullTime `db:"gmt_risk_finish_time"` // 推送时间
ComplainUrl sql.NullString `db:"complain_url"` // 投诉网址
CertifyInfo sql.NullString `db:"certify_info"` // 投诉凭证图片信息JSON数组字符串
}
)
func newComplaintAlipayModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultComplaintAlipayModel {
return &defaultComplaintAlipayModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`complaint_alipay`",
}
}
func (m *defaultComplaintAlipayModel) Insert(ctx context.Context, session sqlx.Session, data *ComplaintAlipay) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccComplaintAlipayAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayAlipayIdPrefix, data.AlipayId)
yccComplaintAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, data.Id)
yccComplaintAlipayTaskIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTaskIdPrefix, data.TaskId)
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, complaintAlipayRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayId, data.TaskId, data.OppositePid, data.OppositeName, data.ComplainAmount, data.GmtComplain, data.GmtProcess, data.GmtOverdue, data.ComplainContent, data.TradeNo, data.Status, data.StatusDescription, data.ProcessCode, data.ProcessMessage, data.ProcessRemark, data.ProcessImgUrlList, data.GmtRiskFinishTime, data.ComplainUrl, data.CertifyInfo)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayId, data.TaskId, data.OppositePid, data.OppositeName, data.ComplainAmount, data.GmtComplain, data.GmtProcess, data.GmtOverdue, data.ComplainContent, data.TradeNo, data.Status, data.StatusDescription, data.ProcessCode, data.ProcessMessage, data.ProcessRemark, data.ProcessImgUrlList, data.GmtRiskFinishTime, data.ComplainUrl, data.CertifyInfo)
}, yccComplaintAlipayAlipayIdKey, yccComplaintAlipayIdKey, yccComplaintAlipayTaskIdKey)
}
func (m *defaultComplaintAlipayModel) insertUUID(data *ComplaintAlipay) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultComplaintAlipayModel) FindOne(ctx context.Context, id string) (*ComplaintAlipay, error) {
yccComplaintAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, id)
var resp ComplaintAlipay
err := m.QueryRowCtx(ctx, &resp, yccComplaintAlipayIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", complaintAlipayRows, 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 *defaultComplaintAlipayModel) FindOneByAlipayId(ctx context.Context, alipayId int64) (*ComplaintAlipay, error) {
yccComplaintAlipayAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayAlipayIdPrefix, alipayId)
var resp ComplaintAlipay
err := m.QueryRowIndexCtx(ctx, &resp, yccComplaintAlipayAlipayIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `alipay_id` = ? and del_state = ? limit 1", complaintAlipayRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, alipayId, globalkey.DelStateNo); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) FindOneByTaskId(ctx context.Context, taskId string) (*ComplaintAlipay, error) {
yccComplaintAlipayTaskIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTaskIdPrefix, taskId)
var resp ComplaintAlipay
err := m.QueryRowIndexCtx(ctx, &resp, yccComplaintAlipayTaskIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `task_id` = ? and del_state = ? limit 1", complaintAlipayRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, taskId, globalkey.DelStateNo); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) Update(ctx context.Context, session sqlx.Session, newData *ComplaintAlipay) (sql.Result, error) {
data, err := m.FindOne(ctx, newData.Id)
if err != nil {
return nil, err
}
yccComplaintAlipayAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayAlipayIdPrefix, data.AlipayId)
yccComplaintAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, data.Id)
yccComplaintAlipayTaskIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTaskIdPrefix, data.TaskId)
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, complaintAlipayRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ComplaintId, newData.AlipayId, newData.TaskId, newData.OppositePid, newData.OppositeName, newData.ComplainAmount, newData.GmtComplain, newData.GmtProcess, newData.GmtOverdue, newData.ComplainContent, newData.TradeNo, newData.Status, newData.StatusDescription, newData.ProcessCode, newData.ProcessMessage, newData.ProcessRemark, newData.ProcessImgUrlList, newData.GmtRiskFinishTime, newData.ComplainUrl, newData.CertifyInfo, newData.Id)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ComplaintId, newData.AlipayId, newData.TaskId, newData.OppositePid, newData.OppositeName, newData.ComplainAmount, newData.GmtComplain, newData.GmtProcess, newData.GmtOverdue, newData.ComplainContent, newData.TradeNo, newData.Status, newData.StatusDescription, newData.ProcessCode, newData.ProcessMessage, newData.ProcessRemark, newData.ProcessImgUrlList, newData.GmtRiskFinishTime, newData.ComplainUrl, newData.CertifyInfo, newData.Id)
}, yccComplaintAlipayAlipayIdKey, yccComplaintAlipayIdKey, yccComplaintAlipayTaskIdKey)
}
func (m *defaultComplaintAlipayModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *ComplaintAlipay) error {
oldVersion := newData.Version
newData.Version += 1
var sqlResult sql.Result
var err error
data, err := m.FindOne(ctx, newData.Id)
if err != nil {
return err
}
yccComplaintAlipayAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayAlipayIdPrefix, data.AlipayId)
yccComplaintAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, data.Id)
yccComplaintAlipayTaskIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTaskIdPrefix, data.TaskId)
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, complaintAlipayRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ComplaintId, newData.AlipayId, newData.TaskId, newData.OppositePid, newData.OppositeName, newData.ComplainAmount, newData.GmtComplain, newData.GmtProcess, newData.GmtOverdue, newData.ComplainContent, newData.TradeNo, newData.Status, newData.StatusDescription, newData.ProcessCode, newData.ProcessMessage, newData.ProcessRemark, newData.ProcessImgUrlList, newData.GmtRiskFinishTime, newData.ComplainUrl, newData.CertifyInfo, newData.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ComplaintId, newData.AlipayId, newData.TaskId, newData.OppositePid, newData.OppositeName, newData.ComplainAmount, newData.GmtComplain, newData.GmtProcess, newData.GmtOverdue, newData.ComplainContent, newData.TradeNo, newData.Status, newData.StatusDescription, newData.ProcessCode, newData.ProcessMessage, newData.ProcessRemark, newData.ProcessImgUrlList, newData.GmtRiskFinishTime, newData.ComplainUrl, newData.CertifyInfo, newData.Id, oldVersion)
}, yccComplaintAlipayAlipayIdKey, yccComplaintAlipayIdKey, yccComplaintAlipayTaskIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultComplaintAlipayModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *ComplaintAlipay) 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 "), "ComplaintAlipayModel delete err : %+v", err)
}
return nil
}
func (m *defaultComplaintAlipayModel) 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 *defaultComplaintAlipayModel) 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 *defaultComplaintAlipayModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*ComplaintAlipay, error) {
builder = builder.Columns(complaintAlipayRows)
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 []*ComplaintAlipay
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipay, error) {
builder = builder.Columns(complaintAlipayRows)
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 []*ComplaintAlipay
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipay, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(complaintAlipayRows)
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 []*ComplaintAlipay
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultComplaintAlipayModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintAlipay, error) {
builder = builder.Columns(complaintAlipayRows)
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 []*ComplaintAlipay
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintAlipay, error) {
builder = builder.Columns(complaintAlipayRows)
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 []*ComplaintAlipay
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayModel) 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 *defaultComplaintAlipayModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultComplaintAlipayModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
data, err := m.FindOne(ctx, id)
if err != nil {
return err
}
yccComplaintAlipayAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayAlipayIdPrefix, data.AlipayId)
yccComplaintAlipayIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, id)
yccComplaintAlipayTaskIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTaskIdPrefix, data.TaskId)
_, 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)
}, yccComplaintAlipayAlipayIdKey, yccComplaintAlipayIdKey, yccComplaintAlipayTaskIdKey)
return err
}
func (m *defaultComplaintAlipayModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccComplaintAlipayIdPrefix, primary)
}
func (m *defaultComplaintAlipayModel) 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", complaintAlipayRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultComplaintAlipayModel) tableName() string {
return 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 _ ComplaintAlipayTradeModel = (*customComplaintAlipayTradeModel)(nil)
type (
// ComplaintAlipayTradeModel is an interface to be customized, add more methods here,
// and implement the added methods in customComplaintAlipayTradeModel.
ComplaintAlipayTradeModel interface {
complaintAlipayTradeModel
}
customComplaintAlipayTradeModel struct {
*defaultComplaintAlipayTradeModel
}
)
// NewComplaintAlipayTradeModel returns a model for the database table.
func NewComplaintAlipayTradeModel(conn sqlx.SqlConn, c cache.CacheConf) ComplaintAlipayTradeModel {
return &customComplaintAlipayTradeModel{
defaultComplaintAlipayTradeModel: newComplaintAlipayTradeModel(conn, c),
}
}

View File

@@ -0,0 +1,395 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
complaintAlipayTradeFieldNames = builder.RawFieldNames(&ComplaintAlipayTrade{})
complaintAlipayTradeRows = strings.Join(complaintAlipayTradeFieldNames, ",")
complaintAlipayTradeRowsExpectAutoSet = strings.Join(stringx.Remove(complaintAlipayTradeFieldNames, "`create_time`", "`update_time`"), ",")
complaintAlipayTradeRowsWithPlaceHolder = strings.Join(stringx.Remove(complaintAlipayTradeFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccComplaintAlipayTradeIdPrefix = "cache:ycc:complaintAlipayTrade:id:"
)
type (
complaintAlipayTradeModel interface {
Insert(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) (sql.Result, error)
FindOne(ctx context.Context, id string) (*ComplaintAlipayTrade, error)
Update(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) 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 *ComplaintAlipayTrade) 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) ([]*ComplaintAlipayTrade, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipayTrade, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipayTrade, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintAlipayTrade, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintAlipayTrade, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultComplaintAlipayTradeModel struct {
sqlc.CachedConn
table string
}
ComplaintAlipayTrade struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
ComplaintId string `db:"complaint_id"` // 投诉主表ID关联complaint_alipay.id
AlipayTradeId sql.NullInt64 `db:"alipay_trade_id"` // 交易信息表主键id支付宝返回的id
AlipayComplaintRecordId sql.NullInt64 `db:"alipay_complaint_record_id"` // 投诉主表id支付宝返回的complaint_record_id
TradeNo sql.NullString `db:"trade_no"` // 支付宝交易单号
OutNo sql.NullString `db:"out_no"` // 商家订单号
GmtTrade sql.NullTime `db:"gmt_trade"` // 交易时间
GmtRefund sql.NullTime `db:"gmt_refund"` // 退款时间
Status sql.NullString `db:"status"` // 交易投诉状态
StatusDescription sql.NullString `db:"status_description"` // 交易投诉状态描述
Amount sql.NullFloat64 `db:"amount"` // 交易单金额(单位:人民币元)
}
)
func newComplaintAlipayTradeModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultComplaintAlipayTradeModel {
return &defaultComplaintAlipayTradeModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`complaint_alipay_trade`",
}
}
func (m *defaultComplaintAlipayTradeModel) Insert(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccComplaintAlipayTradeIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, 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, complaintAlipayTradeRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount)
}, yccComplaintAlipayTradeIdKey)
}
func (m *defaultComplaintAlipayTradeModel) insertUUID(data *ComplaintAlipayTrade) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultComplaintAlipayTradeModel) FindOne(ctx context.Context, id string) (*ComplaintAlipayTrade, error) {
yccComplaintAlipayTradeIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, id)
var resp ComplaintAlipayTrade
err := m.QueryRowCtx(ctx, &resp, yccComplaintAlipayTradeIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", complaintAlipayTradeRows, 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 *defaultComplaintAlipayTradeModel) Update(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) (sql.Result, error) {
yccComplaintAlipayTradeIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, 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, complaintAlipayTradeRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount, data.Id)
}, yccComplaintAlipayTradeIdKey)
}
func (m *defaultComplaintAlipayTradeModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccComplaintAlipayTradeIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, 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, complaintAlipayTradeRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.AlipayTradeId, data.AlipayComplaintRecordId, data.TradeNo, data.OutNo, data.GmtTrade, data.GmtRefund, data.Status, data.StatusDescription, data.Amount, data.Id, oldVersion)
}, yccComplaintAlipayTradeIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultComplaintAlipayTradeModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *ComplaintAlipayTrade) 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 "), "ComplaintAlipayTradeModel delete err : %+v", err)
}
return nil
}
func (m *defaultComplaintAlipayTradeModel) 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 *defaultComplaintAlipayTradeModel) 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 *defaultComplaintAlipayTradeModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*ComplaintAlipayTrade, error) {
builder = builder.Columns(complaintAlipayTradeRows)
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 []*ComplaintAlipayTrade
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayTradeModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipayTrade, error) {
builder = builder.Columns(complaintAlipayTradeRows)
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 []*ComplaintAlipayTrade
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayTradeModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintAlipayTrade, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(complaintAlipayTradeRows)
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 []*ComplaintAlipayTrade
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultComplaintAlipayTradeModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintAlipayTrade, error) {
builder = builder.Columns(complaintAlipayTradeRows)
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 []*ComplaintAlipayTrade
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayTradeModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintAlipayTrade, error) {
builder = builder.Columns(complaintAlipayTradeRows)
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 []*ComplaintAlipayTrade
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintAlipayTradeModel) 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 *defaultComplaintAlipayTradeModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultComplaintAlipayTradeModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
yccComplaintAlipayTradeIdKey := fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, 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)
}, yccComplaintAlipayTradeIdKey)
return err
}
func (m *defaultComplaintAlipayTradeModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccComplaintAlipayTradeIdPrefix, primary)
}
func (m *defaultComplaintAlipayTradeModel) 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", complaintAlipayTradeRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultComplaintAlipayTradeModel) tableName() string {
return 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 _ ComplaintMainModel = (*customComplaintMainModel)(nil)
type (
// ComplaintMainModel is an interface to be customized, add more methods here,
// and implement the added methods in customComplaintMainModel.
ComplaintMainModel interface {
complaintMainModel
}
customComplaintMainModel struct {
*defaultComplaintMainModel
}
)
// NewComplaintMainModel returns a model for the database table.
func NewComplaintMainModel(conn sqlx.SqlConn, c cache.CacheConf) ComplaintMainModel {
return &customComplaintMainModel{
defaultComplaintMainModel: newComplaintMainModel(conn, c),
}
}

View File

@@ -0,0 +1,395 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
complaintMainFieldNames = builder.RawFieldNames(&ComplaintMain{})
complaintMainRows = strings.Join(complaintMainFieldNames, ",")
complaintMainRowsExpectAutoSet = strings.Join(stringx.Remove(complaintMainFieldNames, "`create_time`", "`update_time`"), ",")
complaintMainRowsWithPlaceHolder = strings.Join(stringx.Remove(complaintMainFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccComplaintMainIdPrefix = "cache:ycc:complaintMain:id:"
)
type (
complaintMainModel interface {
Insert(ctx context.Context, session sqlx.Session, data *ComplaintMain) (sql.Result, error)
FindOne(ctx context.Context, id string) (*ComplaintMain, error)
Update(ctx context.Context, session sqlx.Session, data *ComplaintMain) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintMain) 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 *ComplaintMain) 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) ([]*ComplaintMain, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintMain, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintMain, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintMain, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintMain, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultComplaintMainModel struct {
sqlc.CachedConn
table string
}
ComplaintMain struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
Type string `db:"type"` // 投诉类型alipay=支付宝投诉manual=主动投诉
OrderId sql.NullString `db:"order_id"` // 关联订单ID如果有相关订单
Name sql.NullString `db:"name"` // 投诉人姓名
Contact sql.NullString `db:"contact"` // 联系方式(手机号、邮箱等)
Content sql.NullString `db:"content"` // 投诉内容
Status sql.NullString `db:"status"` // 投诉状态通用状态pending=待处理processing=处理中resolved=已解决closed=已关闭)
StatusDescription sql.NullString `db:"status_description"` // 状态描述
Remark sql.NullString `db:"remark"` // 处理备注
HandlerId sql.NullString `db:"handler_id"` // 处理人ID关联管理员或其他处理人员
HandleTime sql.NullTime `db:"handle_time"` // 处理时间
}
)
func newComplaintMainModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultComplaintMainModel {
return &defaultComplaintMainModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`complaint_main`",
}
}
func (m *defaultComplaintMainModel) Insert(ctx context.Context, session sqlx.Session, data *ComplaintMain) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccComplaintMainIdKey := fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, 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, complaintMainRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime)
}, yccComplaintMainIdKey)
}
func (m *defaultComplaintMainModel) insertUUID(data *ComplaintMain) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultComplaintMainModel) FindOne(ctx context.Context, id string) (*ComplaintMain, error) {
yccComplaintMainIdKey := fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, id)
var resp ComplaintMain
err := m.QueryRowCtx(ctx, &resp, yccComplaintMainIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", complaintMainRows, 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 *defaultComplaintMainModel) Update(ctx context.Context, session sqlx.Session, data *ComplaintMain) (sql.Result, error) {
yccComplaintMainIdKey := fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, 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, complaintMainRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime, data.Id)
}, yccComplaintMainIdKey)
}
func (m *defaultComplaintMainModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintMain) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccComplaintMainIdKey := fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, 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, complaintMainRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Type, data.OrderId, data.Name, data.Contact, data.Content, data.Status, data.StatusDescription, data.Remark, data.HandlerId, data.HandleTime, data.Id, oldVersion)
}, yccComplaintMainIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultComplaintMainModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *ComplaintMain) 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 "), "ComplaintMainModel delete err : %+v", err)
}
return nil
}
func (m *defaultComplaintMainModel) 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 *defaultComplaintMainModel) 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 *defaultComplaintMainModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*ComplaintMain, error) {
builder = builder.Columns(complaintMainRows)
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 []*ComplaintMain
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintMainModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintMain, error) {
builder = builder.Columns(complaintMainRows)
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 []*ComplaintMain
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintMainModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintMain, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(complaintMainRows)
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 []*ComplaintMain
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultComplaintMainModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintMain, error) {
builder = builder.Columns(complaintMainRows)
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 []*ComplaintMain
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintMainModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintMain, error) {
builder = builder.Columns(complaintMainRows)
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 []*ComplaintMain
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintMainModel) 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 *defaultComplaintMainModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultComplaintMainModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
yccComplaintMainIdKey := fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, 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)
}, yccComplaintMainIdKey)
return err
}
func (m *defaultComplaintMainModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccComplaintMainIdPrefix, primary)
}
func (m *defaultComplaintMainModel) 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", complaintMainRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultComplaintMainModel) tableName() string {
return 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 _ ComplaintManualModel = (*customComplaintManualModel)(nil)
type (
// ComplaintManualModel is an interface to be customized, add more methods here,
// and implement the added methods in customComplaintManualModel.
ComplaintManualModel interface {
complaintManualModel
}
customComplaintManualModel struct {
*defaultComplaintManualModel
}
)
// NewComplaintManualModel returns a model for the database table.
func NewComplaintManualModel(conn sqlx.SqlConn, c cache.CacheConf) ComplaintManualModel {
return &customComplaintManualModel{
defaultComplaintManualModel: newComplaintManualModel(conn, c),
}
}

View File

@@ -0,0 +1,391 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
complaintManualFieldNames = builder.RawFieldNames(&ComplaintManual{})
complaintManualRows = strings.Join(complaintManualFieldNames, ",")
complaintManualRowsExpectAutoSet = strings.Join(stringx.Remove(complaintManualFieldNames, "`create_time`", "`update_time`"), ",")
complaintManualRowsWithPlaceHolder = strings.Join(stringx.Remove(complaintManualFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccComplaintManualIdPrefix = "cache:ycc:complaintManual:id:"
)
type (
complaintManualModel interface {
Insert(ctx context.Context, session sqlx.Session, data *ComplaintManual) (sql.Result, error)
FindOne(ctx context.Context, id string) (*ComplaintManual, error)
Update(ctx context.Context, session sqlx.Session, data *ComplaintManual) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintManual) 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 *ComplaintManual) 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) ([]*ComplaintManual, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintManual, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintManual, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintManual, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintManual, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultComplaintManualModel struct {
sqlc.CachedConn
table string
}
ComplaintManual struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
ComplaintId string `db:"complaint_id"` // 关联投诉主表ID关联complaint_main.id
UserId sql.NullString `db:"user_id"` // 关联用户ID如果有相关用户
Subject sql.NullString `db:"subject"` // 投诉主题
Priority sql.NullString `db:"priority"` // 优先级low=低medium=中high=高urgent=紧急
Source sql.NullString `db:"source"` // 投诉来源web=网站phone=电话email=邮件other=其他
AttachmentUrls sql.NullString `db:"attachment_urls"` // 附件URL列表JSON数组字符串
}
)
func newComplaintManualModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultComplaintManualModel {
return &defaultComplaintManualModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`complaint_manual`",
}
}
func (m *defaultComplaintManualModel) Insert(ctx context.Context, session sqlx.Session, data *ComplaintManual) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccComplaintManualIdKey := fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, 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, complaintManualRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls)
}, yccComplaintManualIdKey)
}
func (m *defaultComplaintManualModel) insertUUID(data *ComplaintManual) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultComplaintManualModel) FindOne(ctx context.Context, id string) (*ComplaintManual, error) {
yccComplaintManualIdKey := fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, id)
var resp ComplaintManual
err := m.QueryRowCtx(ctx, &resp, yccComplaintManualIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", complaintManualRows, 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 *defaultComplaintManualModel) Update(ctx context.Context, session sqlx.Session, data *ComplaintManual) (sql.Result, error) {
yccComplaintManualIdKey := fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, 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, complaintManualRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls, data.Id)
}, yccComplaintManualIdKey)
}
func (m *defaultComplaintManualModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *ComplaintManual) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccComplaintManualIdKey := fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, 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, complaintManualRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.ComplaintId, data.UserId, data.Subject, data.Priority, data.Source, data.AttachmentUrls, data.Id, oldVersion)
}, yccComplaintManualIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultComplaintManualModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *ComplaintManual) 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 "), "ComplaintManualModel delete err : %+v", err)
}
return nil
}
func (m *defaultComplaintManualModel) 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 *defaultComplaintManualModel) 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 *defaultComplaintManualModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*ComplaintManual, error) {
builder = builder.Columns(complaintManualRows)
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 []*ComplaintManual
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintManualModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintManual, error) {
builder = builder.Columns(complaintManualRows)
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 []*ComplaintManual
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintManualModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*ComplaintManual, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(complaintManualRows)
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 []*ComplaintManual
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultComplaintManualModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*ComplaintManual, error) {
builder = builder.Columns(complaintManualRows)
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 []*ComplaintManual
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintManualModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*ComplaintManual, error) {
builder = builder.Columns(complaintManualRows)
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 []*ComplaintManual
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultComplaintManualModel) 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 *defaultComplaintManualModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultComplaintManualModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
yccComplaintManualIdKey := fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, 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)
}, yccComplaintManualIdKey)
return err
}
func (m *defaultComplaintManualModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccComplaintManualIdPrefix, primary)
}
func (m *defaultComplaintManualModel) 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", complaintManualRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultComplaintManualModel) tableName() string {
return m.table
}

View File

@@ -58,14 +58,15 @@ type (
}
Feature struct {
Id string `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"` // 版本号
ApiId string `db:"api_id"` // API标识
Name string `db:"name"` // 描述
Id string `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"` // 版本号
ApiId string `db:"api_id"` // API标识
Name string `db:"name"` // 描述
WhitelistPrice float64 `db:"whitelist_price"` // 白名单屏蔽价格(单位:元)
}
)
@@ -82,11 +83,11 @@ func (m *defaultFeatureModel) Insert(ctx context.Context, session sqlx.Session,
yccFeatureApiIdKey := fmt.Sprintf("%s%v", cacheYccFeatureApiIdPrefix, data.ApiId)
yccFeatureIdKey := fmt.Sprintf("%s%v", cacheYccFeatureIdPrefix, 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, featureRowsExpectAutoSet)
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, featureRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name)
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name)
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.ApiId, data.Name, data.WhitelistPrice)
}, yccFeatureApiIdKey, yccFeatureIdKey)
}
func (m *defaultFeatureModel) insertUUID(data *Feature) {
@@ -153,9 +154,9 @@ func (m *defaultFeatureModel) Update(ctx context.Context, session sqlx.Session,
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, featureRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.Id)
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.Id)
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id)
}, yccFeatureApiIdKey, yccFeatureIdKey)
}
@@ -176,9 +177,9 @@ func (m *defaultFeatureModel) UpdateWithVersion(ctx context.Context, session sql
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, featureRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.Id, oldVersion)
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.Id, oldVersion)
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.ApiId, newData.Name, newData.WhitelistPrice, newData.Id, oldVersion)
}, yccFeatureApiIdKey, yccFeatureIdKey)
if err != nil {
return err

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 _ UserFeatureWhitelistModel = (*customUserFeatureWhitelistModel)(nil)
type (
// UserFeatureWhitelistModel is an interface to be customized, add more methods here,
// and implement the added methods in customUserFeatureWhitelistModel.
UserFeatureWhitelistModel interface {
userFeatureWhitelistModel
}
customUserFeatureWhitelistModel struct {
*defaultUserFeatureWhitelistModel
}
)
// NewUserFeatureWhitelistModel returns a model for the database table.
func NewUserFeatureWhitelistModel(conn sqlx.SqlConn, c cache.CacheConf) UserFeatureWhitelistModel {
return &customUserFeatureWhitelistModel{
defaultUserFeatureWhitelistModel: newUserFeatureWhitelistModel(conn, c),
}
}

View File

@@ -0,0 +1,484 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
userFeatureWhitelistFieldNames = builder.RawFieldNames(&UserFeatureWhitelist{})
userFeatureWhitelistRows = strings.Join(userFeatureWhitelistFieldNames, ",")
userFeatureWhitelistRowsExpectAutoSet = strings.Join(stringx.Remove(userFeatureWhitelistFieldNames, "`create_time`", "`update_time`"), ",")
userFeatureWhitelistRowsWithPlaceHolder = strings.Join(stringx.Remove(userFeatureWhitelistFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccUserFeatureWhitelistIdPrefix = "cache:ycc:userFeatureWhitelist:id:"
cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix = "cache:ycc:userFeatureWhitelist:idCard:featureApiId:delState:"
cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix = "cache:ycc:userFeatureWhitelist:idCard:featureId:delState:"
cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix = "cache:ycc:userFeatureWhitelist:whitelistOrderId:"
)
type (
userFeatureWhitelistModel interface {
Insert(ctx context.Context, session sqlx.Session, data *UserFeatureWhitelist) (sql.Result, error)
FindOne(ctx context.Context, id string) (*UserFeatureWhitelist, error)
FindOneByIdCardFeatureApiIdDelState(ctx context.Context, idCard string, featureApiId string, delState int64) (*UserFeatureWhitelist, error)
FindOneByIdCardFeatureIdDelState(ctx context.Context, idCard string, featureId string, delState int64) (*UserFeatureWhitelist, error)
FindOneByWhitelistOrderId(ctx context.Context, whitelistOrderId sql.NullString) (*UserFeatureWhitelist, error)
Update(ctx context.Context, session sqlx.Session, data *UserFeatureWhitelist) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *UserFeatureWhitelist) 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 *UserFeatureWhitelist) 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) ([]*UserFeatureWhitelist, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserFeatureWhitelist, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserFeatureWhitelist, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserFeatureWhitelist, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserFeatureWhitelist, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultUserFeatureWhitelistModel struct {
sqlc.CachedConn
table string
}
UserFeatureWhitelist struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
IdCard string `db:"id_card"` // 身份证号(查询对象标识)
FeatureId string `db:"feature_id"` // Feature的UUID
FeatureApiId string `db:"feature_api_id"` // Feature的API标识冗余字段方便查询
UserId string `db:"user_id"` // 操作用户ID代理的user_id以后可扩展为普通用户
OrderId sql.NullString `db:"order_id"` // 关联的查询订单ID可选记录来源
WhitelistOrderId sql.NullString `db:"whitelist_order_id"` // 关联的白名单订单ID用于付费
Amount float64 `db:"amount"` // 费用(单位:元)
Status int64 `db:"status"` // 状态1=生效2=已失效
}
)
func newUserFeatureWhitelistModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserFeatureWhitelistModel {
return &defaultUserFeatureWhitelistModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`user_feature_whitelist`",
}
}
func (m *defaultUserFeatureWhitelistModel) Insert(ctx context.Context, session sqlx.Session, data *UserFeatureWhitelist) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix, data.IdCard, data.FeatureApiId, data.DelState)
yccUserFeatureWhitelistIdCardFeatureIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix, data.IdCard, data.FeatureId, data.DelState)
yccUserFeatureWhitelistIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, data.Id)
yccUserFeatureWhitelistWhitelistOrderIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix, data.WhitelistOrderId)
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, userFeatureWhitelistRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.IdCard, data.FeatureId, data.FeatureApiId, data.UserId, data.OrderId, data.WhitelistOrderId, data.Amount, data.Status)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.IdCard, data.FeatureId, data.FeatureApiId, data.UserId, data.OrderId, data.WhitelistOrderId, data.Amount, data.Status)
}, yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey, yccUserFeatureWhitelistIdCardFeatureIdDelStateKey, yccUserFeatureWhitelistIdKey, yccUserFeatureWhitelistWhitelistOrderIdKey)
}
func (m *defaultUserFeatureWhitelistModel) insertUUID(data *UserFeatureWhitelist) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultUserFeatureWhitelistModel) FindOne(ctx context.Context, id string) (*UserFeatureWhitelist, error) {
yccUserFeatureWhitelistIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, id)
var resp UserFeatureWhitelist
err := m.QueryRowCtx(ctx, &resp, yccUserFeatureWhitelistIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userFeatureWhitelistRows, 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 *defaultUserFeatureWhitelistModel) FindOneByIdCardFeatureApiIdDelState(ctx context.Context, idCard string, featureApiId string, delState int64) (*UserFeatureWhitelist, error) {
yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix, idCard, featureApiId, delState)
var resp UserFeatureWhitelist
err := m.QueryRowIndexCtx(ctx, &resp, yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `id_card` = ? and `feature_api_id` = ? and `del_state` = ? and del_state = ? limit 1", userFeatureWhitelistRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, idCard, featureApiId, delState, globalkey.DelStateNo); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindOneByIdCardFeatureIdDelState(ctx context.Context, idCard string, featureId string, delState int64) (*UserFeatureWhitelist, error) {
yccUserFeatureWhitelistIdCardFeatureIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix, idCard, featureId, delState)
var resp UserFeatureWhitelist
err := m.QueryRowIndexCtx(ctx, &resp, yccUserFeatureWhitelistIdCardFeatureIdDelStateKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `id_card` = ? and `feature_id` = ? and `del_state` = ? and del_state = ? limit 1", userFeatureWhitelistRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, idCard, featureId, delState, globalkey.DelStateNo); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindOneByWhitelistOrderId(ctx context.Context, whitelistOrderId sql.NullString) (*UserFeatureWhitelist, error) {
yccUserFeatureWhitelistWhitelistOrderIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix, whitelistOrderId)
var resp UserFeatureWhitelist
err := m.QueryRowIndexCtx(ctx, &resp, yccUserFeatureWhitelistWhitelistOrderIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `whitelist_order_id` = ? and del_state = ? limit 1", userFeatureWhitelistRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, whitelistOrderId, globalkey.DelStateNo); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) Update(ctx context.Context, session sqlx.Session, newData *UserFeatureWhitelist) (sql.Result, error) {
data, err := m.FindOne(ctx, newData.Id)
if err != nil {
return nil, err
}
yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix, data.IdCard, data.FeatureApiId, data.DelState)
yccUserFeatureWhitelistIdCardFeatureIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix, data.IdCard, data.FeatureId, data.DelState)
yccUserFeatureWhitelistIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, data.Id)
yccUserFeatureWhitelistWhitelistOrderIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix, data.WhitelistOrderId)
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, userFeatureWhitelistRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.IdCard, newData.FeatureId, newData.FeatureApiId, newData.UserId, newData.OrderId, newData.WhitelistOrderId, newData.Amount, newData.Status, newData.Id)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.IdCard, newData.FeatureId, newData.FeatureApiId, newData.UserId, newData.OrderId, newData.WhitelistOrderId, newData.Amount, newData.Status, newData.Id)
}, yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey, yccUserFeatureWhitelistIdCardFeatureIdDelStateKey, yccUserFeatureWhitelistIdKey, yccUserFeatureWhitelistWhitelistOrderIdKey)
}
func (m *defaultUserFeatureWhitelistModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *UserFeatureWhitelist) error {
oldVersion := newData.Version
newData.Version += 1
var sqlResult sql.Result
var err error
data, err := m.FindOne(ctx, newData.Id)
if err != nil {
return err
}
yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix, data.IdCard, data.FeatureApiId, data.DelState)
yccUserFeatureWhitelistIdCardFeatureIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix, data.IdCard, data.FeatureId, data.DelState)
yccUserFeatureWhitelistIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, data.Id)
yccUserFeatureWhitelistWhitelistOrderIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix, data.WhitelistOrderId)
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, userFeatureWhitelistRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.IdCard, newData.FeatureId, newData.FeatureApiId, newData.UserId, newData.OrderId, newData.WhitelistOrderId, newData.Amount, newData.Status, newData.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.IdCard, newData.FeatureId, newData.FeatureApiId, newData.UserId, newData.OrderId, newData.WhitelistOrderId, newData.Amount, newData.Status, newData.Id, oldVersion)
}, yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey, yccUserFeatureWhitelistIdCardFeatureIdDelStateKey, yccUserFeatureWhitelistIdKey, yccUserFeatureWhitelistWhitelistOrderIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultUserFeatureWhitelistModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *UserFeatureWhitelist) 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 "), "UserFeatureWhitelistModel delete err : %+v", err)
}
return nil
}
func (m *defaultUserFeatureWhitelistModel) 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 *defaultUserFeatureWhitelistModel) 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 *defaultUserFeatureWhitelistModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*UserFeatureWhitelist, error) {
builder = builder.Columns(userFeatureWhitelistRows)
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 []*UserFeatureWhitelist
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserFeatureWhitelist, error) {
builder = builder.Columns(userFeatureWhitelistRows)
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 []*UserFeatureWhitelist
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserFeatureWhitelist, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(userFeatureWhitelistRows)
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 []*UserFeatureWhitelist
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserFeatureWhitelist, error) {
builder = builder.Columns(userFeatureWhitelistRows)
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 []*UserFeatureWhitelist
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserFeatureWhitelist, error) {
builder = builder.Columns(userFeatureWhitelistRows)
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 []*UserFeatureWhitelist
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultUserFeatureWhitelistModel) 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 *defaultUserFeatureWhitelistModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultUserFeatureWhitelistModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
data, err := m.FindOne(ctx, id)
if err != nil {
return err
}
yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureApiIdDelStatePrefix, data.IdCard, data.FeatureApiId, data.DelState)
yccUserFeatureWhitelistIdCardFeatureIdDelStateKey := fmt.Sprintf("%s%v:%v:%v", cacheYccUserFeatureWhitelistIdCardFeatureIdDelStatePrefix, data.IdCard, data.FeatureId, data.DelState)
yccUserFeatureWhitelistIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, id)
yccUserFeatureWhitelistWhitelistOrderIdKey := fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistWhitelistOrderIdPrefix, data.WhitelistOrderId)
_, 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)
}, yccUserFeatureWhitelistIdCardFeatureApiIdDelStateKey, yccUserFeatureWhitelistIdCardFeatureIdDelStateKey, yccUserFeatureWhitelistIdKey, yccUserFeatureWhitelistWhitelistOrderIdKey)
return err
}
func (m *defaultUserFeatureWhitelistModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccUserFeatureWhitelistIdPrefix, primary)
}
func (m *defaultUserFeatureWhitelistModel) 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", userFeatureWhitelistRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultUserFeatureWhitelistModel) tableName() string {
return 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 _ WhitelistOrderItemModel = (*customWhitelistOrderItemModel)(nil)
type (
// WhitelistOrderItemModel is an interface to be customized, add more methods here,
// and implement the added methods in customWhitelistOrderItemModel.
WhitelistOrderItemModel interface {
whitelistOrderItemModel
}
customWhitelistOrderItemModel struct {
*defaultWhitelistOrderItemModel
}
)
// NewWhitelistOrderItemModel returns a model for the database table.
func NewWhitelistOrderItemModel(conn sqlx.SqlConn, c cache.CacheConf) WhitelistOrderItemModel {
return &customWhitelistOrderItemModel{
defaultWhitelistOrderItemModel: newWhitelistOrderItemModel(conn, c),
}
}

View File

@@ -0,0 +1,390 @@
// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"reflect"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"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"
"ycc-server/common/globalkey"
)
var (
whitelistOrderItemFieldNames = builder.RawFieldNames(&WhitelistOrderItem{})
whitelistOrderItemRows = strings.Join(whitelistOrderItemFieldNames, ",")
whitelistOrderItemRowsExpectAutoSet = strings.Join(stringx.Remove(whitelistOrderItemFieldNames, "`create_time`", "`update_time`"), ",")
whitelistOrderItemRowsWithPlaceHolder = strings.Join(stringx.Remove(whitelistOrderItemFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheYccWhitelistOrderItemIdPrefix = "cache:ycc:whitelistOrderItem:id:"
)
type (
whitelistOrderItemModel interface {
Insert(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) (sql.Result, error)
FindOne(ctx context.Context, id string) (*WhitelistOrderItem, error)
Update(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) (sql.Result, error)
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) 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 *WhitelistOrderItem) 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) ([]*WhitelistOrderItem, error)
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*WhitelistOrderItem, error)
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*WhitelistOrderItem, int64, error)
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*WhitelistOrderItem, error)
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*WhitelistOrderItem, error)
Delete(ctx context.Context, session sqlx.Session, id string) error
}
defaultWhitelistOrderItemModel struct {
sqlc.CachedConn
table string
}
WhitelistOrderItem struct {
Id string `db:"id"` // UUID主键
CreateTime time.Time `db:"create_time"` // 创建时间
UpdateTime time.Time `db:"update_time"` // 更新时间
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
DelState int64 `db:"del_state"` // 删除状态0=未删除1=已删除
Version int64 `db:"version"` // 版本号(乐观锁)
OrderId string `db:"order_id"` // 订单ID关联whitelist_order.id
FeatureId string `db:"feature_id"` // Feature的UUID
FeatureApiId string `db:"feature_api_id"` // Feature的API标识冗余
FeatureName string `db:"feature_name"` // Feature的名称冗余
Price float64 `db:"price"` // 单价(单位:元)
}
)
func newWhitelistOrderItemModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultWhitelistOrderItemModel {
return &defaultWhitelistOrderItemModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`whitelist_order_item`",
}
}
func (m *defaultWhitelistOrderItemModel) Insert(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) (sql.Result, error) {
data.DelState = globalkey.DelStateNo
m.insertUUID(data)
yccWhitelistOrderItemIdKey := fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, 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, whitelistOrderItemRowsExpectAutoSet)
if session != nil {
return session.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price)
}
return conn.ExecCtx(ctx, query, data.Id, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price)
}, yccWhitelistOrderItemIdKey)
}
func (m *defaultWhitelistOrderItemModel) insertUUID(data *WhitelistOrderItem) {
t := reflect.TypeOf(data).Elem()
v := reflect.ValueOf(data).Elem()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Tag.Get("db") == "id" {
f := v.Field(i)
if f.IsValid() && f.CanSet() && f.Kind() == reflect.String {
if f.String() == "" {
f.SetString(uuid.NewString())
}
}
break
}
}
}
func (m *defaultWhitelistOrderItemModel) FindOne(ctx context.Context, id string) (*WhitelistOrderItem, error) {
yccWhitelistOrderItemIdKey := fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, id)
var resp WhitelistOrderItem
err := m.QueryRowCtx(ctx, &resp, yccWhitelistOrderItemIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", whitelistOrderItemRows, 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 *defaultWhitelistOrderItemModel) Update(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) (sql.Result, error) {
yccWhitelistOrderItemIdKey := fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, 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, whitelistOrderItemRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price, data.Id)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price, data.Id)
}, yccWhitelistOrderItemIdKey)
}
func (m *defaultWhitelistOrderItemModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) error {
oldVersion := data.Version
data.Version += 1
var sqlResult sql.Result
var err error
yccWhitelistOrderItemIdKey := fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, 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, whitelistOrderItemRowsWithPlaceHolder)
if session != nil {
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price, data.Id, oldVersion)
}
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.OrderId, data.FeatureId, data.FeatureApiId, data.FeatureName, data.Price, data.Id, oldVersion)
}, yccWhitelistOrderItemIdKey)
if err != nil {
return err
}
updateCount, err := sqlResult.RowsAffected()
if err != nil {
return err
}
if updateCount == 0 {
return ErrNoRowsUpdate
}
return nil
}
func (m *defaultWhitelistOrderItemModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *WhitelistOrderItem) 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 "), "WhitelistOrderItemModel delete err : %+v", err)
}
return nil
}
func (m *defaultWhitelistOrderItemModel) 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 *defaultWhitelistOrderItemModel) 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 *defaultWhitelistOrderItemModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*WhitelistOrderItem, error) {
builder = builder.Columns(whitelistOrderItemRows)
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 []*WhitelistOrderItem
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultWhitelistOrderItemModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*WhitelistOrderItem, error) {
builder = builder.Columns(whitelistOrderItemRows)
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 []*WhitelistOrderItem
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultWhitelistOrderItemModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*WhitelistOrderItem, int64, error) {
total, err := m.FindCount(ctx, builder, "id")
if err != nil {
return nil, 0, err
}
builder = builder.Columns(whitelistOrderItemRows)
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 []*WhitelistOrderItem
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, total, nil
default:
return nil, total, err
}
}
func (m *defaultWhitelistOrderItemModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*WhitelistOrderItem, error) {
builder = builder.Columns(whitelistOrderItemRows)
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 []*WhitelistOrderItem
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultWhitelistOrderItemModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*WhitelistOrderItem, error) {
builder = builder.Columns(whitelistOrderItemRows)
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 []*WhitelistOrderItem
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
switch err {
case nil:
return resp, nil
default:
return nil, err
}
}
func (m *defaultWhitelistOrderItemModel) 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 *defaultWhitelistOrderItemModel) SelectBuilder() squirrel.SelectBuilder {
return squirrel.Select().From(m.table)
}
func (m *defaultWhitelistOrderItemModel) Delete(ctx context.Context, session sqlx.Session, id string) error {
yccWhitelistOrderItemIdKey := fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, 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)
}, yccWhitelistOrderItemIdKey)
return err
}
func (m *defaultWhitelistOrderItemModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheYccWhitelistOrderItemIdPrefix, primary)
}
func (m *defaultWhitelistOrderItemModel) 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", whitelistOrderItemRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
}
func (m *defaultWhitelistOrderItemModel) tableName() string {
return 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 _ WhitelistOrderModel = (*customWhitelistOrderModel)(nil)
type (
// WhitelistOrderModel is an interface to be customized, add more methods here,
// and implement the added methods in customWhitelistOrderModel.
WhitelistOrderModel interface {
whitelistOrderModel
}
customWhitelistOrderModel struct {
*defaultWhitelistOrderModel
}
)
// NewWhitelistOrderModel returns a model for the database table.
func NewWhitelistOrderModel(conn sqlx.SqlConn, c cache.CacheConf) WhitelistOrderModel {
return &customWhitelistOrderModel{
defaultWhitelistOrderModel: newWhitelistOrderModel(conn, c),
}
}

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