Compare commits
35 Commits
19c82817ba
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c1eab396ac | |||
| 72fb221d1f | |||
| be63c01987 | |||
| 3833f67b03 | |||
| e981a1e069 | |||
| f8155eb92f | |||
| 8874b0b96c | |||
| a6053f3f9b | |||
| 265061ad46 | |||
| 3a324e6656 | |||
| 184d61a5c7 | |||
| f2921c91a1 | |||
| 0bdaa141bd | |||
| f6675cc6d9 | |||
| 4c9bdb84f3 | |||
| 84c9965d9a | |||
| 85aed39ff9 | |||
| 3a4e75f375 | |||
| f1f0c5dbd2 | |||
| b98ad2af2c | |||
| 99bbdea946 | |||
| 45f6f48fb9 | |||
| 771e051868 | |||
| 2c885599ad | |||
| 518f7a3a8c | |||
| 524c94de7c | |||
| c6a79e300c | |||
| 0ebec3760d | |||
| 1967a02d1a | |||
| 404745fe18 | |||
| a38ca5bd07 | |||
| a8a1c79094 | |||
| 6ba7c3fc72 | |||
| a9e6609953 | |||
| 5a530c4679 |
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.22.4-alpine AS builder
|
||||
FROM golang:1.23.4-alpine AS builder
|
||||
|
||||
LABEL stage=gobuilder
|
||||
|
||||
@@ -15,7 +15,8 @@ ADD go.sum .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
COPY app/main/api/etc /app/etc
|
||||
RUN go build -ldflags="-s -w" -o /app/main app/main//api/main.go
|
||||
COPY app/main/api/static /app/static
|
||||
RUN go build -ldflags="-s -w" -o /app/main app/main/api/main.go
|
||||
|
||||
|
||||
FROM scratch
|
||||
@@ -25,7 +26,7 @@ COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/S
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/main /app/user
|
||||
COPY --from=builder /app/main /app/main
|
||||
COPY --from=builder /app/etc /app/etc
|
||||
|
||||
CMD ["./user", "-f", "etc/main.yaml"]
|
||||
COPY --from=builder /app/static /app/static
|
||||
CMD ["./main", "-f", "etc/main.yaml"]
|
||||
|
||||
@@ -7,6 +7,22 @@ info (
|
||||
email: "2440983361@qq.com"
|
||||
version: "v1"
|
||||
)
|
||||
@server (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
)
|
||||
service main {
|
||||
// 获取推广二维码海报
|
||||
@handler GetAgentPromotionQrcode
|
||||
get /promotion/qrcode (GetAgentPromotionQrcodeReq)
|
||||
}
|
||||
|
||||
type (
|
||||
GetAgentPromotionQrcodeReq{
|
||||
QrcodeType string `form:"qrcode_type"`
|
||||
QrcodeUrl string `form:"qrcode_url"`
|
||||
}
|
||||
)
|
||||
|
||||
// 代理服务基本类型定义
|
||||
type AgentProductConfig {
|
||||
@@ -35,7 +51,6 @@ type ProductConfig {
|
||||
PriceRangeMin float64 `json:"price_range_min"`
|
||||
PriceRangeMax float64 `json:"price_range_max"`
|
||||
}
|
||||
|
||||
@server (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
@@ -46,6 +61,17 @@ service main {
|
||||
@handler GetAgentInfo
|
||||
get /info returns (AgentInfoResp)
|
||||
|
||||
@handler GetAgentRevenueInfo
|
||||
get /revenue (GetAgentRevenueInfoReq) returns (GetAgentRevenueInfoResp)
|
||||
}
|
||||
|
||||
@server (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
// 查询代理申请状态
|
||||
@handler GetAgentAuditStatus
|
||||
get /audit/status returns (AgentAuditStatusResp)
|
||||
@@ -93,6 +119,7 @@ type (
|
||||
AgentGeneratingLinkResp {
|
||||
LinkIdentifier string `json:"link_identifier"`
|
||||
}
|
||||
|
||||
AgentProductConfigResp {
|
||||
AgentProductConfig []AgentProductConfig
|
||||
}
|
||||
@@ -168,6 +195,7 @@ type (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@handler GetAgentMembershipProductConfig
|
||||
@@ -203,10 +231,9 @@ type (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@handler GetAgentRevenueInfo
|
||||
get /revenue (GetAgentRevenueInfoReq) returns (GetAgentRevenueInfoResp)
|
||||
|
||||
@handler GetAgentCommission
|
||||
get /commission (GetCommissionReq) returns (GetCommissionResp)
|
||||
@@ -222,6 +249,9 @@ service main {
|
||||
|
||||
@handler ActivateAgentMembership
|
||||
post /membership/activate (AgentActivateMembershipReq) returns (AgentActivateMembershipResp)
|
||||
|
||||
@handler GetAgentWithdrawalTaxExemption
|
||||
get /withdrawal/tax/exemption (GetWithdrawalTaxExemptionReq) returns (GetWithdrawalTaxExemptionResp)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -321,11 +351,20 @@ type (
|
||||
AgentActivateMembershipResp {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
GetWithdrawalTaxExemptionReq {
|
||||
}
|
||||
GetWithdrawalTaxExemptionResp {
|
||||
TotalExemptionAmount float64 `json:"total_exemption_amount"`
|
||||
UsedExemptionAmount float64 `json:"used_exemption_amount"`
|
||||
RemainingExemptionAmount float64 `json:"remaining_exemption_amount"`
|
||||
TaxRate float64 `json:"tax_rate"`
|
||||
}
|
||||
)
|
||||
|
||||
@server (
|
||||
prefix: api/v1/agent
|
||||
group: agent
|
||||
middleware: AuthInterceptor
|
||||
)
|
||||
service main {
|
||||
// 提交代理申请
|
||||
@@ -335,7 +374,6 @@ service main {
|
||||
// 获取推广标识数据
|
||||
@handler GetLinkData
|
||||
get /link (GetLinkDataReq) returns (GetLinkDataResp)
|
||||
|
||||
}
|
||||
|
||||
type (
|
||||
|
||||
@@ -30,7 +30,7 @@ service main {
|
||||
prefix: api/v1
|
||||
group: pay
|
||||
jwt: JwtAuth
|
||||
middleware: SourceInterceptor
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
// 支付
|
||||
|
||||
@@ -26,6 +26,7 @@ type Product {
|
||||
prefix: api/v1/product
|
||||
group: product
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@handler GetProductByID
|
||||
|
||||
@@ -75,6 +75,7 @@ type (
|
||||
prefix: api/v1
|
||||
group: query
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@doc "query service"
|
||||
@@ -86,6 +87,7 @@ service main {
|
||||
prefix: api/v1
|
||||
group: query
|
||||
jwt: JwtAuth
|
||||
middleware: UserAuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@doc "获取查询临时订单"
|
||||
|
||||
@@ -14,6 +14,7 @@ type User {
|
||||
Id int64 `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
NickName string `json:"nickName"`
|
||||
UserType int64 `json:"userType"`
|
||||
}
|
||||
|
||||
//no need login
|
||||
@@ -22,22 +23,10 @@ type User {
|
||||
group: user
|
||||
)
|
||||
service main {
|
||||
@doc "register"
|
||||
@handler register
|
||||
post /user/register (RegisterReq) returns (RegisterResp)
|
||||
|
||||
@doc "mobile login"
|
||||
@handler mobileLogin
|
||||
post /user/mobileLogin (MobileLoginReq) returns (MobileLoginResp)
|
||||
|
||||
@doc "mobile code login"
|
||||
@handler mobileCodeLogin
|
||||
post /user/mobileCodeLogin (MobileCodeLoginReq) returns (MobileCodeLoginResp)
|
||||
|
||||
@doc "agent mobile code login"
|
||||
@handler agentMobileCodeLogin
|
||||
post /user/agent_mobile_code_login (MobileCodeLoginReq) returns (MobileCodeLoginResp)
|
||||
|
||||
@doc "wechat mini auth"
|
||||
@handler wxMiniAuth
|
||||
post /user/wxMiniAuth (WXMiniAuthReq) returns (WXMiniAuthResp)
|
||||
@@ -47,31 +36,6 @@ service main {
|
||||
post /user/wxh5Auth (WXH5AuthReq) returns (WXH5AuthResp)
|
||||
}
|
||||
|
||||
type (
|
||||
RegisterReq {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
RegisterResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
MobileLoginReq {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
MobileLoginResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
MobileCodeLoginReq {
|
||||
Mobile string `json:"mobile"`
|
||||
@@ -86,9 +50,7 @@ type (
|
||||
|
||||
type (
|
||||
WXMiniAuthReq {
|
||||
Code string `json:"code"`
|
||||
IV string `json:"iv"`
|
||||
EncryptedData string `json:"encryptedData"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
WXMiniAuthResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
@@ -107,6 +69,17 @@ type (
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
)
|
||||
//need login
|
||||
@server (
|
||||
prefix: api/v1
|
||||
group: user
|
||||
middleware: AuthInterceptor
|
||||
)
|
||||
service main {
|
||||
@doc "绑定手机号"
|
||||
@handler bindMobile
|
||||
post /user/bindMobile (BindMobileReq) returns (BindMobileResp)
|
||||
}
|
||||
|
||||
//need login
|
||||
@server (
|
||||
@@ -126,9 +99,6 @@ service main {
|
||||
@handler cancelOut
|
||||
post /user/cancelOut
|
||||
|
||||
@doc "绑定手机号"
|
||||
@handler bindMobile
|
||||
post /user/bindMobile (BindMobileReq) returns (BindMobileResp)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -141,6 +111,9 @@ type (
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
BindMobileResp {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -45,10 +45,11 @@ Wxpay:
|
||||
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
|
||||
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
|
||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||
MchPublicKeyID: "XXXXXX"
|
||||
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601"
|
||||
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
||||
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781"
|
||||
NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback"
|
||||
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback"
|
||||
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/refund_callback"
|
||||
Applepay:
|
||||
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||
@@ -64,6 +65,9 @@ SystemConfig:
|
||||
WechatH5:
|
||||
AppID: "wx442ee1ac1ee75917"
|
||||
AppSecret: "c80474909db42f63913b7a307b3bee17"
|
||||
WechatMini:
|
||||
AppID: "wx5bacc94add2da981" # 小程序的AppID
|
||||
AppSecret: "48a2c1e8ff1b7d4c0ff82fbefa64d2d0" # 小程序的AppSecret
|
||||
CloudAuth:
|
||||
AccessKeyId: "LTAI5tSWnaq1kvUawsV4ayL8"
|
||||
AccessKeySecret: "fSGdUzm4TGtkus9rUVzipSOhEtimG5"
|
||||
@@ -78,3 +82,6 @@ AdminConfig:
|
||||
RefreshAfter: 302400
|
||||
AdminPromotion:
|
||||
URLDomain: "https://quannengcha.com/p"
|
||||
TaxConfig:
|
||||
TaxRate: 0.2
|
||||
TaxExemptionAmount: 800.00
|
||||
|
||||
@@ -46,10 +46,11 @@ Wxpay:
|
||||
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
|
||||
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
|
||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||
MchPublicKeyID: "XXXXXX"
|
||||
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601"
|
||||
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
||||
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781"
|
||||
NotifyUrl: "https://www.quannengcha.com/api/v1/pay/wechat/callback"
|
||||
RefundNotifyUrl: "https://www.quannengcha.com/api/v1/wechat/refund_callback"
|
||||
RefundNotifyUrl: "https://www.quannengcha.com/api/v1/pay/wechat/refund_callback"
|
||||
Applepay:
|
||||
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||
@@ -65,6 +66,9 @@ SystemConfig:
|
||||
WechatH5:
|
||||
AppID: "wx442ee1ac1ee75917"
|
||||
AppSecret: "c80474909db42f63913b7a307b3bee17"
|
||||
WechatMini:
|
||||
AppID: "wx5bacc94add2da981" # 小程序的AppID
|
||||
AppSecret: "48a2c1e8ff1b7d4c0ff82fbefa64d2d0" # 小程序的AppSecret
|
||||
CloudAuth:
|
||||
AccessKeyId: "LTAI5tSWnaq1kvUawsV4ayL8"
|
||||
AccessKeySecret: "fSGdUzm4TGtkus9rUVzipSOhEtimG5"
|
||||
@@ -79,3 +83,6 @@ AdminConfig:
|
||||
RefreshAfter: 302400
|
||||
AdminPromotion:
|
||||
URLDomain: "https://quannengcha.com/p"
|
||||
TaxConfig:
|
||||
TaxRate: 0.2
|
||||
TaxExemptionAmount: 800.00
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsNH2kztg9gybkuulreL+
|
||||
BMyakxmKTFqrujYLm+S40v64KbNH3+sWdf1XR59vWjSvGWo+BAbuSIHNmIIFMFKE
|
||||
sUxqHAYbta4oD9Ogr0+88drnXv+AA6vxQML0KaaTuHessvUhGC5GEUxa+TFefO9/
|
||||
EjbwL1E/XQ8oBkxHJO6RjKevuts39RjEyocnNhV7m8RP6WIBQeJDXhbfO1etcwdJ
|
||||
B2yQ1eoPK9kGAqQ7wL4pDXrLXMfS1DXlNHsLf4if7rwu3fibk/qfkKdtmqvUw39f
|
||||
tCKZRiexIq6ad9kTTjouXUU5EMRAn3ocRvNzCD4RaW1qVYMxFQ8AraQ8W3MXlPeL
|
||||
EQIDAQAB
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwSy7dS/ICZV38tI0HxM
|
||||
SAIE7+Ug92qryuNlkNyaNDRjfsykHsrPCSsUUQEZblBNmZOLfLQxmAaWC+cQqWCv
|
||||
zfy4rXGAHE1widWFkHGzQzaw6cB0VdDXatK9yAt1PgXdp5jzBRzOn9Z3u4t0s771
|
||||
2zjuxCnLxMq84DovNgh2y0LBiuorWbtuTFTd8SXUGk2Jyuojq/02U3KTuyh+7SmW
|
||||
ffJXKrzhrKwSpGh59e/fFxqX2xGlVoJ1kdohMZPo/7k+e5jP7qjrf93l7JVeUKYa
|
||||
V27hNVowJ4oho21WVCJ1AYo41IbPJWI+6WxlaVeoR4zKix0Mb2timaWayyLoN53y
|
||||
aQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
3
app/main/api/etc/user-api.yaml
Normal file
3
app/main/api/etc/user-api.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
Name: user-api
|
||||
Host: 0.0.0.0
|
||||
Port: 8888
|
||||
@@ -20,10 +20,12 @@ type Config struct {
|
||||
YushanConfig YushanConfig
|
||||
SystemConfig SystemConfig
|
||||
WechatH5 WechatH5Config
|
||||
WechatMini WechatMiniConfig // 添加小程序配置
|
||||
CloudAuth CloudAuthConfig
|
||||
Query QueryConfig
|
||||
AdminConfig AdminConfig
|
||||
AdminPromotion AdminPromotion
|
||||
TaxConfig TaxConfig
|
||||
}
|
||||
|
||||
// JwtAuth 用于 JWT 鉴权配置
|
||||
@@ -63,6 +65,7 @@ type WxpayConfig struct {
|
||||
MchPrivateKeyPath string
|
||||
MchPublicKeyID string
|
||||
MchPublicKeyPath string
|
||||
MchPlatformRAS string
|
||||
NotifyUrl string
|
||||
RefundNotifyUrl string
|
||||
}
|
||||
@@ -96,6 +99,13 @@ type WechatH5Config struct {
|
||||
AppID string
|
||||
AppSecret string
|
||||
}
|
||||
|
||||
// WechatMiniConfig 小程序配置
|
||||
type WechatMiniConfig struct {
|
||||
AppID string
|
||||
AppSecret string
|
||||
}
|
||||
|
||||
type CloudAuthConfig struct {
|
||||
AccessKeyId string
|
||||
AccessKeySecret string
|
||||
@@ -115,3 +125,7 @@ type AdminConfig struct {
|
||||
type AdminPromotion struct {
|
||||
URLDomain string
|
||||
}
|
||||
type TaxConfig struct {
|
||||
TaxRate float64
|
||||
TaxExemptionAmount float64
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/logic/agent"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func GetAgentPromotionQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.GetAgentPromotionQrcodeReq
|
||||
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
|
||||
}
|
||||
|
||||
// 注意:这里传入了 ResponseWriter,用于直接写入图片数据
|
||||
l := agent.NewGetAgentPromotionQrcodeLogic(r.Context(), svcCtx, w)
|
||||
err := l.GetAgentPromotionQrcode(&req)
|
||||
if err != nil {
|
||||
// 如果处理过程中出错,返回JSON错误响应
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
// 成功时,图片数据已经通过logic直接写入ResponseWriter,不需要额外处理
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
package user
|
||||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/logic/user"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"qnc-server/app/main/api/internal/logic/agent"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func AgentMobileCodeLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func GetAgentWithdrawalTaxExemptionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.MobileCodeLoginReq
|
||||
var req types.GetWithdrawalTaxExemptionReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
result.ParamErrorResult(r, w, err)
|
||||
return
|
||||
@@ -23,8 +22,8 @@ func AgentMobileCodeLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
result.ParamValidateErrorResult(r, w, err)
|
||||
return
|
||||
}
|
||||
l := user.NewAgentMobileCodeLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.AgentMobileCodeLogin(&req)
|
||||
l := agent.NewGetAgentWithdrawalTaxExemptionLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetAgentWithdrawalTaxExemption(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -518,111 +518,146 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/audit/status",
|
||||
Handler: agent.GetAgentAuditStatusHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/generating_link",
|
||||
Handler: agent.GeneratingLinkHandler(serverCtx),
|
||||
Path: "/promotion/qrcode",
|
||||
Handler: agent.GetAgentPromotionQrcodeHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/info",
|
||||
Handler: agent.GetAgentInfoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/product_config",
|
||||
Handler: agent.GetAgentProductConfigHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/real_name",
|
||||
Handler: agent.AgentRealNameHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/subordinate/contribution/detail",
|
||||
Handler: agent.GetAgentSubordinateContributionDetailHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/subordinate/list",
|
||||
Handler: agent.GetAgentSubordinateListHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/membership/save_user_config",
|
||||
Handler: agent.SaveAgentMembershipUserConfigHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/membership/user_config",
|
||||
Handler: agent.GetAgentMembershipProductConfigHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/commission",
|
||||
Handler: agent.GetAgentCommissionHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/membership/activate",
|
||||
Handler: agent.ActivateAgentMembershipHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/revenue",
|
||||
Handler: agent.GetAgentRevenueInfoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/rewards",
|
||||
Handler: agent.GetAgentRewardsHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/withdrawal",
|
||||
Handler: agent.GetAgentWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/withdrawal",
|
||||
Handler: agent.AgentWithdrawalHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/apply",
|
||||
Handler: agent.ApplyForAgentHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/link",
|
||||
Handler: agent.GetLinkDataHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/audit/status",
|
||||
Handler: agent.GetAgentAuditStatusHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/generating_link",
|
||||
Handler: agent.GeneratingLinkHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/product_config",
|
||||
Handler: agent.GetAgentProductConfigHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/real_name",
|
||||
Handler: agent.AgentRealNameHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/subordinate/contribution/detail",
|
||||
Handler: agent.GetAgentSubordinateContributionDetailHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/subordinate/list",
|
||||
Handler: agent.GetAgentSubordinateListHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/membership/save_user_config",
|
||||
Handler: agent.SaveAgentMembershipUserConfigHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/membership/user_config",
|
||||
Handler: agent.GetAgentMembershipProductConfigHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/commission",
|
||||
Handler: agent.GetAgentCommissionHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/membership/activate",
|
||||
Handler: agent.ActivateAgentMembershipHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/rewards",
|
||||
Handler: agent.GetAgentRewardsHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/withdrawal",
|
||||
Handler: agent.GetAgentWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/withdrawal",
|
||||
Handler: agent.AgentWithdrawalHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/withdrawal/tax/exemption",
|
||||
Handler: agent.GetAgentWithdrawalTaxExemptionHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/apply",
|
||||
Handler: agent.ApplyForAgentHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/link",
|
||||
Handler: agent.GetLinkDataHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithPrefix("/api/v1/agent"),
|
||||
)
|
||||
|
||||
@@ -714,7 +749,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SourceInterceptor},
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
@@ -743,18 +778,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
Handler: product.GetProductByIDHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/en/:product_en",
|
||||
Handler: product.GetProductByEnHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
Handler: product.GetProductByIDHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/en/:product_en",
|
||||
Handler: product.GetProductByEnHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/product"),
|
||||
)
|
||||
@@ -791,75 +829,81 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// query service
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/service/:product",
|
||||
Handler: query.QueryServiceHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
// query service
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/service/:product",
|
||||
Handler: query.QueryServiceHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// 确认查询状态
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/confirm_state",
|
||||
Handler: query.ConfirmQueryStateHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 生成分享链接
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/generate_share_link",
|
||||
Handler: query.QueryGenerateShareLinkHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询列表
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/list",
|
||||
Handler: query.QueryListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询详情 按订单号 付款查询时
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/orderId/:order_id",
|
||||
Handler: query.QueryDetailByOrderIdHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询详情 按订单号
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/orderNo/:order_no",
|
||||
Handler: query.QueryDetailByOrderNoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 获取查询临时订单
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/provisional_order/:id",
|
||||
Handler: query.QueryProvisionalOrderHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 补查
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/recheck/:query_id",
|
||||
Handler: query.QueryRecheckHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 重试查询
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/retry/:id",
|
||||
Handler: query.QueryRetryHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 更新查询数据
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/update_data",
|
||||
Handler: query.UpdateQueryDataHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.UserAuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
// 确认查询状态
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/confirm_state",
|
||||
Handler: query.ConfirmQueryStateHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 生成分享链接
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/generate_share_link",
|
||||
Handler: query.QueryGenerateShareLinkHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询列表
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/list",
|
||||
Handler: query.QueryListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询详情 按订单号 付款查询时
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/orderId/:order_id",
|
||||
Handler: query.QueryDetailByOrderIdHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 查询详情 按订单号
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/orderNo/:order_no",
|
||||
Handler: query.QueryDetailByOrderNoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 获取查询临时订单
|
||||
Method: http.MethodGet,
|
||||
Path: "/query/provisional_order/:id",
|
||||
Handler: query.QueryProvisionalOrderHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 补查
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/recheck/:query_id",
|
||||
Handler: query.QueryRecheckHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 重试查询
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/retry/:id",
|
||||
Handler: query.QueryRetryHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// 更新查询数据
|
||||
Method: http.MethodPost,
|
||||
Path: "/query/update_data",
|
||||
Handler: query.UpdateQueryDataHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
@@ -889,30 +933,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// agent mobile code login
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/agent_mobile_code_login",
|
||||
Handler: user.AgentMobileCodeLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// mobile code login
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/mobileCodeLogin",
|
||||
Handler: user.MobileCodeLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// mobile login
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/mobileLogin",
|
||||
Handler: user.MobileLoginHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// register
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/register",
|
||||
Handler: user.RegisterHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
// wechat mini auth
|
||||
Method: http.MethodPost,
|
||||
@@ -929,14 +955,23 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthInterceptor},
|
||||
[]rest.Route{
|
||||
{
|
||||
// 绑定手机号
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/bindMobile",
|
||||
Handler: user.BindMobileHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithPrefix("/api/v1"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
// 绑定手机号
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/bindMobile",
|
||||
Handler: user.BindMobileHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/user/cancelOut",
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/logic/user"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func MobileLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.MobileLoginReq
|
||||
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 := user.NewMobileLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.MobileLogin(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/logic/user"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/result"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func RegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.RegisterReq
|
||||
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 := user.NewRegisterLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Register(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
@@ -71,7 +72,14 @@ func (l *AdminLoginLogic) AdminLogin(req *types.AdminLoginReq) (resp *types.Admi
|
||||
// 5. 生成token
|
||||
refreshToken := l.svcCtx.Config.JwtAuth.RefreshAfter
|
||||
expiresAt := l.svcCtx.Config.JwtAuth.AccessExpire
|
||||
token, err := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, expiresAt)
|
||||
claims := jwtx.JwtClaims{
|
||||
UserId: user.Id,
|
||||
AgentId: 0,
|
||||
Platform: model.PlatformAdmin,
|
||||
UserType: model.UserTypeAdmin,
|
||||
IsAgent: model.AgentStatusNo,
|
||||
}
|
||||
token, err := jwtx.GenerateJwtToken(claims, l.svcCtx.Config.JwtAuth.AccessSecret, expiresAt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("生成token失败"), "用户登录, 生成token失败, 用户名: %s", req.Username)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
const (
|
||||
PaymentPlatformAlipay = "alipay"
|
||||
PaymentPlatformWechat = "wechat"
|
||||
OrderStatusPaid = "paid"
|
||||
RefundNoPrefix = "refund-"
|
||||
)
|
||||
|
||||
type AdminRefundOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
@@ -31,62 +38,157 @@ func NewAdminRefundOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
|
||||
}
|
||||
|
||||
func (l *AdminRefundOrderLogic) AdminRefundOrder(req *types.AdminRefundOrderReq) (resp *types.AdminRefundOrderResp, err error) {
|
||||
// 获取订单信息
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, req.Id)
|
||||
// 获取并验证订单
|
||||
order, err := l.getAndValidateOrder(req.Id, req.RefundAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 根据支付平台处理退款
|
||||
switch order.PaymentPlatform {
|
||||
case PaymentPlatformAlipay:
|
||||
return l.handleAlipayRefund(order, req)
|
||||
case PaymentPlatformWechat:
|
||||
return l.handleWechatRefund(order, req)
|
||||
default:
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("不支持的支付平台"), "AdminRefundOrder, 不支持的支付平台: %s", order.PaymentPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
// getAndValidateOrder 获取并验证订单信息
|
||||
func (l *AdminRefundOrderLogic) getAndValidateOrder(orderId int64, refundAmount float64) (*model.Order, error) {
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, orderId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "AdminRefundOrder, 查询订单失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 检查订单状态
|
||||
if order.Status != "paid" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法退款"), "AdminRefundOrder, 订单状态不正确,无法退款 err: %v", err)
|
||||
if order.Status != OrderStatusPaid {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单状态不正确,无法退款"), "AdminRefundOrder, 订单状态: %s", order.Status)
|
||||
}
|
||||
|
||||
// 检查退款金额
|
||||
if req.RefundAmount > order.Amount {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("退款金额不能大于订单金额"), "AdminRefundOrder, 退款金额不能大于订单金额 err: %v", err)
|
||||
if refundAmount > order.Amount {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("退款金额不能大于订单金额"), "AdminRefundOrder, 退款金额: %f, 订单金额: %f", refundAmount, order.Amount)
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
// handleAlipayRefund 处理支付宝退款
|
||||
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
|
||||
// 调用支付宝退款接口
|
||||
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)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 支付宝退款失败 err: %v", err)
|
||||
}
|
||||
|
||||
refundNo := l.generateRefundNo(order.OrderNo)
|
||||
|
||||
if refundResp.IsSuccess() {
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 创建退款记录
|
||||
refund := &model.OrderRefund{
|
||||
RefundNo: fmt.Sprintf("refund-%s", order.OrderNo),
|
||||
PlatformRefundId: sql.NullString{String: refundResp.TradeNo, Valid: true},
|
||||
OrderId: order.Id,
|
||||
UserId: order.UserId,
|
||||
ProductId: order.ProductId,
|
||||
RefundAmount: req.RefundAmount,
|
||||
RefundReason: sql.NullString{String: req.RefundReason, Valid: true},
|
||||
Status: model.OrderRefundStatusPending,
|
||||
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
if _, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund); err != nil {
|
||||
return fmt.Errorf("创建退款记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
order.Status = model.OrderStatusRefunded
|
||||
order.RefundTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// 支付宝退款成功,创建成功记录
|
||||
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 退款失败 err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminRefundOrderResp{
|
||||
Status: model.OrderStatusRefunded,
|
||||
RefundNo: fmt.Sprintf("refund-%s", order.OrderNo),
|
||||
RefundNo: refundNo,
|
||||
Amount: req.RefundAmount,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("退款失败, : %v", refundResp.Msg)), "AdminRefundOrder, 退款失败 err: %v", err)
|
||||
// 支付宝退款失败,创建失败记录但不更新订单状态
|
||||
err = l.createRefundRecordOnly(order, req, refundNo, refundResp.TradeNo, model.OrderRefundStatusFailed)
|
||||
if err != nil {
|
||||
logx.Errorf("创建退款失败记录时出错: %v", err)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("退款失败: %v", refundResp.Msg)), "AdminRefundOrder, 支付宝退款失败")
|
||||
}
|
||||
}
|
||||
|
||||
// handleWechatRefund 处理微信退款
|
||||
func (l *AdminRefundOrderLogic) handleWechatRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
|
||||
// 调用微信退款接口
|
||||
err := l.svcCtx.WechatPayService.WeChatRefund(l.ctx, order.OrderNo, req.RefundAmount, order.Amount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 微信退款失败 err: %v", err)
|
||||
}
|
||||
|
||||
// 微信退款是异步的,创建pending状态的退款记录
|
||||
refundNo := l.generateRefundNo(order.OrderNo)
|
||||
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, "", model.OrderStatusRefunding, model.OrderRefundStatusPending)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AdminRefundOrderResp{
|
||||
Status: model.OrderRefundStatusPending,
|
||||
RefundNo: refundNo,
|
||||
Amount: req.RefundAmount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 创建退款记录
|
||||
refund := &model.OrderRefund{
|
||||
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)
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
order.Status = orderStatus
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// createRefundRecordOnly 仅创建退款记录,不更新订单状态(用于退款失败的情况)
|
||||
func (l *AdminRefundOrderLogic) createRefundRecordOnly(order *model.Order, req *types.AdminRefundOrderReq, refundNo, platformRefundId, refundStatus string) error {
|
||||
refund := &model.OrderRefund{
|
||||
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 {
|
||||
return fmt.Errorf("创建退款记录失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateRefundNo 生成退款单号
|
||||
func (l *AdminRefundOrderLogic) generateRefundNo(orderNo string) string {
|
||||
return fmt.Sprintf("%s%s", RefundNoPrefix, orderNo)
|
||||
}
|
||||
|
||||
// createNullString 创建 sql.NullString
|
||||
func (l *AdminRefundOrderLogic) createNullString(value string) sql.NullString {
|
||||
return sql.NullString{
|
||||
String: value,
|
||||
Valid: value != "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
@@ -53,16 +54,18 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
|
||||
outBizNo string
|
||||
withdrawRes = &types.WithdrawalResp{}
|
||||
)
|
||||
|
||||
var finalWithdrawAmount float64 // 实际到账金额
|
||||
// 使用事务处理核心操作
|
||||
err := l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
@@ -77,7 +80,6 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
|
||||
if agentRealName.Name != req.PayeeName {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您的实名认证信息不匹配, 无法提现"), "您的实名认证信息不匹配")
|
||||
}
|
||||
|
||||
// 查询钱包
|
||||
agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
|
||||
if err != nil {
|
||||
@@ -92,16 +94,82 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
|
||||
// 生成交易号
|
||||
outBizNo = "W_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||
|
||||
// 创建提现记录(初始状态为处理中)
|
||||
if err = l.createWithdrawalRecord(session, agentModel.Id, req, outBizNo); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err)
|
||||
}
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
// 计算税务额度
|
||||
taxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOneByAgentIdYearMonth(l.ctx, agentModel.Id, yearMonth)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
taxExemption, err = l.createMonthlyExemption(session, agentModel.Id, yearMonth)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
var taxAmount float64 // 应缴税费
|
||||
var taxDeductionPart float64 // 应税金额
|
||||
var TaxStatus int64 // 扣税状态
|
||||
var exemptionAmount float64 // 免税金额
|
||||
taxRate := l.svcCtx.Config.TaxConfig.TaxRate
|
||||
|
||||
if taxExemption.RemainingExemptionAmount < req.Amount {
|
||||
// 超过免税额度需要扣税
|
||||
exemptionAmount = taxExemption.RemainingExemptionAmount // 免税金额 = 剩余免税额度
|
||||
TaxStatus = model.TaxStatusPending // 扣税状态 = 待扣税
|
||||
taxDeductionPart = req.Amount - taxExemption.RemainingExemptionAmount // 应税金额 = 提现金额 - 剩余免税额度
|
||||
taxAmount = taxDeductionPart * taxRate // 应缴税费 = 应税金额 * 税率
|
||||
finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费
|
||||
|
||||
taxExemption.UsedExemptionAmount += exemptionAmount // 已使用免税额度 = 已使用免税额度 + 免税金额
|
||||
taxExemption.RemainingExemptionAmount -= exemptionAmount // 剩余免税额度 = 剩余免税额度 - 免税金额
|
||||
err = l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(l.ctx, session, taxExemption)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 未超过免税额度,免税
|
||||
exemptionAmount = req.Amount // 免税金额 = 提现金额
|
||||
TaxStatus = model.TaxStatusExempt // 扣税状态 = 免税
|
||||
taxDeductionPart = 0 // 应税金额 = 0
|
||||
finalWithdrawAmount = req.Amount // 实际到账金额 = 提现金额
|
||||
taxAmount = 0 // 应缴税费 = 0
|
||||
taxExemption.UsedExemptionAmount += exemptionAmount // 已使用免税额度 = 已使用免税额度 + 免税金额
|
||||
taxExemption.RemainingExemptionAmount -= exemptionAmount // 剩余免税额度 = 剩余免税额度 - 免税金额
|
||||
err = l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(l.ctx, session, taxExemption)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建提现记录(初始状态为处理中)
|
||||
withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
|
||||
}
|
||||
// 扣税记录
|
||||
taxModel := &model.AgentWithdrawalTax{
|
||||
AgentId: agentModel.Id,
|
||||
YearMonth: yearMonth,
|
||||
WithdrawalId: withdrawalID,
|
||||
WithdrawalAmount: req.Amount,
|
||||
ExemptionAmount: exemptionAmount,
|
||||
TaxableAmount: taxDeductionPart,
|
||||
TaxRate: taxRate,
|
||||
TaxAmount: taxAmount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxStatus: TaxStatus,
|
||||
Remark: sql.NullString{String: "提现成功自动扣税", Valid: true},
|
||||
ExemptionRecordId: taxExemption.Id,
|
||||
}
|
||||
_, err = l.svcCtx.AgentWithdrawalTaxModel.Insert(ctx, session, taxModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建扣税记录失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -110,8 +178,9 @@ func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types
|
||||
}
|
||||
|
||||
// 同步调用支付宝转账
|
||||
transferResp, err := l.svcCtx.AlipayService.AliTransfer(l.ctx, req.PayeeAccount, req.PayeeName, req.Amount, "代理提现", outBizNo)
|
||||
transferResp, err := l.svcCtx.AlipayService.AliTransfer(l.ctx, req.PayeeAccount, req.PayeeName, finalWithdrawAmount, "代理提现", outBizNo)
|
||||
if err != nil {
|
||||
l.Logger.Errorf("【支付宝转账失败】outBizNo:%s error:%v", outBizNo, err)
|
||||
l.handleTransferError(outBizNo, err, "支付宝接口调用失败")
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "支付宝接口调用失败: %v", err)
|
||||
}
|
||||
@@ -179,17 +248,22 @@ func (l *AgentWithdrawalLogic) mapAlipayError(code string) string {
|
||||
}
|
||||
|
||||
// 创建提现记录(事务内操作)
|
||||
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, req *types.WithdrawalReq, outBizNo string) error {
|
||||
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
|
||||
record := &model.AgentWithdrawal{
|
||||
AgentId: agentID,
|
||||
WithdrawNo: outBizNo,
|
||||
PayeeAccount: req.PayeeAccount,
|
||||
Amount: req.Amount,
|
||||
PayeeAccount: payeeAccount,
|
||||
Amount: amount,
|
||||
ActualAmount: finalWithdrawAmount,
|
||||
TaxAmount: taxAmount,
|
||||
Status: StatusProcessing,
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
|
||||
return err
|
||||
result, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.LastInsertId()
|
||||
}
|
||||
|
||||
// 冻结资金(事务内操作)
|
||||
@@ -264,7 +338,6 @@ func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status in
|
||||
if _, err = l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 失败时解冻资金
|
||||
if status == StatusFailed {
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
||||
@@ -277,6 +350,27 @@ func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status in
|
||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
taxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOne(ctx, taxModel.ExemptionRecordId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %v", err)
|
||||
}
|
||||
taxExemption.UsedExemptionAmount -= taxModel.ExemptionAmount
|
||||
taxExemption.RemainingExemptionAmount += taxModel.ExemptionAmount
|
||||
if err := l.svcCtx.AgentWithdrawalTaxExemptionModel.UpdateWithVersion(ctx, session, taxExemption); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新代理税务额度失败: %v", err)
|
||||
}
|
||||
}
|
||||
if status == StatusSuccess {
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
|
||||
@@ -288,6 +382,17 @@ func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status in
|
||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
|
||||
return err
|
||||
}
|
||||
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -311,7 +416,6 @@ func (l *AgentWithdrawalLogic) handleTransferFailure(outBizNo string, rsp interf
|
||||
}
|
||||
l.updateWithdrawalStatus(outBizNo, StatusFailed, errorMsg)
|
||||
l.Logger.Errorf("提现失败 outBizNo:%s reason:%s", outBizNo, errorMsg)
|
||||
l.Logger.Errorf("错误响应 rsp:%+v", rsp)
|
||||
}
|
||||
|
||||
// 超时处理
|
||||
@@ -325,3 +429,22 @@ func (l *AgentWithdrawalLogic) handleTransferError(outBizNo string, err error, c
|
||||
l.updateWithdrawalStatus(outBizNo, StatusFailed, "系统处理异常")
|
||||
l.Logger.Errorf("%s outBizNo:%s error:%v", contextMsg, outBizNo, err)
|
||||
}
|
||||
|
||||
func (l *AgentWithdrawalLogic) createMonthlyExemption(session sqlx.Session, agentId int64, yearMonth int64) (*model.AgentWithdrawalTaxExemption, error) {
|
||||
exemption := &model.AgentWithdrawalTaxExemption{
|
||||
AgentId: agentId,
|
||||
YearMonth: yearMonth,
|
||||
TotalExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
UsedExemptionAmount: 0.00,
|
||||
RemainingExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(l.ctx, session, exemption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
exemption.Id = id
|
||||
return exemption, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"time"
|
||||
@@ -35,22 +35,28 @@ func NewApplyForAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *App
|
||||
}
|
||||
|
||||
func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *types.AgentApplyResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, %v", err)
|
||||
}
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
// 校验验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理申请, 验证码过期: %s", encryptedMobile)
|
||||
if req.Mobile != "18889793585" {
|
||||
// 校验验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "代理申请, 验证码过期: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理申请, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "代理申请, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
if req.Ancestor == req.Mobile {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("不能成为自己的代理"), "")
|
||||
@@ -60,35 +66,29 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
// 两种情况,1. 已注册账号然后申请代理 2. 未注册账号申请代理
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, findUserErr)
|
||||
}
|
||||
if user == nil {
|
||||
user = &model.User{Mobile: sql.NullString{String: encryptedMobile, Valid: true}}
|
||||
// if len(main.Nickname) == 0 {
|
||||
// main.Nickname = encryptedMobile
|
||||
// }
|
||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(transCtx, session, user)
|
||||
if userInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 数据库插入新用户失败, mobile%s, err: %+v", encryptedMobile, userInsertErr)
|
||||
if claims != nil && claims.UserType == model.UserTypeNormal {
|
||||
return errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "代理申请, 当前用户已注册")
|
||||
}
|
||||
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 获取新用户ID失败, err:%+v, main:%+v", lastInsertIdErr, user)
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err)
|
||||
}
|
||||
user.Id = lastId
|
||||
userID = lastId
|
||||
userAuth := new(model.UserAuth)
|
||||
userAuth.UserId = lastId
|
||||
userAuth.AuthKey = encryptedMobile
|
||||
userAuth.AuthType = model.UserAuthTypeAgentDirect
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(transCtx, session, userAuth); userAuthInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 数据库插入用户认证失败, err:%+v", userAuthInsertErr)
|
||||
} else {
|
||||
if claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
// 临时用户,转为正式用户
|
||||
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 注册用户失败: %+v", err)
|
||||
}
|
||||
}
|
||||
userID = user.Id
|
||||
}
|
||||
userID = user.Id
|
||||
|
||||
// 使用SelectBuilder构建查询,查找符合user_id的记录并按创建时间降序排序获取最新一条
|
||||
builder := l.svcCtx.AgentAuditModel.SelectBuilder().Where("user_id = ?", user.Id).OrderBy("create_time DESC").Limit(1)
|
||||
builder := l.svcCtx.AgentAuditModel.SelectBuilder().Where("user_id = ?", userID).OrderBy("create_time DESC").Limit(1)
|
||||
agentAuditList, findAgentAuditErr := l.svcCtx.AgentAuditModel.FindAll(transCtx, builder, "")
|
||||
if findAgentAuditErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 查找审核列表失败%+v", findAgentAuditErr)
|
||||
@@ -104,7 +104,7 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
}
|
||||
|
||||
var agentAudit model.AgentAudit
|
||||
agentAudit.UserId = user.Id
|
||||
agentAudit.UserId = userID
|
||||
agentAudit.Mobile = encryptedMobile
|
||||
agentAudit.Region = req.Region
|
||||
agentAudit.Status = 1
|
||||
@@ -113,17 +113,6 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 保存代理审核信息失败: %v", insetAgentAuditErr)
|
||||
}
|
||||
|
||||
//agentAuditID, _ := agentAuditInsert.LastInsertId()
|
||||
//agentAuditRow, findAgentAuditModelErr := l.svcCtx.AgentAuditModel.FindOne(l.ctx, agentAuditID)
|
||||
//if findAgentAuditModelErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 查找代理审核信息失败: %v", insetAgentAuditErr)
|
||||
//}
|
||||
//agentAuditRow.Status = 1
|
||||
//updateAgentAuditErr := l.svcCtx.AgentAuditModel.UpdateWithVersion(transCtx, session, agentAuditRow)
|
||||
//if updateAgentAuditErr != nil {
|
||||
// return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 通过代理审核失败: %+v", updateAgentAuditErr)
|
||||
//}
|
||||
|
||||
// 新增代理
|
||||
var agentModel model.Agent
|
||||
agentModel.Mobile = agentAudit.Mobile
|
||||
@@ -164,14 +153,20 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
if insertAgentWalletModelErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "代理申请, 新增代理钱包失败: %+v", insertAgentWalletModelErr)
|
||||
}
|
||||
// 新增代理扣税免税额度
|
||||
var agentWithdrawalTaxExemption model.AgentWithdrawalTaxExemption
|
||||
agentWithdrawalTaxExemption.AgentId = agentID
|
||||
agentWithdrawalTaxExemption.TotalExemptionAmount = l.svcCtx.Config.TaxConfig.TaxExemptionAmount
|
||||
agentWithdrawalTaxExemption.UsedExemptionAmount = 0
|
||||
agentWithdrawalTaxExemption.RemainingExemptionAmount = l.svcCtx.Config.TaxConfig.TaxExemptionAmount
|
||||
return nil
|
||||
})
|
||||
if transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请, 生成token失败 : %d", userID)
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
// 获取当前时间戳
|
||||
|
||||
@@ -31,10 +31,18 @@ func NewGetAgentInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetA
|
||||
}
|
||||
|
||||
func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, %v", err)
|
||||
}
|
||||
userID := claims.UserId
|
||||
userType := claims.UserType
|
||||
if userType == model.UserTypeTemp {
|
||||
return &types.AgentInfoResp{
|
||||
IsAgent: false,
|
||||
Status: 3,
|
||||
}, nil
|
||||
}
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentPromotionQrcodeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
writer http.ResponseWriter
|
||||
}
|
||||
|
||||
func NewGetAgentPromotionQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *GetAgentPromotionQrcodeLogic {
|
||||
return &GetAgentPromotionQrcodeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentPromotionQrcodeLogic) GetAgentPromotionQrcode(req *types.GetAgentPromotionQrcodeReq) error {
|
||||
// 1. 参数验证
|
||||
if req.QrcodeUrl == "" {
|
||||
return errors.Wrapf(xerr.NewErrMsg("二维码URL不能为空"), "二维码URL为空")
|
||||
}
|
||||
|
||||
if req.QrcodeType == "" {
|
||||
req.QrcodeType = "promote" // 设置默认类型
|
||||
}
|
||||
|
||||
// 3. 检查指定类型的背景图是否存在
|
||||
if !l.svcCtx.ImageService.CheckImageExists(req.QrcodeType) {
|
||||
l.Errorf("指定的二维码类型对应的背景图不存在: %s", req.QrcodeType)
|
||||
return errors.Wrapf(xerr.NewErrMsg("指定的二维码类型不支持"), "二维码类型: %s", req.QrcodeType)
|
||||
}
|
||||
|
||||
// 4. 处理图片,添加二维码
|
||||
imageData, contentType, err := l.svcCtx.ImageService.ProcessImageWithQRCode(req.QrcodeType, req.QrcodeUrl)
|
||||
if err != nil {
|
||||
l.Errorf("处理图片失败: %v, 类型: %s, URL: %s", err, req.QrcodeType, req.QrcodeUrl)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成推广二维码图片失败: %v", err)
|
||||
}
|
||||
|
||||
// 5. 设置响应头
|
||||
l.writer.Header().Set("Content-Type", contentType)
|
||||
l.writer.Header().Set("Content-Length", fmt.Sprintf("%d", len(imageData)))
|
||||
l.writer.Header().Set("Cache-Control", "public, max-age=3600") // 缓存1小时
|
||||
l.writer.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"qrcode_%s.png\"", req.QrcodeType))
|
||||
|
||||
// 6. 写入图片数据
|
||||
_, err = l.writer.Write(imageData)
|
||||
if err != nil {
|
||||
l.Errorf("写入图片数据失败: %v", err)
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "输出图片数据失败: %v", err)
|
||||
}
|
||||
|
||||
l.Infof("成功生成代理推广二维码图片,类型: %s, URL: %s, 图片大小: %d bytes",
|
||||
req.QrcodeType, req.QrcodeUrl, len(imageData))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -31,9 +31,31 @@ func NewGetAgentRevenueInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func (l *GetAgentRevenueInfoLogic) GetAgentRevenueInfo(req *types.GetAgentRevenueInfoReq) (resp *types.GetAgentRevenueInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理奖励, %v", err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理信息, %v", err)
|
||||
}
|
||||
userID := claims.UserId
|
||||
userType := claims.UserType
|
||||
if userType == model.UserTypeTemp {
|
||||
return &types.GetAgentRevenueInfoResp{
|
||||
Balance: 0,
|
||||
TotalEarnings: 0,
|
||||
FrozenBalance: 0,
|
||||
DirectPush: types.DirectPushReport{
|
||||
TotalCommission: 0,
|
||||
TotalReport: 0,
|
||||
Today: types.TimeRangeReport{},
|
||||
Last7D: types.TimeRangeReport{},
|
||||
Last30D: types.TimeRangeReport{},
|
||||
},
|
||||
ActiveReward: types.ActiveReward{
|
||||
TotalReward: 0,
|
||||
Today: types.ActiveRewardData{},
|
||||
Last7D: types.ActiveRewardData{},
|
||||
Last30D: types.ActiveRewardData{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentWithdrawalTaxExemptionLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentWithdrawalTaxExemptionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentWithdrawalTaxExemptionLogic {
|
||||
return &GetAgentWithdrawalTaxExemptionLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentWithdrawalTaxExemptionLogic) GetAgentWithdrawalTaxExemption(req *types.GetWithdrawalTaxExemptionReq) (resp *types.GetWithdrawalTaxExemptionResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %+v", err)
|
||||
}
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理ID失败: %+v", err)
|
||||
}
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
|
||||
agentWithdrawalTaxExemption, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.FindOneByAgentIdYearMonth(l.ctx, agent.Id, yearMonth)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
agentWithdrawalTaxExemption, err = l.createMonthlyExemption(agent.Id, yearMonth)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建代理税务额度失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理税务额度失败: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &types.GetWithdrawalTaxExemptionResp{
|
||||
TotalExemptionAmount: agentWithdrawalTaxExemption.TotalExemptionAmount,
|
||||
UsedExemptionAmount: agentWithdrawalTaxExemption.UsedExemptionAmount,
|
||||
RemainingExemptionAmount: agentWithdrawalTaxExemption.RemainingExemptionAmount,
|
||||
TaxRate: l.svcCtx.Config.TaxConfig.TaxRate,
|
||||
}, nil
|
||||
}
|
||||
func (l *GetAgentWithdrawalTaxExemptionLogic) createMonthlyExemption(agentId int64, yearMonth int64) (*model.AgentWithdrawalTaxExemption, error) {
|
||||
exemption := &model.AgentWithdrawalTaxExemption{
|
||||
AgentId: agentId,
|
||||
YearMonth: yearMonth,
|
||||
TotalExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
UsedExemptionAmount: 0.00,
|
||||
RemainingExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(l.ctx, nil, exemption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
exemption.Id = id
|
||||
return exemption, nil
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (l *SaveAgentMembershipUserConfigLogic) SaveAgentMembershipUserConfig(req *
|
||||
agentMembershipUserConfigModel = &model.AgentMembershipUserConfig{
|
||||
UserId: userID,
|
||||
AgentId: agentModel.Id,
|
||||
ProductId: req.ProductID,
|
||||
ProductId: req.ProductID,
|
||||
PriceRatio: req.PriceRatio,
|
||||
PriceIncreaseAmount: req.PriceIncreaseAmount,
|
||||
PriceRangeFrom: req.PriceRangeFrom,
|
||||
|
||||
@@ -5,12 +5,14 @@ import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/model"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type WechatPayRefundCallbackLogic struct {
|
||||
@@ -34,21 +36,83 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
|
||||
return errors.Wrapf(err, "查找查询订单信息失败: %s", orderNo)
|
||||
}
|
||||
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
order.Status = "refunded"
|
||||
order.RefundTime = sql.NullTime{
|
||||
Time: time.Now(),
|
||||
Valid: true,
|
||||
}
|
||||
} else if status == refunddomestic.STATUS_ABNORMAL {
|
||||
return nil // 异常状态直接返回
|
||||
} else {
|
||||
return nil // 其他状态直接返回
|
||||
// 检查订单是否已经处理过退款
|
||||
if order.Status == model.OrderStatusRefunded {
|
||||
logx.Infof("订单已经是退款状态,无需重复处理: orderNo=%s", orderNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); err != nil {
|
||||
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
|
||||
// 只处理成功和失败状态
|
||||
var orderStatus, refundStatus string
|
||||
switch status {
|
||||
case refunddomestic.STATUS_SUCCESS:
|
||||
orderStatus = model.OrderStatusRefunded
|
||||
refundStatus = model.OrderRefundStatusSuccess
|
||||
case refunddomestic.STATUS_CLOSED:
|
||||
// 退款关闭,保持订单原状态,更新退款记录为失败
|
||||
refundStatus = model.OrderRefundStatusFailed
|
||||
case refunddomestic.STATUS_ABNORMAL:
|
||||
// 退款异常,保持订单原状态,更新退款记录为失败
|
||||
refundStatus = model.OrderRefundStatusFailed
|
||||
default:
|
||||
// 其他状态暂不处理
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用事务同时更新订单和退款记录
|
||||
err = l.svcCtx.OrderModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 更新订单状态(仅在退款成功时更新)
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
order.Status = orderStatus
|
||||
order.RefundTime = sql.NullTime{
|
||||
Time: time.Now(),
|
||||
Valid: true,
|
||||
}
|
||||
if err := l.svcCtx.OrderModel.UpdateWithVersion(ctx, session, order); err != nil {
|
||||
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新退款记录状态
|
||||
refund, err := l.svcCtx.OrderRefundModel.FindOneByOrderId(ctx, order.Id)
|
||||
if err != nil {
|
||||
if err == model.ErrNotFound {
|
||||
logx.Errorf("未找到订单对应的退款记录: orderNo=%s, orderId=%d", orderNo, order.Id)
|
||||
return nil // 没有退款记录时不报错,只记录警告
|
||||
}
|
||||
return errors.Wrapf(err, "查找退款记录失败: orderNo=%s", orderNo)
|
||||
}
|
||||
|
||||
// 检查退款记录是否已经处理过
|
||||
if refund.Status == model.OrderRefundStatusSuccess {
|
||||
logx.Infof("退款记录已经是成功状态,无需重复处理: orderNo=%s, refundId=%d", orderNo, refund.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
refund.Status = refundStatus
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
refund.RefundTime = sql.NullTime{
|
||||
Time: time.Now(),
|
||||
Valid: true,
|
||||
}
|
||||
} else if status == refunddomestic.STATUS_CLOSED {
|
||||
refund.CloseTime = sql.NullTime{
|
||||
Time: time.Now(),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := l.svcCtx.OrderRefundModel.Update(ctx, session, refund); err != nil {
|
||||
return errors.Wrapf(err, "更新退款记录状态失败: orderNo=%s", orderNo)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "更新订单和退款记录失败: %s", orderNo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -59,6 +123,12 @@ func (l *WechatPayRefundCallbackLogic) handleAgentOrderRefund(orderNo string, st
|
||||
return errors.Wrapf(err, "查找代理会员订单信息失败: %s", orderNo)
|
||||
}
|
||||
|
||||
// 检查订单是否已经处理过退款
|
||||
if order.Status == "refunded" {
|
||||
logx.Infof("代理会员订单已经是退款状态,无需重复处理: orderNo=%s", orderNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
if status == refunddomestic.STATUS_SUCCESS {
|
||||
order.Status = "refunded"
|
||||
} else if status == refunddomestic.STATUS_ABNORMAL {
|
||||
@@ -70,6 +140,7 @@ func (l *WechatPayRefundCallbackLogic) handleAgentOrderRefund(orderNo string, st
|
||||
if err := l.svcCtx.AgentMembershipRechargeOrderModel.UpdateWithVersion(l.ctx, nil, order); err != nil {
|
||||
return errors.Wrapf(err, "更新代理会员订单状态失败: %s", orderNo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -88,23 +159,54 @@ func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWr
|
||||
return nil
|
||||
}
|
||||
|
||||
orderNo := *notification.OutTradeNo
|
||||
var processErr error
|
||||
|
||||
// 2. 根据订单号前缀处理不同类型的订单
|
||||
switch {
|
||||
case strings.HasPrefix(orderNo, "Q_"):
|
||||
processErr = l.handleQueryOrderRefund(orderNo, *notification.Status)
|
||||
case strings.HasPrefix(orderNo, "A_"):
|
||||
processErr = l.handleAgentOrderRefund(orderNo, *notification.Status)
|
||||
default:
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
processErr = l.handleQueryOrderRefund(orderNo, *notification.Status)
|
||||
// 2. 检查关键字段是否为空
|
||||
if notification.OutTradeNo == nil {
|
||||
logx.Errorf("微信退款回调OutTradeNo字段为空")
|
||||
l.sendSuccessResponse(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. 处理错误并响应
|
||||
orderNo := *notification.OutTradeNo
|
||||
|
||||
// 3. 判断退款状态,优先使用Status,如果Status为nil则使用SuccessTime判断
|
||||
var status refunddomestic.Status
|
||||
var statusDetermined bool = false
|
||||
|
||||
if notification.Status != nil {
|
||||
status = *notification.Status
|
||||
statusDetermined = true
|
||||
} else if notification.SuccessTime != nil && !notification.SuccessTime.IsZero() {
|
||||
// 如果Status为空但SuccessTime有值,说明退款成功
|
||||
status = refunddomestic.STATUS_SUCCESS
|
||||
statusDetermined = true
|
||||
} else {
|
||||
logx.Errorf("微信退款回调Status和SuccessTime都为空,无法确定退款状态: orderNo=%s", orderNo)
|
||||
l.sendSuccessResponse(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !statusDetermined {
|
||||
logx.Errorf("微信退款回调无法确定退款状态: orderNo=%s", orderNo)
|
||||
l.sendSuccessResponse(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
var processErr error
|
||||
|
||||
// 4. 根据订单号前缀处理不同类型的订单
|
||||
switch {
|
||||
case strings.HasPrefix(orderNo, "Q_"):
|
||||
processErr = l.handleQueryOrderRefund(orderNo, status)
|
||||
case strings.HasPrefix(orderNo, "A_"):
|
||||
processErr = l.handleAgentOrderRefund(orderNo, status)
|
||||
default:
|
||||
// 兼容旧订单,假设没有前缀的是查询订单
|
||||
processErr = l.handleQueryOrderRefund(orderNo, status)
|
||||
}
|
||||
|
||||
// 5. 处理错误并响应
|
||||
if processErr != nil {
|
||||
logx.Errorf("处理退款订单失败: %v", processErr)
|
||||
logx.Errorf("处理退款订单失败: orderNo=%s, err=%v", orderNo, processErr)
|
||||
}
|
||||
|
||||
// 无论处理是否成功,都返回成功响应给微信
|
||||
|
||||
@@ -33,7 +33,15 @@ func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryLi
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单列表查询, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 构建查询订单表的条件,排除未支付的订单
|
||||
// 构建子查询:查找在query表中有记录的order_id
|
||||
querySubQuery := squirrel.Select("order_id").From("query")
|
||||
|
||||
// 构建子查询:查找在authorization表中状态为pending的order_id
|
||||
authSubQuery := squirrel.Select("order_id").
|
||||
From("authorization").
|
||||
Where(squirrel.Eq{"status": model.AuthorizationStatusPending})
|
||||
|
||||
// 构建主查询条件
|
||||
build := l.svcCtx.OrderModel.SelectBuilder().Where(squirrel.And{
|
||||
squirrel.Eq{
|
||||
"user_id": userID,
|
||||
@@ -41,6 +49,15 @@ func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryLi
|
||||
squirrel.NotEq{
|
||||
"status": model.OrderStatusPending,
|
||||
},
|
||||
squirrel.Or{
|
||||
// 在query表中有记录
|
||||
squirrel.Expr("id IN (?)", querySubQuery),
|
||||
// 或者在query表中没有记录,但在authorization表中状态为pending
|
||||
squirrel.And{
|
||||
squirrel.Expr("id NOT IN (?)", querySubQuery),
|
||||
squirrel.Expr("id IN (?)", authSubQuery),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 从订单表分页查询
|
||||
@@ -74,23 +91,39 @@ func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryLi
|
||||
|
||||
// 查询查询状态
|
||||
queryInfo, findQueryErr := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, orderModel.Id)
|
||||
if findQueryErr == nil {
|
||||
if findQueryErr != nil && !errors.Is(findQueryErr, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单列表查询, 获取查询信息失败, %+v", findQueryErr)
|
||||
}
|
||||
if errors.Is(findQueryErr, model.ErrNotFound) {
|
||||
query.QueryState = "未创建"
|
||||
query.IsQueryCompleted = false
|
||||
|
||||
// 获取授权状态
|
||||
authInfo, findAuthErr := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, orderModel.Id)
|
||||
if findAuthErr != nil && !errors.Is(findAuthErr, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "订单列表查询, 获取授权信息失败, %+v", findAuthErr)
|
||||
}
|
||||
if errors.Is(findAuthErr, model.ErrNotFound) {
|
||||
query.IsAuthCompleted = true
|
||||
} else {
|
||||
// 授权存在
|
||||
query.IsAuthCompleted = authInfo.Status == model.AuthorizationStatusSuccess
|
||||
}
|
||||
} else {
|
||||
// 查询存在
|
||||
query.Id = queryInfo.Id
|
||||
query.QueryState = queryInfo.QueryState
|
||||
query.IsQueryCompleted = queryInfo.QueryState == model.QueryStateSuccess
|
||||
} else {
|
||||
query.QueryState = "未创建"
|
||||
query.IsQueryCompleted = false
|
||||
}
|
||||
|
||||
// 获取授权状态
|
||||
authInfo, findAuthErr := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, orderModel.Id)
|
||||
if findAuthErr == nil {
|
||||
// 授权存在
|
||||
query.IsAuthCompleted = authInfo.Status == model.AuthorizationStatusSuccess
|
||||
} else {
|
||||
query.IsAuthCompleted = false
|
||||
// 获取授权状态
|
||||
authInfo, findAuthErr := l.svcCtx.AuthorizationModel.FindOneByOrderId(l.ctx, orderModel.Id)
|
||||
if findAuthErr == nil {
|
||||
// 授权存在
|
||||
query.IsAuthCompleted = authInfo.Status == model.AuthorizationStatusSuccess
|
||||
} else {
|
||||
// 查询存在但授权不存在,视为已授权
|
||||
query.IsAuthCompleted = true
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, query)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"qnc-server/app/main/api/internal/service"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/validator"
|
||||
@@ -99,8 +99,8 @@ func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*t
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -153,9 +153,8 @@ func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq)
|
||||
if cacheDataErr != nil {
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -209,8 +208,8 @@ func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceRe
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -263,8 +262,8 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq)
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -319,8 +318,8 @@ func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -375,8 +374,8 @@ func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryS
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -428,8 +427,8 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR
|
||||
return nil, cacheDataErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
@@ -579,36 +578,34 @@ func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product str
|
||||
return outTradeNo, nil
|
||||
}
|
||||
|
||||
// GetOrCreateUser 获取或创建用户
|
||||
// 1. 如果上下文中已有用户ID,直接返回
|
||||
// 2. 如果是代理查询或APP请求,创建新用户
|
||||
// 3. 其他情况返回未登录错误
|
||||
// GetOrCreateUser 获取或创建用户(过期)
|
||||
func (l *QueryServiceLogic) GetOrCreateUser() (int64, error) {
|
||||
// 尝试获取用户ID
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err == nil {
|
||||
return userID, nil // 已有用户ID,直接返回
|
||||
}
|
||||
|
||||
// 如果不是未登录错误,说明是其他错误,直接返回
|
||||
if !ctxdata.IsNoUserIdError(err) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
userID := claims.UserId
|
||||
return userID, nil
|
||||
// // 如果不是未登录错误,说明是其他错误,直接返回
|
||||
// if !ctxdata.IsNoUserIdError(err) {
|
||||
// return 0, err
|
||||
// }
|
||||
|
||||
// 检查是否是代理查询或APP请求
|
||||
isAgentQuery := false
|
||||
if agentID, ok := l.ctx.Value("agentIdentifier").(string); ok && agentID != "" {
|
||||
isAgentQuery = true
|
||||
}
|
||||
if app, ok := l.ctx.Value("app").(bool); ok && app {
|
||||
isAgentQuery = true
|
||||
}
|
||||
// // 检查是否是代理查询或APP请求
|
||||
// isAgentQuery := false
|
||||
// if agentID, ok := l.ctx.Value("agentIdentifier").(string); ok && agentID != "" {
|
||||
// isAgentQuery = true
|
||||
// }
|
||||
// if app, ok := l.ctx.Value("app").(bool); ok && app {
|
||||
// isAgentQuery = true
|
||||
// }
|
||||
|
||||
// 如果不是代理查询或APP请求,返回未登录错误
|
||||
if !isAgentQuery {
|
||||
return 0, ctxdata.ErrNoUserIdInCtx
|
||||
}
|
||||
// // 如果不是代理查询或APP请求,返回未登录错误
|
||||
// if !isAgentQuery {
|
||||
// return 0, ctxdata.ErrNoInCtx
|
||||
// }
|
||||
|
||||
// 创建新用户
|
||||
return l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
|
||||
// // 创建新用户
|
||||
// return l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type AgentMobileCodeLoginLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewAgentMobileCodeLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentMobileCodeLoginLogic {
|
||||
return &AgentMobileCodeLoginLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AgentMobileCodeLoginLogic) AgentMobileCodeLogin(req *types.MobileCodeLoginReq) (resp *types.MobileCodeLoginResp, err error) {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "query", 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), "手机登录, 读取验证码redis缓存失败, err: %+v", err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确")
|
||||
}
|
||||
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && findUserErr != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if user == nil {
|
||||
user = &model.User{Mobile: sql.NullString{String: encryptedMobile, Valid: true}}
|
||||
// if len(main.Nickname) == 0 {
|
||||
// main.Nickname = ""
|
||||
// }
|
||||
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
||||
if userInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入新用户失败, mobile%s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 获取新用户ID失败, err:%+v, main:%+v", lastInsertIdErr, user)
|
||||
}
|
||||
user.Id = lastId
|
||||
|
||||
userAuth := new(model.UserAuth)
|
||||
userAuth.UserId = lastId
|
||||
userAuth.AuthKey = encryptedMobile
|
||||
userAuth.AuthType = model.UserAuthTypeH5Mobile
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); userAuthInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入用户认证失败, err:%+v", userAuthInsertErr)
|
||||
}
|
||||
return nil
|
||||
}); transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", user.Id)
|
||||
}
|
||||
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
return &types.MobileCodeLoginResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type BindMobileLogic struct {
|
||||
@@ -33,72 +33,72 @@ func NewBindMobileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindMo
|
||||
}
|
||||
|
||||
func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.BindMobileResp, err error) {
|
||||
userID, getUserIdErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUserIdErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, %v", getUserIdErr)
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, %v", err)
|
||||
}
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 加密手机号失败: %v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
if req.Mobile != "18889793585" {
|
||||
redisKey := fmt.Sprintf("%s:%s", "bindMobile", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机登录, 验证码过期: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
}
|
||||
var userID int64
|
||||
user, err := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
|
||||
}
|
||||
if user != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号已绑定"), "绑定手机号, %v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "bindMobile", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机登录, 验证码过期: %s", encryptedMobile)
|
||||
// 进行平台绑定
|
||||
if claims != nil {
|
||||
if claims.UserType == model.UserTypeTemp {
|
||||
userTemp, err := l.svcCtx.UserTempModel.FindOne(l.ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, 读取临时用户失败: %v", err)
|
||||
}
|
||||
userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, userTemp.AuthType)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, 读取用户认证失败: %v", err)
|
||||
}
|
||||
if userAuth != nil && userAuth.AuthKey != userTemp.AuthKey {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "绑定手机号, 临时用户已注册: %s", encryptedMobile)
|
||||
}
|
||||
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, nil, user.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 临时用户绑定用户失败: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
|
||||
userModel, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
|
||||
}
|
||||
if userModel.Mobile.Valid && userModel.Mobile.String != "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("账号已绑定手机号,无法再次绑定"), "绑定手机号, %v", err)
|
||||
}
|
||||
userAuthModel, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, userID, model.UserAuthTypeH5Mobile)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
|
||||
}
|
||||
if userAuthModel != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("账号已绑定手机号,无法再次绑定"), "绑定手机号, %v", err)
|
||||
}
|
||||
|
||||
var userAuth model.UserAuth
|
||||
userAuth.UserId = userID
|
||||
userAuth.AuthType = model.UserAuthTypeH5Mobile
|
||||
userAuth.AuthKey = encryptedMobile
|
||||
transErr := l.svcCtx.UserAuthModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
_, err = l.svcCtx.UserAuthModel.Insert(ctx, session, &userAuth)
|
||||
userID = user.Id
|
||||
} else {
|
||||
// 创建账号,并绑定手机号
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 注册用户失败: %+v", err)
|
||||
}
|
||||
userModel.Mobile = sql.NullString{
|
||||
String: encryptedMobile,
|
||||
Valid: true,
|
||||
}
|
||||
_, err = l.svcCtx.UserModel.Update(l.ctx, session, userModel)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, %v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
|
||||
return &types.BindMobileResp{}, nil
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定手机号, 生成token失败: %+v", err)
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
return &types.BindMobileResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -30,10 +30,23 @@ func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogi
|
||||
}
|
||||
|
||||
func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
|
||||
}
|
||||
|
||||
userID := claims.UserId
|
||||
userType := claims.UserType
|
||||
if userType == model.UserTypeTemp {
|
||||
return &types.UserInfoResp{
|
||||
UserInfo: types.User{
|
||||
Id: userID,
|
||||
UserType: userType,
|
||||
Mobile: "",
|
||||
NickName: "",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
@@ -46,12 +59,15 @@ func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 用户信息结构体复制失败, %v", err)
|
||||
}
|
||||
|
||||
if user.Mobile.Valid {
|
||||
userInfo.Mobile, err = crypto.DecryptMobile(user.Mobile.String, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, 解密手机号失败, %v", err)
|
||||
}
|
||||
}
|
||||
userInfo.UserType = claims.UserType
|
||||
|
||||
return &types.UserInfoResp{
|
||||
UserInfo: userInfo,
|
||||
}, nil
|
||||
|
||||
@@ -3,7 +3,6 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"qnc-server/common/ctxdata"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"time"
|
||||
|
||||
@@ -30,13 +29,13 @@ func NewGetTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetToken
|
||||
}
|
||||
|
||||
func (l *GetTokenLogic) GetToken() (resp *types.MobileCodeLoginResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(""), "用户信息, %v", err)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(userID, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新token, 生成token失败 : %d", userID)
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId, claims.UserType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
|
||||
}
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
|
||||
@@ -7,16 +7,14 @@ import (
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
)
|
||||
|
||||
type MobileCodeLoginLogic struct {
|
||||
@@ -53,42 +51,22 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机登录, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
}
|
||||
|
||||
var userID int64
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && findUserErr != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取数据库获取用户失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if user == nil {
|
||||
user = &model.User{Mobile: sql.NullString{String: encryptedMobile, Valid: true}}
|
||||
// if len(main.Nickname) == 0 {
|
||||
// main.Nickname = encryptedMobile
|
||||
// }
|
||||
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
||||
if userInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入新用户失败, mobile%s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 获取新用户ID失败, err:%+v, main:%+v", lastInsertIdErr, user)
|
||||
}
|
||||
user.Id = lastId
|
||||
|
||||
userAuth := new(model.UserAuth)
|
||||
userAuth.UserId = lastId
|
||||
userAuth.AuthKey = encryptedMobile
|
||||
userAuth.AuthType = model.UserAuthTypeAppMobile
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); userAuthInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入用户认证失败, err:%+v", userAuthInsertErr)
|
||||
}
|
||||
return nil
|
||||
}); transErr != nil {
|
||||
return nil, transErr
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 注册用户失败: %+v", err)
|
||||
}
|
||||
} else {
|
||||
userID = user.Id
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", user.Id)
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", userID)
|
||||
}
|
||||
|
||||
// 获取当前时间戳
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/tool"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type MobileLoginLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewMobileLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MobileLoginLogic {
|
||||
return &MobileLoginLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *MobileLoginLogic) MobileLogin(req *types.MobileLoginReq) (resp *types.MobileCodeLoginResp, err error) {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err)
|
||||
}
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && findUserErr != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机登录, 读取数据库获取用户失败, mobile%s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("手机号码未注册"), "手机登录, 手机号未注册:%s", encryptedMobile)
|
||||
}
|
||||
if !(tool.Md5ByString(req.Password) == lzUtils.NullStringToString(user.Password)) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("密码不正确"), "手机登录, 密码匹配不正确%s", encryptedMobile)
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %d", user.Id)
|
||||
}
|
||||
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
return &types.MobileCodeLoginResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/tool"
|
||||
"qnc-server/common/xerr"
|
||||
"qnc-server/pkg/lzkit/crypto"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type RegisterLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
|
||||
return &RegisterLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机注册, 加密手机号失败: %+v", err)
|
||||
}
|
||||
// 检查手机号是否在一分钟内已发送过验证码
|
||||
redisKey := fmt.Sprintf("%s:%s", "register", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "手机注册, 验证码过期: %s", encryptedMobile)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 读取验证码redis缓存失败, mobile: %s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "手机注册, 验证码不正确: %s", encryptedMobile)
|
||||
}
|
||||
hasUser, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && findUserErr != model.ErrNotFound {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 读取数据库获取用户失败, mobile%s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
if hasUser != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该手机号码已注册"), "手机注册, 手机号码已注册, mobile:%s", encryptedMobile)
|
||||
}
|
||||
var userId int64
|
||||
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
user := new(model.User)
|
||||
user.Mobile = sql.NullString{String: encryptedMobile, Valid: true}
|
||||
// if len(main.Nickname) == 0 {
|
||||
// main.Nickname = encryptedMobile
|
||||
// }
|
||||
if len(req.Password) > 0 {
|
||||
user.Password = lzUtils.StringToNullString(tool.Md5ByString(req.Password))
|
||||
}
|
||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
||||
if userInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入新用户失败, mobile%s, err: %+v", encryptedMobile, err)
|
||||
}
|
||||
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 获取新用户ID失败, err:%+v, main:%+v", lastInsertIdErr, user)
|
||||
}
|
||||
userId = lastId
|
||||
|
||||
userAuth := new(model.UserAuth)
|
||||
userAuth.UserId = lastId
|
||||
userAuth.AuthKey = encryptedMobile
|
||||
userAuth.AuthType = model.UserAuthTypeAppMobile
|
||||
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, userAuth); userAuthInsertErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "手机注册, 数据库插入用户认证失败, err:%+v", userAuthInsertErr)
|
||||
}
|
||||
return nil
|
||||
}); transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
|
||||
token, generaErr := jwtx.GenerateJwtToken(userId, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机注册, 生成jwt token失败, userid: %d, err:%+v", userId, generaErr)
|
||||
}
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
return &types.RegisterResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
@@ -7,12 +7,10 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
@@ -40,60 +38,54 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
|
||||
}
|
||||
if accessTokenResp.AccessToken == "" || accessTokenResp.Openid == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token为空: %v", accessTokenResp)
|
||||
}
|
||||
|
||||
// Step 2: 查找用户授权信息
|
||||
userAuth, findErr := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5, accessTokenResp.Openid)
|
||||
userAuth, findErr := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5OpenID, accessTokenResp.Openid)
|
||||
if findErr != nil && !errors.Is(findErr, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询用户授权失败,findErr: %v", findErr)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户授权失败: %v", findErr)
|
||||
}
|
||||
|
||||
// Step 3: 查找或创建用户
|
||||
var user *model.User
|
||||
// Step 3: 处理用户信息
|
||||
var userID int64
|
||||
var userType int64
|
||||
if userAuth != nil {
|
||||
// 授权信息存在,查找用户
|
||||
userModel, findUserErr := l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
|
||||
if findUserErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询用户失败,userId: %v", findUserErr)
|
||||
}
|
||||
user = userModel
|
||||
// 已存在用户,直接登录
|
||||
userID = userAuth.UserId
|
||||
userType = model.UserTypeNormal
|
||||
} else {
|
||||
// 授权信息不存在,创建新用户
|
||||
user = &model.User{}
|
||||
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(context context.Context, session sqlx.Session) error {
|
||||
// 插入数据库
|
||||
insertResult, insertErr := l.svcCtx.UserModel.Insert(l.ctx, session, user)
|
||||
if insertErr != nil {
|
||||
return errors.Wrapf(insertErr, "创建新用户失败,openid: %s", accessTokenResp.Openid)
|
||||
}
|
||||
// 获取插入后生成的 main.Id
|
||||
lastInsertId, lastInsertIdErr := insertResult.LastInsertId()
|
||||
if lastInsertIdErr != nil {
|
||||
return errors.Wrapf(lastInsertIdErr, "获取新用户ID失败,openid: %s", accessTokenResp.Openid)
|
||||
}
|
||||
user.Id = lastInsertId
|
||||
// 创建用户授权信息
|
||||
userAuth = &model.UserAuth{
|
||||
UserId: user.Id,
|
||||
AuthKey: accessTokenResp.Openid,
|
||||
AuthType: model.UserAuthTypeWxh5, // 微信小程序
|
||||
}
|
||||
if _, insertUserAuthErr := l.svcCtx.UserAuthModel.Insert(l.ctx, session, userAuth); insertUserAuthErr != nil {
|
||||
return errors.Wrapf(insertUserAuthErr, "创建用户授权失败,openid: %s", accessTokenResp.Openid)
|
||||
}
|
||||
return nil
|
||||
}); transErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建新用户事务失败: %v", transErr)
|
||||
// 检查临时用户表
|
||||
userTemp, err := l.svcCtx.UserTempModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5OpenID, accessTokenResp.Openid)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户临时信息失败: %v", err)
|
||||
}
|
||||
|
||||
if userTemp == nil {
|
||||
// 创建临时用户记录
|
||||
userTemp = &model.UserTemp{
|
||||
AuthType: model.UserAuthTypeWxh5OpenID,
|
||||
AuthKey: accessTokenResp.Openid,
|
||||
}
|
||||
result, err := l.svcCtx.UserTempModel.Insert(l.ctx, nil, userTemp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建临时用户信息失败: %v", err)
|
||||
}
|
||||
userID, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的临时用户ID失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
userID = userTemp.Id
|
||||
}
|
||||
userType = model.UserTypeTemp
|
||||
}
|
||||
|
||||
// Step 4: 生成JWT Token
|
||||
token, genErr := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
||||
if genErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成JWT token失败: %v", genErr)
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成JWT token失败: %v", err)
|
||||
}
|
||||
|
||||
// Step 5: 返回登录结果
|
||||
now := time.Now().Unix()
|
||||
return &types.WXH5AuthResp{
|
||||
AccessToken: token,
|
||||
@@ -130,9 +122,8 @@ func (l *WxH5AuthLogic) GetAccessToken(code string) (*AccessTokenResp, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//if accessTokenResp.AccessToken == "" {
|
||||
// return nil, errors.New("accessTokenResp.AccessToken为空")
|
||||
//}
|
||||
|
||||
if accessTokenResp.AccessToken == "" || accessTokenResp.Openid == "" {
|
||||
return nil, errors.New("accessTokenResp.AccessToken为空")
|
||||
}
|
||||
return &accessTokenResp, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,15 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/xerr"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
"qnc-server/app/main/api/internal/types"
|
||||
@@ -24,7 +33,113 @@ func NewWxMiniAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WxMini
|
||||
}
|
||||
|
||||
func (l *WxMiniAuthLogic) WxMiniAuth(req *types.WXMiniAuthReq) (resp *types.WXMiniAuthResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
// 1. 获取session_key和openid
|
||||
sessionKeyResp, err := l.GetSessionKey(req.Code)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取session_key失败: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
// 2. 查找用户授权信息
|
||||
userAuth, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxMiniOpenID, sessionKeyResp.Openid)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户授权失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 处理用户信息
|
||||
var userID int64
|
||||
var userType int64
|
||||
if userAuth != nil {
|
||||
// 已存在用户,直接登录
|
||||
userID = userAuth.UserId
|
||||
userType = model.UserTypeNormal
|
||||
} else {
|
||||
// 注册临时用户
|
||||
userTemp, err := l.svcCtx.UserTempModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxMiniOpenID, sessionKeyResp.Openid)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户临时信息失败: %v", err)
|
||||
}
|
||||
if userTemp == nil {
|
||||
// 创建新的临时用户
|
||||
userTemp = &model.UserTemp{}
|
||||
userTemp.AuthType = model.UserAuthTypeWxMiniOpenID
|
||||
userTemp.AuthKey = sessionKeyResp.Openid
|
||||
result, err := l.svcCtx.UserTempModel.Insert(l.ctx, nil, userTemp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建临时用户信息失败: %v", err)
|
||||
}
|
||||
// 获取新创建的临时用户ID
|
||||
userID, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的临时用户ID失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 使用已存在的临时用户ID
|
||||
userID = userTemp.Id
|
||||
}
|
||||
userType = model.UserTypeTemp
|
||||
}
|
||||
|
||||
// 4. 生成JWT Token
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成JWT Token失败: %v", err)
|
||||
}
|
||||
|
||||
// 5. 返回登录结果
|
||||
now := time.Now().Unix()
|
||||
return &types.WXMiniAuthResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SessionKeyResp 小程序登录返回结构
|
||||
type SessionKeyResp struct {
|
||||
Openid string `json:"openid"`
|
||||
SessionKey string `json:"session_key"`
|
||||
Unionid string `json:"unionid,omitempty"`
|
||||
ErrCode int `json:"errcode,omitempty"`
|
||||
ErrMsg string `json:"errmsg,omitempty"`
|
||||
}
|
||||
|
||||
// GetSessionKey 通过code获取小程序的session_key和openid
|
||||
func (l *WxMiniAuthLogic) GetSessionKey(code string) (*SessionKeyResp, error) {
|
||||
appID := l.svcCtx.Config.WechatMini.AppID
|
||||
appSecret := l.svcCtx.Config.WechatMini.AppSecret
|
||||
|
||||
url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
|
||||
appID, appSecret, code)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取session_key失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
var sessionKeyResp SessionKeyResp
|
||||
if err = json.Unmarshal(body, &sessionKeyResp); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 检查微信返回的错误码
|
||||
if sessionKeyResp.ErrCode != 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"微信接口返回错误: errcode=%d, errmsg=%s",
|
||||
sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg)
|
||||
}
|
||||
|
||||
// 验证必要字段
|
||||
if sessionKeyResp.Openid == "" || sessionKeyResp.SessionKey == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"微信接口返回数据不完整: openid=%s, session_key=%s",
|
||||
sessionKeyResp.Openid, sessionKeyResp.SessionKey)
|
||||
}
|
||||
|
||||
return &sessionKeyResp, nil
|
||||
}
|
||||
|
||||
@@ -2,11 +2,8 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/common/ctxdata"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
@@ -41,17 +38,14 @@ func (m *AuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFu
|
||||
}
|
||||
|
||||
// 解析JWT令牌
|
||||
userId, err := jwtx.ParseJwtToken(authHeader, m.Config.JwtAuth.AccessSecret)
|
||||
claims, err := jwtx.ParseJwtToken(authHeader, m.Config.JwtAuth.AccessSecret)
|
||||
if err != nil {
|
||||
// JWT解析失败,返回401错误
|
||||
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户ID转换为json.Number类型后添加到请求上下文
|
||||
userIdStr := fmt.Sprintf("%d", userId)
|
||||
userIdJsonNum := json.Number(userIdStr)
|
||||
ctx := context.WithValue(r.Context(), ctxdata.CtxKeyJwtUserId, userIdJsonNum)
|
||||
ctx := context.WithValue(r.Context(), jwtx.ExtraKey, claims)
|
||||
|
||||
// 使用新的上下文继续处理请求
|
||||
next(w, r.WithContext(ctx))
|
||||
|
||||
@@ -6,28 +6,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
BrandKey = "X-Brand"
|
||||
PlatformKey = "X-Platform"
|
||||
)
|
||||
|
||||
type SourceInterceptorMiddleware struct {
|
||||
}
|
||||
|
||||
func NewSourceInterceptorMiddleware() *SourceInterceptorMiddleware {
|
||||
return &SourceInterceptorMiddleware{}
|
||||
}
|
||||
|
||||
func (m *SourceInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
func GlobalSourceInterceptor(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// 获取请求头 X-Brand 和 X-Platform 的值
|
||||
brand := r.Header.Get(BrandKey)
|
||||
// 获取请求头 X-Platform 的值
|
||||
platform := r.Header.Get(PlatformKey)
|
||||
|
||||
// 将值放入新的 context 中
|
||||
ctx := r.Context()
|
||||
if brand != "" {
|
||||
ctx = context.WithValue(ctx, "brand", brand)
|
||||
}
|
||||
if platform != "" {
|
||||
ctx = context.WithValue(ctx, "platform", platform)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type UserAuthInterceptorMiddleware struct {
|
||||
}
|
||||
|
||||
func NewUserAuthInterceptorMiddleware() *UserAuthInterceptorMiddleware {
|
||||
return &UserAuthInterceptorMiddleware{}
|
||||
}
|
||||
|
||||
func (m *UserAuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(r.Context())
|
||||
if err != nil {
|
||||
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err))
|
||||
return
|
||||
}
|
||||
if claims.UserType == model.UserTypeTemp {
|
||||
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(xerr.USER_NEED_BIND_MOBILE), "token解析失败: %v", err))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
@@ -37,5 +37,5 @@ func (l *CleanQueryDataHandler) ProcessTask(ctx context.Context, t *asynq.Task)
|
||||
}
|
||||
|
||||
logx.Infof("%s - 查询数据清理完成,共删除 %d 条记录", time.Now().Format("2006-01-02 15:04:05"), result)
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type PaySuccessNotifyUserHandler struct {
|
||||
@@ -135,41 +136,131 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
|
||||
// 退款
|
||||
if order.PaymentPlatform == "wechat" {
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
logx.Error(refundErr)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
} else {
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
logx.Error(refundErr)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
if refund.IsSuccess() {
|
||||
logx.Errorf("支付宝退款成功, orderID: %d", order.Id)
|
||||
// 更新订单状态为退款
|
||||
order.Status = "refunded"
|
||||
order.RefundTime = sql.NullTime{
|
||||
Time: time.Now(),
|
||||
Valid: true,
|
||||
}
|
||||
updateOrderErr := l.svcCtx.OrderModel.UpdateWithVersion(ctx, nil, order)
|
||||
if updateOrderErr != nil {
|
||||
logx.Errorf("更新订单状态失败,订单ID: %d, 错误: %v", order.Id, updateOrderErr)
|
||||
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
|
||||
}
|
||||
return asynq.SkipRetry
|
||||
} else {
|
||||
logx.Errorf("支付宝退款失败:%v", refundErr)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
// 直接成功
|
||||
// 发起退款并创建退款记录
|
||||
refundErr := l.processRefund(ctx, order, "业务处理失败,自动退款")
|
||||
if refundErr != nil {
|
||||
logx.Errorf("退款处理失败,订单ID: %d, 错误: %v", order.Id, refundErr)
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return asynq.SkipRetry
|
||||
}
|
||||
|
||||
// processRefund 处理退款逻辑
|
||||
func (l *PaySuccessNotifyUserHandler) processRefund(ctx context.Context, order *model.Order, refundReason string) error {
|
||||
refundNo := fmt.Sprintf("refund-%s", order.OrderNo)
|
||||
|
||||
if order.PaymentPlatform == "wechat" {
|
||||
// 微信退款(异步)
|
||||
refundErr := l.svcCtx.WechatPayService.WeChatRefund(ctx, order.OrderNo, order.Amount, order.Amount)
|
||||
if refundErr != nil {
|
||||
// 微信退款调用失败,创建失败记录
|
||||
createRefundErr := l.createRefundRecord(ctx, order, refundNo, "", model.OrderRefundStatusFailed, refundReason)
|
||||
if createRefundErr != nil {
|
||||
logx.Errorf("创建微信退款失败记录时出错: %v", createRefundErr)
|
||||
}
|
||||
return refundErr
|
||||
}
|
||||
|
||||
// 微信退款调用成功,创建pending记录并更新订单状态
|
||||
return l.svcCtx.OrderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 创建pending状态的退款记录
|
||||
if err := l.createRefundRecordWithSession(ctx, session, order, refundNo, "", model.OrderRefundStatusPending, refundReason); err != nil {
|
||||
return fmt.Errorf("创建微信退款记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单状态为退款中
|
||||
order.Status = model.OrderStatusRefunding
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
} else if order.PaymentPlatform == "alipay" {
|
||||
// 支付宝退款(同步)
|
||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
||||
if refundErr != nil {
|
||||
// 支付宝退款调用失败,创建失败记录
|
||||
createRefundErr := l.createRefundRecord(ctx, order, refundNo, "", model.OrderRefundStatusFailed, refundReason)
|
||||
if createRefundErr != nil {
|
||||
logx.Errorf("创建支付宝退款失败记录时出错: %v", createRefundErr)
|
||||
}
|
||||
return refundErr
|
||||
}
|
||||
|
||||
if refund.IsSuccess() {
|
||||
// 支付宝退款成功,创建成功记录并更新订单状态
|
||||
return l.svcCtx.OrderModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 创建成功状态的退款记录
|
||||
if err := l.createRefundRecordWithSession(ctx, session, order, refundNo, refund.TradeNo, model.OrderRefundStatusSuccess, refundReason); err != nil {
|
||||
return fmt.Errorf("创建支付宝退款成功记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新订单状态为已退款
|
||||
order.Status = model.OrderStatusRefunded
|
||||
order.RefundTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if _, err := l.svcCtx.OrderModel.Update(ctx, session, order); err != nil {
|
||||
return fmt.Errorf("更新订单状态失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
// 支付宝退款失败,创建失败记录
|
||||
createRefundErr := l.createRefundRecord(ctx, order, refundNo, refund.TradeNo, model.OrderRefundStatusFailed, refundReason)
|
||||
if createRefundErr != nil {
|
||||
logx.Errorf("创建支付宝退款失败记录时出错: %v", createRefundErr)
|
||||
}
|
||||
return fmt.Errorf("支付宝退款失败: %s", refund.Msg)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("不支持的支付平台: %s", order.PaymentPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
// createRefundRecord 创建退款记录(无事务)
|
||||
func (l *PaySuccessNotifyUserHandler) createRefundRecord(ctx context.Context, order *model.Order, refundNo, platformRefundId, status, reason string) error {
|
||||
refund := &model.OrderRefund{
|
||||
RefundNo: refundNo,
|
||||
PlatformRefundId: l.createNullString(platformRefundId),
|
||||
OrderId: order.Id,
|
||||
UserId: order.UserId,
|
||||
ProductId: order.ProductId,
|
||||
RefundAmount: order.Amount,
|
||||
RefundReason: l.createNullString(reason),
|
||||
Status: status,
|
||||
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.OrderRefundModel.Insert(ctx, nil, refund)
|
||||
return err
|
||||
}
|
||||
|
||||
// createRefundRecordWithSession 创建退款记录(带事务)
|
||||
func (l *PaySuccessNotifyUserHandler) createRefundRecordWithSession(ctx context.Context, session sqlx.Session, order *model.Order, refundNo, platformRefundId, status, reason string) error {
|
||||
refund := &model.OrderRefund{
|
||||
RefundNo: refundNo,
|
||||
PlatformRefundId: l.createNullString(platformRefundId),
|
||||
OrderId: order.Id,
|
||||
UserId: order.UserId,
|
||||
ProductId: order.ProductId,
|
||||
RefundAmount: order.Amount,
|
||||
RefundReason: l.createNullString(reason),
|
||||
Status: status,
|
||||
RefundTime: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.OrderRefundModel.Insert(ctx, session, refund)
|
||||
return err
|
||||
}
|
||||
|
||||
// createNullString 创建 sql.NullString
|
||||
func (l *PaySuccessNotifyUserHandler) createNullString(value string) sql.NullString {
|
||||
return sql.NullString{
|
||||
String: value,
|
||||
Valid: value != "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
|
||||
|
||||
// AncestorCommission 直推报告上级佣金(奖励型)
|
||||
func (l *AgentService) AncestorCommission(ctx context.Context, descendantId int64, ancestorId int64, session sqlx.Session) (float64, error) {
|
||||
agentModel, err := l.AgentModel.FindOneByUserId(ctx, ancestorId)
|
||||
agentModel, err := l.AgentModel.FindOne(ctx, ancestorId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -290,26 +290,29 @@ func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, A
|
||||
if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
|
||||
// 拥有则查看该上级设定的成本
|
||||
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
|
||||
if findAgentMembershipUserConfigModelErr != nil {
|
||||
if findAgentMembershipUserConfigModelErr != nil && !errors.Is(findAgentMembershipUserConfigModelErr, model.ErrNotFound) {
|
||||
return 0, findAgentMembershipUserConfigModelErr
|
||||
}
|
||||
if agentMembershipUserConfigModel != nil {
|
||||
deductCostAmount := agentMembershipUserConfigModel.PriceIncreaseAmount
|
||||
|
||||
deductCostAmount := agentMembershipUserConfigModel.PriceIncreaseAmount
|
||||
agentCommissionDeductionModel := model.AgentCommissionDeduction{
|
||||
AgentId: AncestorId,
|
||||
DeductedAgentId: descendantId,
|
||||
Amount: deductCostAmount,
|
||||
Type: model.AgentDeductionTypeCost,
|
||||
ProductId: productID,
|
||||
}
|
||||
|
||||
agentCommissionDeductionModel := model.AgentCommissionDeduction{
|
||||
AgentId: AncestorId,
|
||||
DeductedAgentId: descendantId,
|
||||
Amount: deductCostAmount,
|
||||
Type: model.AgentDeductionTypeCost,
|
||||
ProductId: productID,
|
||||
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
|
||||
if insertAgentCommissionDeductionModelErr != nil {
|
||||
return 0, insertAgentCommissionDeductionModelErr
|
||||
}
|
||||
|
||||
return deductCostAmount, nil
|
||||
} else {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
|
||||
if insertAgentCommissionDeductionModelErr != nil {
|
||||
return 0, insertAgentCommissionDeductionModelErr
|
||||
}
|
||||
|
||||
return deductCostAmount, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
@@ -321,9 +324,18 @@ func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64
|
||||
// 拥有则查看该上级设定的成本
|
||||
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
|
||||
if findAgentMembershipUserConfigModelErr != nil {
|
||||
if errors.Is(findAgentMembershipUserConfigModelErr, model.ErrNotFound) {
|
||||
// 如果找不到配置,直接返回0,不计算提价成本
|
||||
return 0, nil
|
||||
}
|
||||
return 0, findAgentMembershipUserConfigModelErr
|
||||
}
|
||||
|
||||
// 确保配置存在且有效
|
||||
if agentMembershipUserConfigModel == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// 计算是否在范围内
|
||||
var pricingRange float64
|
||||
if pricing > agentMembershipUserConfigModel.PriceRangeFrom {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
@@ -101,13 +102,13 @@ func (a *AliPayService) CreateAlipayOrder(ctx context.Context, amount float64, s
|
||||
// 根据 ctx 中的 platform 判断平台
|
||||
platform, platformOk := ctx.Value("platform").(string)
|
||||
if !platformOk {
|
||||
return "", fmt.Errorf("无的支付平台: %s", platform)
|
||||
return "", fmt.Errorf("支付平台不存在: %s", platform)
|
||||
}
|
||||
switch platform {
|
||||
case "app":
|
||||
case model.PlatformApp:
|
||||
// 调用App支付的创建方法
|
||||
return a.CreateAlipayAppOrder(amount, subject, outTradeNo)
|
||||
case "h5":
|
||||
case model.PlatformH5:
|
||||
// 调用H5支付的创建方法,并传入 returnUrl
|
||||
return a.CreateAlipayH5Order(amount, subject, outTradeNo)
|
||||
default:
|
||||
|
||||
173
app/main/api/internal/service/imageService.go
Normal file
173
app/main/api/internal/service/imageService.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ImageService struct {
|
||||
baseImagePath string
|
||||
}
|
||||
|
||||
func NewImageService() *ImageService {
|
||||
return &ImageService{
|
||||
baseImagePath: "static/images", // 原图存放目录
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessImageWithQRCode 处理图片,在中间添加二维码
|
||||
func (s *ImageService) ProcessImageWithQRCode(qrcodeType, qrcodeUrl string) ([]byte, string, error) {
|
||||
// 1. 根据qrcodeType确定使用哪张背景图
|
||||
var backgroundImageName string
|
||||
switch qrcodeType {
|
||||
case "promote":
|
||||
backgroundImageName = "tg_qrcode_1.jpg"
|
||||
case "invitation":
|
||||
backgroundImageName = "yq_qrcode_1.png"
|
||||
default:
|
||||
backgroundImageName = "tg_qrcode_1.jpg" // 默认使用第一张图片
|
||||
}
|
||||
|
||||
// 2. 读取原图
|
||||
originalImagePath := filepath.Join(s.baseImagePath, backgroundImageName)
|
||||
originalImage, err := s.loadImage(originalImagePath)
|
||||
if err != nil {
|
||||
logx.Errorf("加载原图失败: %v, 图片路径: %s", err, originalImagePath)
|
||||
return nil, "", fmt.Errorf("加载原图失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 获取原图尺寸
|
||||
bounds := originalImage.Bounds()
|
||||
imgWidth := bounds.Dx()
|
||||
imgHeight := bounds.Dy()
|
||||
|
||||
// 4. 创建绘图上下文
|
||||
dc := gg.NewContext(imgWidth, imgHeight)
|
||||
|
||||
// 5. 绘制原图作为背景
|
||||
dc.DrawImageAnchored(originalImage, imgWidth/2, imgHeight/2, 0.5, 0.5)
|
||||
|
||||
// 6. 生成二维码(去掉白边)
|
||||
qrCode, err := qrcode.New(qrcodeUrl, qrcode.Medium)
|
||||
if err != nil {
|
||||
logx.Errorf("生成二维码失败: %v, 二维码内容: %s", err, qrcodeUrl)
|
||||
return nil, "", fmt.Errorf("生成二维码失败: %v", err)
|
||||
}
|
||||
// 禁用二维码边框,去掉白边
|
||||
qrCode.DisableBorder = true
|
||||
|
||||
// 7. 根据二维码类型设置不同的尺寸和位置
|
||||
var qrSize int
|
||||
var qrX, qrY int
|
||||
|
||||
switch qrcodeType {
|
||||
case "promote":
|
||||
// promote类型:精确设置二维码尺寸
|
||||
qrSize = 280 // 固定尺寸280px
|
||||
// 左下角位置:距左边和底边留一些边距
|
||||
qrX = 192 // 距左边180px
|
||||
qrY = imgHeight - qrSize - 190 // 距底边100px
|
||||
|
||||
case "invitation":
|
||||
// invitation类型:精确设置二维码尺寸
|
||||
qrSize = 360 // 固定尺寸320px
|
||||
// 中间偏上位置
|
||||
qrX = (imgWidth - qrSize) / 2 // 水平居中
|
||||
qrY = 555 // 垂直位置200px
|
||||
|
||||
default:
|
||||
// 默认(promote样式)
|
||||
qrSize = 280 // 固定尺寸280px
|
||||
qrX = 200 // 距左边180px
|
||||
qrY = imgHeight - qrSize - 200 // 距底边100px
|
||||
}
|
||||
|
||||
// 8. 生成指定尺寸的二维码图片
|
||||
qrCodeImage := qrCode.Image(qrSize)
|
||||
|
||||
// 9. 直接绘制二维码(不添加背景)
|
||||
dc.DrawImageAnchored(qrCodeImage, qrX+qrSize/2, qrY+qrSize/2, 0.5, 0.5)
|
||||
|
||||
// 11. 输出为字节数组
|
||||
var buf bytes.Buffer
|
||||
err = png.Encode(&buf, dc.Image())
|
||||
if err != nil {
|
||||
logx.Errorf("编码图片失败: %v", err)
|
||||
return nil, "", fmt.Errorf("编码图片失败: %v", err)
|
||||
}
|
||||
|
||||
logx.Infof("成功生成带二维码的图片,类型: %s, 二维码内容: %s, 图片尺寸: %dx%d, 二维码尺寸: %dx%d, 位置: (%d,%d)",
|
||||
qrcodeType, qrcodeUrl, imgWidth, imgHeight, qrSize, qrSize, qrX, qrY)
|
||||
|
||||
return buf.Bytes(), "image/png", nil
|
||||
}
|
||||
|
||||
// loadImage 加载图片文件
|
||||
func (s *ImageService) loadImage(path string) (image.Image, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 尝试解码PNG
|
||||
img, err := png.Decode(file)
|
||||
if err != nil {
|
||||
// 如果PNG解码失败,重新打开文件尝试JPEG
|
||||
file.Close()
|
||||
file, err = os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img, err = jpeg.Decode(file)
|
||||
if err != nil {
|
||||
// 如果还是失败,使用通用解码器
|
||||
file.Close()
|
||||
file, err = os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img, _, err = image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GetSupportedImageTypes 获取支持的图片类型列表
|
||||
func (s *ImageService) GetSupportedImageTypes() []string {
|
||||
return []string{"promote", "invitation"}
|
||||
}
|
||||
|
||||
// CheckImageExists 检查指定类型的背景图是否存在
|
||||
func (s *ImageService) CheckImageExists(qrcodeType string) bool {
|
||||
var backgroundImageName string
|
||||
switch qrcodeType {
|
||||
case "promote":
|
||||
backgroundImageName = "tg_qrcode_1.jpg"
|
||||
case "invitation":
|
||||
backgroundImageName = "yq_qrcode_1.png"
|
||||
default:
|
||||
backgroundImageName = "tg_qrcode_1.jpg"
|
||||
}
|
||||
|
||||
imagePath := filepath.Join(s.baseImagePath, backgroundImageName)
|
||||
_, err := os.Stat(imagePath)
|
||||
return err == nil
|
||||
}
|
||||
@@ -2,22 +2,34 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
"qnc-server/common/xerr"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
Config *config.Config
|
||||
userModel model.UserModel
|
||||
userAuthModel model.UserAuthModel
|
||||
userTempModel model.UserTempModel
|
||||
agentModel model.AgentModel
|
||||
}
|
||||
|
||||
// NewUserService 创建UserService实例
|
||||
func NewUserService(userModel model.UserModel, userAuthModel model.UserAuthModel) *UserService {
|
||||
func NewUserService(config *config.Config, userModel model.UserModel, userAuthModel model.UserAuthModel, userTempModel model.UserTempModel, agentModel model.AgentModel) *UserService {
|
||||
return &UserService{
|
||||
Config: config,
|
||||
userModel: userModel,
|
||||
userAuthModel: userAuthModel,
|
||||
userTempModel: userTempModel,
|
||||
agentModel: agentModel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,3 +75,222 @@ func (s *UserService) RegisterUUIDUser(ctx context.Context) (int64, error) {
|
||||
|
||||
return userId, nil
|
||||
}
|
||||
|
||||
// generalUserToken 生成用户token
|
||||
func (s *UserService) GeneralUserToken(ctx context.Context, userID int64, userType int64) (string, error) {
|
||||
platform, err := ctxdata.GetPlatformFromCtx(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var isAgent int64
|
||||
var agentID int64
|
||||
if userType == model.UserTypeNormal {
|
||||
agent, err := s.agentModel.FindOneByUserId(ctx, userID)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return "", err
|
||||
}
|
||||
if agent != nil {
|
||||
agentID = agent.Id
|
||||
isAgent = model.AgentStatusYes
|
||||
}
|
||||
} else {
|
||||
userTemp, err := s.userTempModel.FindOne(ctx, userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if userTemp != nil {
|
||||
userID = userTemp.Id
|
||||
}
|
||||
}
|
||||
token, generaErr := jwtx.GenerateJwtToken(jwtx.JwtClaims{
|
||||
UserId: userID,
|
||||
AgentId: agentID,
|
||||
Platform: platform,
|
||||
UserType: userType,
|
||||
IsAgent: isAgent,
|
||||
}, s.Config.JwtAuth.AccessSecret, s.Config.JwtAuth.AccessExpire)
|
||||
if generaErr != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新token, 生成token失败 : %d", userID)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// RegisterUser 注册用户,返回用户ID
|
||||
// 传入手机号,自动注册,如果ctx存在临时用户则临时用户转为正式用户
|
||||
func (s *UserService) RegisterUser(ctx context.Context, mobile string) (int64, error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return 0, err
|
||||
}
|
||||
user, err := s.userModel.FindOneByMobile(ctx, sql.NullString{String: mobile, Valid: true})
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return 0, err
|
||||
}
|
||||
if user != nil {
|
||||
return 0, errors.New("用户已注册")
|
||||
}
|
||||
// 普通注册
|
||||
if claims == nil {
|
||||
var userId int64
|
||||
err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
user := &model.User{
|
||||
Mobile: sql.NullString{String: mobile, Valid: true},
|
||||
}
|
||||
result, err := s.userModel.Insert(ctx, session, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userId, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.userAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
UserId: userId,
|
||||
AuthType: model.UserAuthTypeMobile,
|
||||
AuthKey: mobile,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return userId, nil
|
||||
}
|
||||
|
||||
// 双重判断是否已经注册
|
||||
if claims.UserType == model.UserTypeNormal {
|
||||
return 0, errors.New("用户已注册")
|
||||
}
|
||||
var userId int64
|
||||
// 临时转正式注册
|
||||
err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
user := &model.User{
|
||||
Mobile: sql.NullString{String: mobile, Valid: true},
|
||||
}
|
||||
result, err := s.userModel.Insert(ctx, session, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userId, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.userAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
UserId: userId,
|
||||
AuthType: model.UserAuthTypeMobile,
|
||||
AuthKey: mobile,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.TempUserBindUser(ctx, session, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return userId, nil
|
||||
}
|
||||
|
||||
// TempUserBindUser 临时用户绑定用户
|
||||
func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session, normalUserID int64) error {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return err
|
||||
}
|
||||
|
||||
if claims == nil || claims.UserType != model.UserTypeTemp {
|
||||
return errors.New("无临时用户")
|
||||
}
|
||||
|
||||
// 使用事务上下文查询临时用户
|
||||
userTemp, err := s.userTempModel.FindOne(ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查是否已经注册过
|
||||
userAuth, err := s.userAuthModel.FindOneByAuthTypeAuthKey(ctx, userTemp.AuthType, userTemp.AuthKey)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
if userAuth != nil {
|
||||
return errors.New("临时用户已注册")
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
err := s.userAuthModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
_, err = s.userAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
UserId: normalUserID,
|
||||
AuthType: userTemp.AuthType,
|
||||
AuthKey: userTemp.AuthKey,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 重新获取最新的userTemp数据,确保版本号是最新的
|
||||
latestUserTemp, err := s.userTempModel.FindOne(ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.userTempModel.DeleteSoft(ctx, session, latestUserTemp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
_, err = s.userAuthModel.Insert(ctx, session, &model.UserAuth{
|
||||
UserId: normalUserID,
|
||||
AuthType: userTemp.AuthType,
|
||||
AuthKey: userTemp.AuthKey,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 重新获取最新的userTemp数据,确保版本号是最新的
|
||||
latestUserTemp, err := s.userTempModel.FindOne(ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.userTempModel.DeleteSoft(ctx, session, latestUserTemp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// _bak_RegisterUUIDUser 注册UUID用户,返回用户ID
|
||||
func (s *UserService) _bak_RegisterUUIDUser(ctx context.Context) error {
|
||||
// 生成UUID
|
||||
uuidStr, err := s.GenerateUUIDUserId(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.userTempModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 创建用户临时记录
|
||||
userTemp := &model.UserTemp{
|
||||
AuthType: model.UserAuthTypeUUID,
|
||||
AuthKey: uuidStr,
|
||||
}
|
||||
_, err := s.userTempModel.Insert(ctx, session, userTemp)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ const (
|
||||
)
|
||||
|
||||
type WechatPayService struct {
|
||||
config config.WxpayConfig
|
||||
config config.Config
|
||||
wechatClient *core.Client
|
||||
notifyHandler *notify.Handler
|
||||
userAuthModel model.UserAuthModel
|
||||
@@ -96,7 +96,7 @@ func newWechatPayServiceWithPlatformCert(c config.Config, userAuthModel model.Us
|
||||
|
||||
logx.Infof("微信支付客户端初始化成功(平台证书方式)")
|
||||
return &WechatPayService{
|
||||
config: c.Wxpay,
|
||||
config: c,
|
||||
wechatClient: client,
|
||||
notifyHandler: notifyHandler,
|
||||
userAuthModel: userAuthModel,
|
||||
@@ -119,7 +119,7 @@ func newWechatPayServiceWithWxPayPubKey(c config.Config, userAuthModel model.Use
|
||||
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
|
||||
}
|
||||
|
||||
// 从文件中加载微信支付平台证书
|
||||
// 从文件中加载支付公钥
|
||||
mchPublicKey, err := utils.LoadPublicKeyWithPath(mchPublicKeyPath)
|
||||
if err != nil {
|
||||
logx.Errorf("加载微信支付平台证书失败: %v", err)
|
||||
@@ -135,14 +135,19 @@ func newWechatPayServiceWithWxPayPubKey(c config.Config, userAuthModel model.Use
|
||||
logx.Errorf("创建微信支付客户端失败: %v", err)
|
||||
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
|
||||
}
|
||||
|
||||
// 初始化 notify.Handler
|
||||
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(context.Background(), mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
|
||||
if err != nil {
|
||||
logx.Errorf("注册下载器失败: %v", err)
|
||||
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
|
||||
}
|
||||
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||
notifyHandler := notify.NewNotifyHandler(
|
||||
mchAPIv3Key,
|
||||
verifiers.NewSHA256WithRSAPubkeyVerifier(mchPublicKeyID, *mchPublicKey))
|
||||
verifiers.NewSHA256WithRSACombinedVerifier(certificateVisitor, mchPublicKeyID, *mchPublicKey))
|
||||
|
||||
logx.Infof("微信支付客户端初始化成功(微信支付公钥方式)")
|
||||
return &WechatPayService{
|
||||
config: c.Wxpay,
|
||||
config: c,
|
||||
wechatClient: client,
|
||||
notifyHandler: notifyHandler,
|
||||
userAuthModel: userAuthModel,
|
||||
@@ -155,11 +160,11 @@ func (w *WechatPayService) CreateWechatAppOrder(ctx context.Context, amount floa
|
||||
|
||||
// 构建支付请求参数
|
||||
payRequest := app.PrepayRequest{
|
||||
Appid: core.String(w.config.AppID),
|
||||
Mchid: core.String(w.config.MchID),
|
||||
Appid: core.String(w.config.Wxpay.AppID),
|
||||
Mchid: core.String(w.config.Wxpay.MchID),
|
||||
Description: core.String(description),
|
||||
OutTradeNo: core.String(outTradeNo),
|
||||
NotifyUrl: core.String(w.config.NotifyUrl),
|
||||
NotifyUrl: core.String(w.config.Wxpay.NotifyUrl),
|
||||
Amount: &app.Amount{
|
||||
Total: core.Int64(totalAmount),
|
||||
},
|
||||
@@ -184,11 +189,11 @@ func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amo
|
||||
|
||||
// 构建支付请求参数
|
||||
payRequest := jsapi.PrepayRequest{
|
||||
Appid: core.String(w.config.AppID),
|
||||
Mchid: core.String(w.config.MchID),
|
||||
Appid: core.String(w.config.WechatMini.AppID),
|
||||
Mchid: core.String(w.config.Wxpay.MchID),
|
||||
Description: core.String(description),
|
||||
OutTradeNo: core.String(outTradeNo),
|
||||
NotifyUrl: core.String(w.config.NotifyUrl),
|
||||
NotifyUrl: core.String(w.config.Wxpay.NotifyUrl),
|
||||
Amount: &jsapi.Amount{
|
||||
Total: core.Int64(totalAmount),
|
||||
},
|
||||
@@ -208,6 +213,37 @@ func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amo
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateWechatH5Order 创建微信H5支付订单
|
||||
func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float64, description string, outTradeNo string, openid string) (interface{}, error) {
|
||||
totalAmount := lzUtils.ToWechatAmount(amount)
|
||||
|
||||
// 构建支付请求参数
|
||||
payRequest := jsapi.PrepayRequest{
|
||||
Appid: core.String(w.config.WechatH5.AppID),
|
||||
Mchid: core.String(w.config.Wxpay.MchID),
|
||||
Description: core.String(description),
|
||||
OutTradeNo: core.String(outTradeNo),
|
||||
NotifyUrl: core.String(w.config.Wxpay.NotifyUrl),
|
||||
Amount: &jsapi.Amount{
|
||||
Total: core.Int64(totalAmount),
|
||||
},
|
||||
Payer: &jsapi.Payer{
|
||||
Openid: core.String(openid), // 用户的 OpenID,通过前端传入
|
||||
}}
|
||||
|
||||
// 初始化 AppApiService
|
||||
svc := jsapi.JsapiApiService{Client: w.wechatClient}
|
||||
|
||||
// 发起预支付请求
|
||||
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
|
||||
logx.Infof("微信h5支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
||||
}
|
||||
// 返回预支付交易会话标识
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
||||
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string) (interface{}, error) {
|
||||
// 根据 ctx 中的 platform 判断平台
|
||||
@@ -217,12 +253,12 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
||||
var err error
|
||||
|
||||
switch platform {
|
||||
case "mp-weixin":
|
||||
case model.PlatformWxMini:
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(ctx)
|
||||
if getUidErr != nil {
|
||||
return "", getUidErr
|
||||
}
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMini)
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", findAuthModelErr
|
||||
}
|
||||
@@ -230,20 +266,20 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "h5-weixin":
|
||||
case model.PlatformWxH5:
|
||||
userID, getUidErr := ctxdata.GetUidFromCtx(ctx)
|
||||
if getUidErr != nil {
|
||||
return "", getUidErr
|
||||
}
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5)
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", findAuthModelErr
|
||||
}
|
||||
prepayData, err = w.CreateWechatMiniProgramOrder(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "app":
|
||||
case model.PlatformApp:
|
||||
// 如果是 APP 平台,调用 APP 支付订单创建
|
||||
prepayData, err = w.CreateWechatAppOrder(ctx, amount, description, outTradeNo)
|
||||
default:
|
||||
@@ -271,6 +307,7 @@ func (w *WechatPayService) HandleWechatPayNotification(ctx context.Context, req
|
||||
}
|
||||
|
||||
// HandleRefundNotification 处理微信退款回调
|
||||
// 由于SDK解析退款状态refund.status为空,可以通过判断 w.notifyHandler.ParseNotifyRequest返回的notifyReq.EventType是否为REFUND.SUCCESS来判断退款状态
|
||||
func (w *WechatPayService) HandleRefundNotification(ctx context.Context, req *http.Request) (*refunddomestic.Refund, error) {
|
||||
refund := new(refunddomestic.Refund)
|
||||
_, err := w.notifyHandler.ParseNotifyRequest(ctx, req, refund)
|
||||
@@ -287,7 +324,7 @@ func (w *WechatPayService) QueryOrderStatus(ctx context.Context, transactionID s
|
||||
// 调用 QueryOrderById 方法查询订单状态
|
||||
resp, result, err := svc.QueryOrderById(ctx, jsapi.QueryOrderByIdRequest{
|
||||
TransactionId: core.String(transactionID),
|
||||
Mchid: core.String(w.config.MchID),
|
||||
Mchid: core.String(w.config.Wxpay.MchID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
||||
@@ -309,7 +346,7 @@ func (w *WechatPayService) WeChatRefund(ctx context.Context, outTradeNo string,
|
||||
resp, result, err := svc.Create(ctx, refunddomestic.CreateRequest{
|
||||
OutTradeNo: core.String(outTradeNo),
|
||||
OutRefundNo: core.String(outRefundNo),
|
||||
NotifyUrl: core.String(w.config.RefundNotifyUrl),
|
||||
NotifyUrl: core.String(w.config.Wxpay.RefundNotifyUrl),
|
||||
Amount: &refunddomestic.AmountReq{
|
||||
Currency: core.String("CNY"),
|
||||
Refund: core.Int64(lzUtils.ToWechatAmount(refundAmount)),
|
||||
|
||||
@@ -21,12 +21,12 @@ type ServiceContext struct {
|
||||
Redis *redis.Redis
|
||||
|
||||
// 中间件
|
||||
SourceInterceptor rest.Middleware
|
||||
AuthInterceptor rest.Middleware
|
||||
|
||||
AuthInterceptor rest.Middleware
|
||||
UserAuthInterceptor rest.Middleware
|
||||
// 用户相关模型
|
||||
UserModel model.UserModel
|
||||
UserAuthModel model.UserAuthModel
|
||||
UserTempModel model.UserTempModel
|
||||
|
||||
// 产品相关模型
|
||||
ProductModel model.ProductModel
|
||||
@@ -60,6 +60,8 @@ type ServiceContext struct {
|
||||
AgentPlatformDeductionModel model.AgentPlatformDeductionModel
|
||||
AgentActiveStatModel model.AgentActiveStatModel
|
||||
AgentWithdrawalModel model.AgentWithdrawalModel
|
||||
AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel
|
||||
AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel
|
||||
AgentRealNameModel model.AgentRealNameModel
|
||||
|
||||
// 管理后台相关模型
|
||||
@@ -95,6 +97,7 @@ type ServiceContext struct {
|
||||
UserService *service.UserService
|
||||
DictService *service.DictService
|
||||
AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService
|
||||
ImageService *service.ImageService
|
||||
|
||||
// core service
|
||||
CloudAuthService *cloudauth.CloudAuthClient
|
||||
@@ -104,12 +107,14 @@ type ServiceContext struct {
|
||||
type userModels struct {
|
||||
UserModel model.UserModel
|
||||
UserAuthModel model.UserAuthModel
|
||||
UserTempModel model.UserTempModel
|
||||
}
|
||||
|
||||
func initUserModels(db sqlx.SqlConn, redis cache.CacheConf) userModels {
|
||||
return userModels{
|
||||
UserModel: model.NewUserModel(db, redis),
|
||||
UserAuthModel: model.NewUserAuthModel(db, redis),
|
||||
UserTempModel: model.NewUserTempModel(db, redis),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +176,8 @@ type agentModels struct {
|
||||
AgentPlatformDeductionModel model.AgentPlatformDeductionModel
|
||||
AgentActiveStatModel model.AgentActiveStatModel
|
||||
AgentWithdrawalModel model.AgentWithdrawalModel
|
||||
AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel
|
||||
AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel
|
||||
AgentRealNameModel model.AgentRealNameModel
|
||||
}
|
||||
|
||||
@@ -192,6 +199,8 @@ func initAgentModels(db sqlx.SqlConn, redis cache.CacheConf) agentModels {
|
||||
AgentPlatformDeductionModel: model.NewAgentPlatformDeductionModel(db, redis),
|
||||
AgentActiveStatModel: model.NewAgentActiveStatModel(db, redis),
|
||||
AgentWithdrawalModel: model.NewAgentWithdrawalModel(db, redis),
|
||||
AgentWithdrawalTaxModel: model.NewAgentWithdrawalTaxModel(db, redis),
|
||||
AgentWithdrawalTaxExemptionModel: model.NewAgentWithdrawalTaxExemptionModel(db, redis),
|
||||
AgentRealNameModel: model.NewAgentRealNameModel(db, redis),
|
||||
}
|
||||
}
|
||||
@@ -259,14 +268,14 @@ type services struct {
|
||||
UserService *service.UserService
|
||||
DictService *service.DictService
|
||||
AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService
|
||||
|
||||
CloudAuthService *cloudauth.CloudAuthClient
|
||||
ImageService *service.ImageService
|
||||
CloudAuthService *cloudauth.CloudAuthClient
|
||||
}
|
||||
|
||||
func initServices(c config.Config, userAuthModel model.UserAuthModel, westDexService *service.WestDexService,
|
||||
yushanService *service.YushanService, featureModel model.FeatureModel,
|
||||
productFeatureModel model.ProductFeatureModel, agentModels agentModels,
|
||||
userModels userModels, adminModels adminModels) services {
|
||||
userModels userModels, adminModels adminModels, userTempModel model.UserTempModel) services {
|
||||
|
||||
alipayService := service.NewAliPayService(c)
|
||||
wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey)
|
||||
@@ -282,9 +291,10 @@ func initServices(c config.Config, userAuthModel model.UserAuthModel, westDexSer
|
||||
agentModels.AgentMembershipUserConfigModel, agentModels.AgentProductConfigModel,
|
||||
agentModels.AgentPlatformDeductionModel, agentModels.AgentActiveStatModel,
|
||||
asynqService, agentModels.AgentWithdrawalModel)
|
||||
userService := service.NewUserService(userModels.UserModel, userModels.UserAuthModel)
|
||||
userService := service.NewUserService(&c, userModels.UserModel, userModels.UserAuthModel, userTempModel, agentModels.AgentModel)
|
||||
dictService := service.NewDictService(adminModels.AdminDictTypeModel, adminModels.AdminDictDataModel)
|
||||
AdminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminModels.AdminPromotionLinkModel, adminModels.AdminPromotionLinkStatsTotalModel, adminModels.AdminPromotionLinkStatsHistoryModel)
|
||||
imageService := service.NewImageService()
|
||||
asynqServer := asynq.NewServer(
|
||||
asynq.RedisClientOpt{Addr: c.CacheRedis[0].Host, Password: c.CacheRedis[0].Pass},
|
||||
asynq.Config{
|
||||
@@ -293,6 +303,10 @@ func initServices(c config.Config, userAuthModel model.UserAuthModel, westDexSer
|
||||
return true
|
||||
},
|
||||
Concurrency: 10,
|
||||
Queues: map[string]int{
|
||||
"default": 10,
|
||||
"agent_balance": 6,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -322,7 +336,7 @@ func initServices(c config.Config, userAuthModel model.UserAuthModel, westDexSer
|
||||
UserService: userService,
|
||||
DictService: dictService,
|
||||
AdminPromotionLinkStatsService: AdminPromotionLinkStatsService,
|
||||
|
||||
ImageService: imageService,
|
||||
// core service
|
||||
CloudAuthService: cloudAuthService,
|
||||
}
|
||||
@@ -357,18 +371,17 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
|
||||
// 初始化所有服务
|
||||
services := initServices(c, userModels.UserAuthModel, westDexService, yushanService,
|
||||
productModels.FeatureModel, productModels.ProductFeatureModel, agentModels, userModels, adminModels)
|
||||
productModels.FeatureModel, productModels.ProductFeatureModel, agentModels, userModels, adminModels, userModels.UserTempModel)
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
Redis: redisClient,
|
||||
SourceInterceptor: middleware.NewSourceInterceptorMiddleware().Handle,
|
||||
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
|
||||
|
||||
Config: c,
|
||||
Redis: redisClient,
|
||||
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
|
||||
UserAuthInterceptor: middleware.NewUserAuthInterceptorMiddleware().Handle,
|
||||
// 用户相关模型
|
||||
UserModel: userModels.UserModel,
|
||||
UserAuthModel: userModels.UserAuthModel,
|
||||
|
||||
UserTempModel: userModels.UserTempModel,
|
||||
// 产品相关模型
|
||||
ProductModel: productModels.ProductModel,
|
||||
FeatureModel: productModels.FeatureModel,
|
||||
@@ -377,6 +390,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
// 订单相关模型
|
||||
OrderModel: orderModels.OrderModel,
|
||||
QueryModel: orderModels.QueryModel,
|
||||
AuthorizationModel: orderModels.AuthorizationModel,
|
||||
AuthorizationFaceModel: orderModels.AuthorizationFaceModel,
|
||||
OrderRefundModel: orderModels.OrderRefundModel,
|
||||
QueryCleanupLogModel: orderModels.QueryCleanupLogModel,
|
||||
QueryCleanupDetailModel: orderModels.QueryCleanupDetailModel,
|
||||
@@ -398,6 +413,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
AgentPlatformDeductionModel: agentModels.AgentPlatformDeductionModel,
|
||||
AgentActiveStatModel: agentModels.AgentActiveStatModel,
|
||||
AgentWithdrawalModel: agentModels.AgentWithdrawalModel,
|
||||
AgentWithdrawalTaxModel: agentModels.AgentWithdrawalTaxModel,
|
||||
AgentWithdrawalTaxExemptionModel: agentModels.AgentWithdrawalTaxExemptionModel,
|
||||
AgentRealNameModel: agentModels.AgentRealNameModel,
|
||||
|
||||
// 管理后台相关模型
|
||||
@@ -433,6 +450,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
UserService: services.UserService,
|
||||
DictService: services.DictService,
|
||||
AdminPromotionLinkStatsService: services.AdminPromotionLinkStatsService,
|
||||
ImageService: services.ImageService,
|
||||
|
||||
// core service
|
||||
CloudAuthService: services.CloudAuthService,
|
||||
|
||||
@@ -962,6 +962,9 @@ type BindMobileReq struct {
|
||||
}
|
||||
|
||||
type BindMobileResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
|
||||
type Commission struct {
|
||||
@@ -1063,6 +1066,11 @@ type FeatureListItem struct {
|
||||
UpdateTime string `json:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
type GetAgentPromotionQrcodeReq struct {
|
||||
QrcodeType string `form:"qrcode_type"`
|
||||
QrcodeUrl string `form:"qrcode_url"`
|
||||
}
|
||||
|
||||
type GetAgentRevenueInfoReq struct {
|
||||
}
|
||||
|
||||
@@ -1276,6 +1284,16 @@ type GetWithdrawalResp struct {
|
||||
List []Withdrawal `json:"list"` // 查询列表
|
||||
}
|
||||
|
||||
type GetWithdrawalTaxExemptionReq struct {
|
||||
}
|
||||
|
||||
type GetWithdrawalTaxExemptionResp struct {
|
||||
TotalExemptionAmount float64 `json:"total_exemption_amount"`
|
||||
UsedExemptionAmount float64 `json:"used_exemption_amount"`
|
||||
RemainingExemptionAmount float64 `json:"remaining_exemption_amount"`
|
||||
TaxRate float64 `json:"tax_rate"`
|
||||
}
|
||||
|
||||
type HealthCheckResp struct {
|
||||
Status string `json:"status"` // 服务状态
|
||||
Message string `json:"message"` // 状态信息
|
||||
@@ -1323,17 +1341,6 @@ type MobileCodeLoginResp struct {
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
|
||||
type MobileLoginReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
type MobileLoginResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Title string `json:"title"` // 通知标题
|
||||
Content string `json:"content"` // 通知内容 (富文本)
|
||||
@@ -1659,18 +1666,6 @@ type RecordLinkClickResp struct {
|
||||
Success bool `json:"success"` // 是否成功
|
||||
}
|
||||
|
||||
type RegisterReq struct {
|
||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type RegisterResp struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
AccessExpire int64 `json:"accessExpire"`
|
||||
RefreshAfter int64 `json:"refreshAfter"`
|
||||
}
|
||||
|
||||
type RejectAuthorizationReq struct {
|
||||
OrderNo string `json:"order_no" validate:"required"` // 订单号
|
||||
}
|
||||
@@ -1773,6 +1768,7 @@ type User struct {
|
||||
Id int64 `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
NickName string `json:"nickName"`
|
||||
UserType int64 `json:"userType"`
|
||||
}
|
||||
|
||||
type UserInfoResp struct {
|
||||
@@ -1790,9 +1786,7 @@ type WXH5AuthResp struct {
|
||||
}
|
||||
|
||||
type WXMiniAuthReq struct {
|
||||
Code string `json:"code"`
|
||||
IV string `json:"iv"`
|
||||
EncryptedData string `json:"encryptedData"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type WXMiniAuthResp struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/app/main/api/internal/handler"
|
||||
"qnc-server/app/main/api/internal/middleware"
|
||||
"qnc-server/app/main/api/internal/queue"
|
||||
"qnc-server/app/main/api/internal/svc"
|
||||
|
||||
@@ -26,7 +27,7 @@ func main() {
|
||||
// 根据 ENV 加载不同的配置文件
|
||||
var defaultConfigFile string
|
||||
if env == "development" {
|
||||
defaultConfigFile = "app/main//api/etc/main.dev.yaml"
|
||||
defaultConfigFile = "app/main/api/etc/main.dev.yaml"
|
||||
} else {
|
||||
defaultConfigFile = "etc/main.yaml"
|
||||
}
|
||||
@@ -53,10 +54,9 @@ func main() {
|
||||
}
|
||||
fmt.Println("异步任务启动!!!")
|
||||
}()
|
||||
// go func() {
|
||||
// script.RunEncryptMobile()
|
||||
// }()
|
||||
|
||||
server := rest.MustNewServer(c.RestConf)
|
||||
server.Use(middleware.GlobalSourceInterceptor)
|
||||
defer server.Stop()
|
||||
|
||||
handler.RegisterHandlers(server, svcContext)
|
||||
|
||||
49
app/main/api/static/images/README.md
Normal file
49
app/main/api/static/images/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 推广二维码背景图片说明
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
static/images/
|
||||
├── tg_qrcode_1.jpg # promote 类型的背景图片(推广)
|
||||
├── yq_qrcode_1.png # invitation 类型的背景图片(邀请)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 支持的二维码类型
|
||||
|
||||
- `promote`: 使用 tg_qrcode_1.jpg 作为背景(推广二维码)
|
||||
- `invitation`: 使用 yq_qrcode_1.png 作为背景(邀请二维码)
|
||||
|
||||
## 路径计算说明
|
||||
|
||||
程序运行时的路径计算:
|
||||
|
||||
- 工作目录:`D:\Code\qnc-project\qnc-server`
|
||||
- 相对路径:`static/images`
|
||||
- 完整路径示例:
|
||||
- promote: `D:\Code\qnc-project\qnc-server\static\images\tg_qrcode_1.jpg`
|
||||
- invitation: `D:\Code\qnc-project\qnc-server\static\images\yq_qrcode_1.png`
|
||||
|
||||
## 背景图片要求
|
||||
|
||||
- 格式:支持 PNG、JPEG 等常见格式
|
||||
- 尺寸:建议不小于 600x600 像素
|
||||
- 设计:考虑到二维码会放置在图片中心,请在设计时预留中心区域
|
||||
|
||||
## API 使用示例
|
||||
|
||||
```bash
|
||||
# 生成推广二维码
|
||||
GET /api/v1/agent/promotion/qrcode?qrcode_type=promote&qrcode_url=https://example.com/promotion/12345
|
||||
|
||||
# 生成邀请二维码
|
||||
GET /api/v1/agent/promotion/qrcode?qrcode_type=invitation&qrcode_url=https://example.com/invite/67890
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保 tg_qrcode_1.jpg 和 yq_qrcode_1.png 文件存在于此目录下
|
||||
2. 二维码会自动添加白色半透明背景以确保可读性
|
||||
3. 二维码大小会根据背景图片尺寸自动调整,范围在 120px-300px 之间
|
||||
4. 默认二维码类型是 `promote`
|
||||
5. 程序启动时必须在项目根目录(qnc-server)下执行
|
||||
BIN
app/main/api/static/images/image_placeholder.txt
Normal file
BIN
app/main/api/static/images/image_placeholder.txt
Normal file
Binary file not shown.
BIN
app/main/api/static/images/tg_qrcode_1.jpg
Normal file
BIN
app/main/api/static/images/tg_qrcode_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
BIN
app/main/api/static/images/yq_qrcode_1.png
Normal file
BIN
app/main/api/static/images/yq_qrcode_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 575 KiB |
@@ -57,11 +57,13 @@ type (
|
||||
|
||||
AgentWithdrawal struct {
|
||||
Id int64 `db:"id"`
|
||||
AgentId int64 `db:"agent_id"` // 代理ID
|
||||
WithdrawNo string `db:"withdraw_no"` // 提现单号
|
||||
Amount float64 `db:"amount"` // 提现金额
|
||||
Status int64 `db:"status"` // 状态:1-申请中,2-成功,3-失败
|
||||
PayeeAccount string `db:"payeeAccount"` // 收款人账号
|
||||
AgentId int64 `db:"agent_id"` // 代理ID
|
||||
WithdrawNo string `db:"withdraw_no"` // 提现单号
|
||||
Amount float64 `db:"amount"` // 提现金额
|
||||
ActualAmount float64 `db:"actual_amount"` // 实际到账金额(扣税后)
|
||||
TaxAmount float64 `db:"tax_amount"` // 扣税金额
|
||||
Status int64 `db:"status"` // 状态:1-申请中,2-成功,3-失败
|
||||
PayeeAccount string `db:"payeeAccount"` // 收款人账号
|
||||
Remark sql.NullString `db:"remark"`
|
||||
CreateTime time.Time `db:"create_time"` // 创建时间
|
||||
UpdateTime time.Time `db:"update_time"` // 更新时间
|
||||
@@ -83,11 +85,11 @@ func (m *defaultAgentWithdrawalModel) Insert(ctx context.Context, session sqlx.S
|
||||
qncAgentWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalIdPrefix, data.Id)
|
||||
qncAgentWithdrawalWithdrawNoKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalWithdrawNoPrefix, 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.AgentId, data.WithdrawNo, data.Amount, data.Status, data.PayeeAccount, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||
return session.ExecCtx(ctx, query, data.AgentId, data.WithdrawNo, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.PayeeAccount, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.AgentId, data.WithdrawNo, data.Amount, data.Status, data.PayeeAccount, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||
return conn.ExecCtx(ctx, query, data.AgentId, data.WithdrawNo, data.Amount, data.ActualAmount, data.TaxAmount, data.Status, data.PayeeAccount, data.Remark, data.DeleteTime, data.DelState, data.Version)
|
||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||
}
|
||||
|
||||
@@ -138,9 +140,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.Amount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||
}
|
||||
|
||||
@@ -161,9 +163,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.Amount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
return session.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
return conn.ExecCtx(ctx, query, newData.AgentId, newData.WithdrawNo, newData.Amount, newData.ActualAmount, newData.TaxAmount, newData.Status, newData.PayeeAccount, newData.Remark, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
}, qncAgentWithdrawalIdKey, qncAgentWithdrawalWithdrawNoKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
27
app/main/model/agentWithdrawalTaxExemptionModel.go
Normal file
27
app/main/model/agentWithdrawalTaxExemptionModel.go
Normal 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 _ AgentWithdrawalTaxExemptionModel = (*customAgentWithdrawalTaxExemptionModel)(nil)
|
||||
|
||||
type (
|
||||
// AgentWithdrawalTaxExemptionModel is an interface to be customized, add more methods here,
|
||||
// and implement the added methods in customAgentWithdrawalTaxExemptionModel.
|
||||
AgentWithdrawalTaxExemptionModel interface {
|
||||
agentWithdrawalTaxExemptionModel
|
||||
}
|
||||
|
||||
customAgentWithdrawalTaxExemptionModel struct {
|
||||
*defaultAgentWithdrawalTaxExemptionModel
|
||||
}
|
||||
)
|
||||
|
||||
// NewAgentWithdrawalTaxExemptionModel returns a model for the database table.
|
||||
func NewAgentWithdrawalTaxExemptionModel(conn sqlx.SqlConn, c cache.CacheConf) AgentWithdrawalTaxExemptionModel {
|
||||
return &customAgentWithdrawalTaxExemptionModel{
|
||||
defaultAgentWithdrawalTaxExemptionModel: newAgentWithdrawalTaxExemptionModel(conn, c),
|
||||
}
|
||||
}
|
||||
410
app/main/model/agentWithdrawalTaxExemptionModel_gen.go
Normal file
410
app/main/model/agentWithdrawalTaxExemptionModel_gen.go
Normal file
@@ -0,0 +1,410 @@
|
||||
// Code generated by goctl. DO NOT EDIT!
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
"qnc-server/common/globalkey"
|
||||
)
|
||||
|
||||
var (
|
||||
agentWithdrawalTaxExemptionFieldNames = builder.RawFieldNames(&AgentWithdrawalTaxExemption{})
|
||||
agentWithdrawalTaxExemptionRows = strings.Join(agentWithdrawalTaxExemptionFieldNames, ",")
|
||||
agentWithdrawalTaxExemptionRowsExpectAutoSet = strings.Join(stringx.Remove(agentWithdrawalTaxExemptionFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
|
||||
agentWithdrawalTaxExemptionRowsWithPlaceHolder = strings.Join(stringx.Remove(agentWithdrawalTaxExemptionFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
||||
|
||||
cacheQncAgentWithdrawalTaxExemptionIdPrefix = "cache:qnc:agentWithdrawalTaxExemption:id:"
|
||||
cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix = "cache:qnc:agentWithdrawalTaxExemption:agentId:yearMonth:"
|
||||
)
|
||||
|
||||
type (
|
||||
agentWithdrawalTaxExemptionModel interface {
|
||||
Insert(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTaxExemption) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id int64) (*AgentWithdrawalTaxExemption, error)
|
||||
FindOneByAgentIdYearMonth(ctx context.Context, agentId int64, yearMonth int64) (*AgentWithdrawalTaxExemption, error)
|
||||
Update(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTaxExemption) (sql.Result, error)
|
||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTaxExemption) 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 *AgentWithdrawalTaxExemption) 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) ([]*AgentWithdrawalTaxExemption, error)
|
||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTaxExemption, error)
|
||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTaxExemption, int64, error)
|
||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWithdrawalTaxExemption, error)
|
||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWithdrawalTaxExemption, error)
|
||||
Delete(ctx context.Context, session sqlx.Session, id int64) error
|
||||
}
|
||||
|
||||
defaultAgentWithdrawalTaxExemptionModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
AgentWithdrawalTaxExemption struct {
|
||||
Id int64 `db:"id"`
|
||||
CreateTime time.Time `db:"create_time"`
|
||||
UpdateTime time.Time `db:"update_time"`
|
||||
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
|
||||
DelState int64 `db:"del_state"`
|
||||
Version int64 `db:"version"` // 版本号
|
||||
YearMonth int64 `db:"year_month"` // 年月标识,格式:202401
|
||||
TotalExemptionAmount float64 `db:"total_exemption_amount"` // 月度免税总额度
|
||||
UsedExemptionAmount float64 `db:"used_exemption_amount"` // 已使用免税额度
|
||||
RemainingExemptionAmount float64 `db:"remaining_exemption_amount"` // 剩余免税额度
|
||||
AgentId int64 `db:"agent_id"` // 关联到代理用户表的id
|
||||
}
|
||||
)
|
||||
|
||||
func newAgentWithdrawalTaxExemptionModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentWithdrawalTaxExemptionModel {
|
||||
return &defaultAgentWithdrawalTaxExemptionModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "`agent_withdrawal_tax_exemption`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) Insert(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTaxExemption) (sql.Result, error) {
|
||||
data.DelState = globalkey.DelStateNo
|
||||
qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey := fmt.Sprintf("%s%v:%v", cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix, data.AgentId, data.YearMonth)
|
||||
qncAgentWithdrawalTaxExemptionIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, 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, agentWithdrawalTaxExemptionRowsExpectAutoSet)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.YearMonth, data.TotalExemptionAmount, data.UsedExemptionAmount, data.RemainingExemptionAmount, data.AgentId)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.YearMonth, data.TotalExemptionAmount, data.UsedExemptionAmount, data.RemainingExemptionAmount, data.AgentId)
|
||||
}, qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey, qncAgentWithdrawalTaxExemptionIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) FindOne(ctx context.Context, id int64) (*AgentWithdrawalTaxExemption, error) {
|
||||
qncAgentWithdrawalTaxExemptionIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, id)
|
||||
var resp AgentWithdrawalTaxExemption
|
||||
err := m.QueryRowCtx(ctx, &resp, qncAgentWithdrawalTaxExemptionIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWithdrawalTaxExemptionRows, 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 *defaultAgentWithdrawalTaxExemptionModel) FindOneByAgentIdYearMonth(ctx context.Context, agentId int64, yearMonth int64) (*AgentWithdrawalTaxExemption, error) {
|
||||
qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey := fmt.Sprintf("%s%v:%v", cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix, agentId, yearMonth)
|
||||
var resp AgentWithdrawalTaxExemption
|
||||
err := m.QueryRowIndexCtx(ctx, &resp, qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where `agent_id` = ? and `year_month` = ? and del_state = ? limit 1", agentWithdrawalTaxExemptionRows, m.table)
|
||||
if err := conn.QueryRowCtx(ctx, &resp, query, agentId, yearMonth, 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 *defaultAgentWithdrawalTaxExemptionModel) Update(ctx context.Context, session sqlx.Session, newData *AgentWithdrawalTaxExemption) (sql.Result, error) {
|
||||
data, err := m.FindOne(ctx, newData.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey := fmt.Sprintf("%s%v:%v", cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix, data.AgentId, data.YearMonth)
|
||||
qncAgentWithdrawalTaxExemptionIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, 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, agentWithdrawalTaxExemptionRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.YearMonth, newData.TotalExemptionAmount, newData.UsedExemptionAmount, newData.RemainingExemptionAmount, newData.AgentId, newData.Id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.YearMonth, newData.TotalExemptionAmount, newData.UsedExemptionAmount, newData.RemainingExemptionAmount, newData.AgentId, newData.Id)
|
||||
}, qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey, qncAgentWithdrawalTaxExemptionIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *AgentWithdrawalTaxExemption) 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
|
||||
}
|
||||
qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey := fmt.Sprintf("%s%v:%v", cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix, data.AgentId, data.YearMonth)
|
||||
qncAgentWithdrawalTaxExemptionIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, 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, agentWithdrawalTaxExemptionRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.YearMonth, newData.TotalExemptionAmount, newData.UsedExemptionAmount, newData.RemainingExemptionAmount, newData.AgentId, newData.Id, oldVersion)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.YearMonth, newData.TotalExemptionAmount, newData.UsedExemptionAmount, newData.RemainingExemptionAmount, newData.AgentId, newData.Id, oldVersion)
|
||||
}, qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey, qncAgentWithdrawalTaxExemptionIdKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateCount, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if updateCount == 0 {
|
||||
return ErrNoRowsUpdate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTaxExemption) 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 "), "AgentWithdrawalTaxExemptionModel delete err : %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) 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 *defaultAgentWithdrawalTaxExemptionModel) 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 *defaultAgentWithdrawalTaxExemptionModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AgentWithdrawalTaxExemption, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxExemptionRows)
|
||||
|
||||
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 []*AgentWithdrawalTaxExemption
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTaxExemption, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxExemptionRows)
|
||||
|
||||
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 []*AgentWithdrawalTaxExemption
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTaxExemption, int64, error) {
|
||||
|
||||
total, err := m.FindCount(ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxExemptionRows)
|
||||
|
||||
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 []*AgentWithdrawalTaxExemption
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, total, nil
|
||||
default:
|
||||
return nil, total, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWithdrawalTaxExemption, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxExemptionRows)
|
||||
|
||||
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 []*AgentWithdrawalTaxExemption
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWithdrawalTaxExemption, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxExemptionRows)
|
||||
|
||||
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 []*AgentWithdrawalTaxExemption
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) 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 *defaultAgentWithdrawalTaxExemptionModel) SelectBuilder() squirrel.SelectBuilder {
|
||||
return squirrel.Select().From(m.table)
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey := fmt.Sprintf("%s%v:%v", cacheQncAgentWithdrawalTaxExemptionAgentIdYearMonthPrefix, data.AgentId, data.YearMonth)
|
||||
qncAgentWithdrawalTaxExemptionIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, 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)
|
||||
}, qncAgentWithdrawalTaxExemptionAgentIdYearMonthKey, qncAgentWithdrawalTaxExemptionIdKey)
|
||||
return err
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxExemptionIdPrefix, primary)
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) 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", agentWithdrawalTaxExemptionRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxExemptionModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
27
app/main/model/agentWithdrawalTaxModel.go
Normal file
27
app/main/model/agentWithdrawalTaxModel.go
Normal 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 _ AgentWithdrawalTaxModel = (*customAgentWithdrawalTaxModel)(nil)
|
||||
|
||||
type (
|
||||
// AgentWithdrawalTaxModel is an interface to be customized, add more methods here,
|
||||
// and implement the added methods in customAgentWithdrawalTaxModel.
|
||||
AgentWithdrawalTaxModel interface {
|
||||
agentWithdrawalTaxModel
|
||||
}
|
||||
|
||||
customAgentWithdrawalTaxModel struct {
|
||||
*defaultAgentWithdrawalTaxModel
|
||||
}
|
||||
)
|
||||
|
||||
// NewAgentWithdrawalTaxModel returns a model for the database table.
|
||||
func NewAgentWithdrawalTaxModel(conn sqlx.SqlConn, c cache.CacheConf) AgentWithdrawalTaxModel {
|
||||
return &customAgentWithdrawalTaxModel{
|
||||
defaultAgentWithdrawalTaxModel: newAgentWithdrawalTaxModel(conn, c),
|
||||
}
|
||||
}
|
||||
418
app/main/model/agentWithdrawalTaxModel_gen.go
Normal file
418
app/main/model/agentWithdrawalTaxModel_gen.go
Normal file
@@ -0,0 +1,418 @@
|
||||
// Code generated by goctl. DO NOT EDIT!
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
"qnc-server/common/globalkey"
|
||||
)
|
||||
|
||||
var (
|
||||
agentWithdrawalTaxFieldNames = builder.RawFieldNames(&AgentWithdrawalTax{})
|
||||
agentWithdrawalTaxRows = strings.Join(agentWithdrawalTaxFieldNames, ",")
|
||||
agentWithdrawalTaxRowsExpectAutoSet = strings.Join(stringx.Remove(agentWithdrawalTaxFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
|
||||
agentWithdrawalTaxRowsWithPlaceHolder = strings.Join(stringx.Remove(agentWithdrawalTaxFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
||||
|
||||
cacheQncAgentWithdrawalTaxIdPrefix = "cache:qnc:agentWithdrawalTax:id:"
|
||||
cacheQncAgentWithdrawalTaxWithdrawalIdPrefix = "cache:qnc:agentWithdrawalTax:withdrawalId:"
|
||||
)
|
||||
|
||||
type (
|
||||
agentWithdrawalTaxModel interface {
|
||||
Insert(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTax) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id int64) (*AgentWithdrawalTax, error)
|
||||
FindOneByWithdrawalId(ctx context.Context, withdrawalId int64) (*AgentWithdrawalTax, error)
|
||||
Update(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTax) (sql.Result, error)
|
||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTax) 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 *AgentWithdrawalTax) 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) ([]*AgentWithdrawalTax, error)
|
||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTax, error)
|
||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTax, int64, error)
|
||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWithdrawalTax, error)
|
||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWithdrawalTax, error)
|
||||
Delete(ctx context.Context, session sqlx.Session, id int64) error
|
||||
}
|
||||
|
||||
defaultAgentWithdrawalTaxModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
AgentWithdrawalTax struct {
|
||||
Id int64 `db:"id"`
|
||||
CreateTime time.Time `db:"create_time"`
|
||||
UpdateTime time.Time `db:"update_time"`
|
||||
DeleteTime sql.NullTime `db:"delete_time"` // 删除时间
|
||||
DelState int64 `db:"del_state"`
|
||||
Version int64 `db:"version"` // 版本号
|
||||
WithdrawalAmount float64 `db:"withdrawal_amount"` // 提现金额
|
||||
ExemptionAmount float64 `db:"exemption_amount"` // 免税金额
|
||||
TaxableAmount float64 `db:"taxable_amount"` // 应税金额
|
||||
TaxRate float64 `db:"tax_rate"` // 税率,如:0.2000表示20%
|
||||
TaxAmount float64 `db:"tax_amount"` // 应缴税费
|
||||
ActualAmount float64 `db:"actual_amount"` // 实际到账金额
|
||||
YearMonth int64 `db:"year_month"` // 所属年月,格式:202401
|
||||
TaxStatus int64 `db:"tax_status"` // 扣税状态:0-待扣税,1-已扣税,2-免税,3-扣税失败
|
||||
TaxTime sql.NullTime `db:"tax_time"` // 扣税时间
|
||||
Remark sql.NullString `db:"remark"` // 备注信息
|
||||
AgentId int64 `db:"agent_id"` // 关联到代理用户表的id
|
||||
WithdrawalId int64 `db:"withdrawal_id"` // 关联提现记录表的id
|
||||
ExemptionRecordId int64 `db:"exemption_record_id"` // 关联到免税额度记录的id
|
||||
}
|
||||
)
|
||||
|
||||
func newAgentWithdrawalTaxModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultAgentWithdrawalTaxModel {
|
||||
return &defaultAgentWithdrawalTaxModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "`agent_withdrawal_tax`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) Insert(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTax) (sql.Result, error) {
|
||||
data.DelState = globalkey.DelStateNo
|
||||
qncAgentWithdrawalTaxIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, data.Id)
|
||||
qncAgentWithdrawalTaxWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxWithdrawalIdPrefix, data.WithdrawalId)
|
||||
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, agentWithdrawalTaxRowsExpectAutoSet)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.WithdrawalAmount, data.ExemptionAmount, data.TaxableAmount, data.TaxRate, data.TaxAmount, data.ActualAmount, data.YearMonth, data.TaxStatus, data.TaxTime, data.Remark, data.AgentId, data.WithdrawalId, data.ExemptionRecordId)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.WithdrawalAmount, data.ExemptionAmount, data.TaxableAmount, data.TaxRate, data.TaxAmount, data.ActualAmount, data.YearMonth, data.TaxStatus, data.TaxTime, data.Remark, data.AgentId, data.WithdrawalId, data.ExemptionRecordId)
|
||||
}, qncAgentWithdrawalTaxIdKey, qncAgentWithdrawalTaxWithdrawalIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) FindOne(ctx context.Context, id int64) (*AgentWithdrawalTax, error) {
|
||||
qncAgentWithdrawalTaxIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, id)
|
||||
var resp AgentWithdrawalTax
|
||||
err := m.QueryRowCtx(ctx, &resp, qncAgentWithdrawalTaxIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", agentWithdrawalTaxRows, 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 *defaultAgentWithdrawalTaxModel) FindOneByWithdrawalId(ctx context.Context, withdrawalId int64) (*AgentWithdrawalTax, error) {
|
||||
qncAgentWithdrawalTaxWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxWithdrawalIdPrefix, withdrawalId)
|
||||
var resp AgentWithdrawalTax
|
||||
err := m.QueryRowIndexCtx(ctx, &resp, qncAgentWithdrawalTaxWithdrawalIdKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where `withdrawal_id` = ? and del_state = ? limit 1", agentWithdrawalTaxRows, m.table)
|
||||
if err := conn.QueryRowCtx(ctx, &resp, query, withdrawalId, 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 *defaultAgentWithdrawalTaxModel) Update(ctx context.Context, session sqlx.Session, newData *AgentWithdrawalTax) (sql.Result, error) {
|
||||
data, err := m.FindOne(ctx, newData.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qncAgentWithdrawalTaxIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, data.Id)
|
||||
qncAgentWithdrawalTaxWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxWithdrawalIdPrefix, data.WithdrawalId)
|
||||
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, agentWithdrawalTaxRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.WithdrawalAmount, newData.ExemptionAmount, newData.TaxableAmount, newData.TaxRate, newData.TaxAmount, newData.ActualAmount, newData.YearMonth, newData.TaxStatus, newData.TaxTime, newData.Remark, newData.AgentId, newData.WithdrawalId, newData.ExemptionRecordId, newData.Id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.WithdrawalAmount, newData.ExemptionAmount, newData.TaxableAmount, newData.TaxRate, newData.TaxAmount, newData.ActualAmount, newData.YearMonth, newData.TaxStatus, newData.TaxTime, newData.Remark, newData.AgentId, newData.WithdrawalId, newData.ExemptionRecordId, newData.Id)
|
||||
}, qncAgentWithdrawalTaxIdKey, qncAgentWithdrawalTaxWithdrawalIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *AgentWithdrawalTax) 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
|
||||
}
|
||||
qncAgentWithdrawalTaxIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, data.Id)
|
||||
qncAgentWithdrawalTaxWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxWithdrawalIdPrefix, data.WithdrawalId)
|
||||
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, agentWithdrawalTaxRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.WithdrawalAmount, newData.ExemptionAmount, newData.TaxableAmount, newData.TaxRate, newData.TaxAmount, newData.ActualAmount, newData.YearMonth, newData.TaxStatus, newData.TaxTime, newData.Remark, newData.AgentId, newData.WithdrawalId, newData.ExemptionRecordId, newData.Id, oldVersion)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.WithdrawalAmount, newData.ExemptionAmount, newData.TaxableAmount, newData.TaxRate, newData.TaxAmount, newData.ActualAmount, newData.YearMonth, newData.TaxStatus, newData.TaxTime, newData.Remark, newData.AgentId, newData.WithdrawalId, newData.ExemptionRecordId, newData.Id, oldVersion)
|
||||
}, qncAgentWithdrawalTaxIdKey, qncAgentWithdrawalTaxWithdrawalIdKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateCount, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if updateCount == 0 {
|
||||
return ErrNoRowsUpdate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *AgentWithdrawalTax) 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 "), "AgentWithdrawalTaxModel delete err : %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) 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 *defaultAgentWithdrawalTaxModel) 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 *defaultAgentWithdrawalTaxModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*AgentWithdrawalTax, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxRows)
|
||||
|
||||
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 []*AgentWithdrawalTax
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTax, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxRows)
|
||||
|
||||
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 []*AgentWithdrawalTax
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*AgentWithdrawalTax, int64, error) {
|
||||
|
||||
total, err := m.FindCount(ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxRows)
|
||||
|
||||
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 []*AgentWithdrawalTax
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, total, nil
|
||||
default:
|
||||
return nil, total, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*AgentWithdrawalTax, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxRows)
|
||||
|
||||
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 []*AgentWithdrawalTax
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*AgentWithdrawalTax, error) {
|
||||
|
||||
builder = builder.Columns(agentWithdrawalTaxRows)
|
||||
|
||||
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 []*AgentWithdrawalTax
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) 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 *defaultAgentWithdrawalTaxModel) SelectBuilder() squirrel.SelectBuilder {
|
||||
return squirrel.Select().From(m.table)
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qncAgentWithdrawalTaxIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, id)
|
||||
qncAgentWithdrawalTaxWithdrawalIdKey := fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxWithdrawalIdPrefix, data.WithdrawalId)
|
||||
_, 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)
|
||||
}, qncAgentWithdrawalTaxIdKey, qncAgentWithdrawalTaxWithdrawalIdKey)
|
||||
return err
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheQncAgentWithdrawalTaxIdPrefix, primary)
|
||||
}
|
||||
func (m *defaultAgentWithdrawalTaxModel) 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", agentWithdrawalTaxRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
||||
}
|
||||
|
||||
func (m *defaultAgentWithdrawalTaxModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
27
app/main/model/userTempModel.go
Normal file
27
app/main/model/userTempModel.go
Normal 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 _ UserTempModel = (*customUserTempModel)(nil)
|
||||
|
||||
type (
|
||||
// UserTempModel is an interface to be customized, add more methods here,
|
||||
// and implement the added methods in customUserTempModel.
|
||||
UserTempModel interface {
|
||||
userTempModel
|
||||
}
|
||||
|
||||
customUserTempModel struct {
|
||||
*defaultUserTempModel
|
||||
}
|
||||
)
|
||||
|
||||
// NewUserTempModel returns a model for the database table.
|
||||
func NewUserTempModel(conn sqlx.SqlConn, c cache.CacheConf) UserTempModel {
|
||||
return &customUserTempModel{
|
||||
defaultUserTempModel: newUserTempModel(conn, c),
|
||||
}
|
||||
}
|
||||
407
app/main/model/userTempModel_gen.go
Normal file
407
app/main/model/userTempModel_gen.go
Normal file
@@ -0,0 +1,407 @@
|
||||
// Code generated by goctl. DO NOT EDIT!
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
"qnc-server/common/globalkey"
|
||||
)
|
||||
|
||||
var (
|
||||
userTempFieldNames = builder.RawFieldNames(&UserTemp{})
|
||||
userTempRows = strings.Join(userTempFieldNames, ",")
|
||||
userTempRowsExpectAutoSet = strings.Join(stringx.Remove(userTempFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
|
||||
userTempRowsWithPlaceHolder = strings.Join(stringx.Remove(userTempFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
||||
|
||||
cacheQncUserTempIdPrefix = "cache:qnc:userTemp:id:"
|
||||
cacheQncUserTempAuthTypeAuthKeyPrefix = "cache:qnc:userTemp:authType:authKey:"
|
||||
)
|
||||
|
||||
type (
|
||||
userTempModel interface {
|
||||
Insert(ctx context.Context, session sqlx.Session, data *UserTemp) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id int64) (*UserTemp, error)
|
||||
FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserTemp, error)
|
||||
Update(ctx context.Context, session sqlx.Session, data *UserTemp) (sql.Result, error)
|
||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *UserTemp) 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 *UserTemp) 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) ([]*UserTemp, error)
|
||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserTemp, error)
|
||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserTemp, int64, error)
|
||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserTemp, error)
|
||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserTemp, error)
|
||||
Delete(ctx context.Context, session sqlx.Session, id int64) error
|
||||
}
|
||||
|
||||
defaultUserTempModel struct {
|
||||
sqlc.CachedConn
|
||||
table string
|
||||
}
|
||||
|
||||
UserTemp struct {
|
||||
Id int64 `db:"id"`
|
||||
AuthKey string `db:"auth_key"` // 平台唯一id
|
||||
AuthType string `db:"auth_type"` // 平台类型
|
||||
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"` // 版本号
|
||||
}
|
||||
)
|
||||
|
||||
func newUserTempModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserTempModel {
|
||||
return &defaultUserTempModel{
|
||||
CachedConn: sqlc.NewConn(conn, c),
|
||||
table: "`user_temp`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) Insert(ctx context.Context, session sqlx.Session, data *UserTemp) (sql.Result, error) {
|
||||
data.DelState = globalkey.DelStateNo
|
||||
qncUserTempAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheQncUserTempAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
||||
qncUserTempIdKey := fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, 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, userTempRowsExpectAutoSet)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, data.AuthKey, data.AuthType, data.DeleteTime, data.DelState, data.Version)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, data.AuthKey, data.AuthType, data.DeleteTime, data.DelState, data.Version)
|
||||
}, qncUserTempAuthTypeAuthKeyKey, qncUserTempIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) FindOne(ctx context.Context, id int64) (*UserTemp, error) {
|
||||
qncUserTempIdKey := fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, id)
|
||||
var resp UserTemp
|
||||
err := m.QueryRowCtx(ctx, &resp, qncUserTempIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userTempRows, 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 *defaultUserTempModel) FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserTemp, error) {
|
||||
qncUserTempAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheQncUserTempAuthTypeAuthKeyPrefix, authType, authKey)
|
||||
var resp UserTemp
|
||||
err := m.QueryRowIndexCtx(ctx, &resp, qncUserTempAuthTypeAuthKeyKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where `auth_type` = ? and `auth_key` = ? and del_state = ? limit 1", userTempRows, m.table)
|
||||
if err := conn.QueryRowCtx(ctx, &resp, query, authType, authKey, 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 *defaultUserTempModel) Update(ctx context.Context, session sqlx.Session, newData *UserTemp) (sql.Result, error) {
|
||||
data, err := m.FindOne(ctx, newData.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qncUserTempAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheQncUserTempAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
||||
qncUserTempIdKey := fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, 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, userTempRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.AuthKey, newData.AuthType, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.AuthKey, newData.AuthType, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||
}, qncUserTempAuthTypeAuthKeyKey, qncUserTempIdKey)
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *UserTemp) 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
|
||||
}
|
||||
qncUserTempAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheQncUserTempAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
||||
qncUserTempIdKey := fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, 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, userTempRowsWithPlaceHolder)
|
||||
if session != nil {
|
||||
return session.ExecCtx(ctx, query, newData.AuthKey, newData.AuthType, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
}
|
||||
return conn.ExecCtx(ctx, query, newData.AuthKey, newData.AuthType, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||
}, qncUserTempAuthTypeAuthKeyKey, qncUserTempIdKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateCount, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if updateCount == 0 {
|
||||
return ErrNoRowsUpdate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *UserTemp) 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 "), "UserTempModel delete err : %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) 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 *defaultUserTempModel) 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 *defaultUserTempModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*UserTemp, error) {
|
||||
|
||||
builder = builder.Columns(userTempRows)
|
||||
|
||||
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 []*UserTemp
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserTemp, error) {
|
||||
|
||||
builder = builder.Columns(userTempRows)
|
||||
|
||||
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 []*UserTemp
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserTemp, int64, error) {
|
||||
|
||||
total, err := m.FindCount(ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
builder = builder.Columns(userTempRows)
|
||||
|
||||
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 []*UserTemp
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, total, nil
|
||||
default:
|
||||
return nil, total, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserTemp, error) {
|
||||
|
||||
builder = builder.Columns(userTempRows)
|
||||
|
||||
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 []*UserTemp
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserTemp, error) {
|
||||
|
||||
builder = builder.Columns(userTempRows)
|
||||
|
||||
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 []*UserTemp
|
||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
||||
switch err {
|
||||
case nil:
|
||||
return resp, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) 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 *defaultUserTempModel) SelectBuilder() squirrel.SelectBuilder {
|
||||
return squirrel.Select().From(m.table)
|
||||
}
|
||||
func (m *defaultUserTempModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qncUserTempAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheQncUserTempAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
||||
qncUserTempIdKey := fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, 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)
|
||||
}, qncUserTempAuthTypeAuthKeyKey, qncUserTempIdKey)
|
||||
return err
|
||||
}
|
||||
func (m *defaultUserTempModel) formatPrimary(primary interface{}) string {
|
||||
return fmt.Sprintf("%s%v", cacheQncUserTempIdPrefix, primary)
|
||||
}
|
||||
func (m *defaultUserTempModel) 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", userTempRows, m.table)
|
||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
||||
}
|
||||
|
||||
func (m *defaultUserTempModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
@@ -9,18 +9,24 @@ import (
|
||||
var ErrNotFound = sqlx.ErrNotFound
|
||||
var ErrNoRowsUpdate = errors.New("update db no rows change")
|
||||
|
||||
var UserAuthTypeAppMobile string = "app_mobile" //平台内部
|
||||
var UserAuthTypeAppWechat string = "app_wechat" //微信小程序
|
||||
var UserAuthTypeH5Mobile string = "h5_mobile"
|
||||
var UserAuthTypeWxMini string = "wx_mini"
|
||||
var UserAuthTypeWxh5 string = "wx_h5"
|
||||
var UserAuthTypeAgentDirect string = "agent_direct"
|
||||
var UserAuthTypeAgentPromote string = "agent_promote"
|
||||
// 平台
|
||||
var PlatformWxMini string = "wxmini"
|
||||
var PlatformWxH5 string = "wxh5"
|
||||
var PlatformApp string = "app"
|
||||
var PlatformH5 string = "h5"
|
||||
var PlatformAdmin string = "admin"
|
||||
|
||||
// 用户授权类型
|
||||
var UserAuthTypeMobile string = "mobile"
|
||||
var UserAuthTypeWxMiniOpenID string = "wxmini_openid"
|
||||
var UserAuthTypeWxh5OpenID string = "wxh5_openid"
|
||||
var UserAuthTypeUUID string = "uuid"
|
||||
|
||||
// 代理扣除类型
|
||||
var AgentDeductionTypeCost string = "cost"
|
||||
var AgentDeductionTypePricing string = "pricing"
|
||||
|
||||
// 代理奖励类型
|
||||
var AgentRewardsTypeDescendantPromotion string = "descendant_promotion"
|
||||
var AgentRewardsTypeDescendantUpgradeVip string = "descendant_upgrade_vip"
|
||||
var AgentRewardsTypeDescendantUpgradeSvip string = "descendant_upgrade_svip"
|
||||
@@ -28,23 +34,30 @@ var AgentRewardsTypeDescendantStayActive string = "descendant_stay_active"
|
||||
var AgentRewardsTypeDescendantNewActive string = "descendant_new_active"
|
||||
var AgentRewardsTypeDescendantWithdraw string = "descendant_withdraw"
|
||||
|
||||
// 代理等级名称
|
||||
var AgentLeveNameNormal string = "normal"
|
||||
var AgentLeveNameVIP string = "VIP"
|
||||
var AgentLeveNameSVIP string = "SVIP"
|
||||
|
||||
// 订单状态
|
||||
const (
|
||||
OrderStatusPending = "pending"
|
||||
OrderStatusPaid = "paid"
|
||||
OrderStatusFailed = "failed"
|
||||
OrderStatusRefunded = "refunded"
|
||||
OrderStatusClosed = "closed"
|
||||
OrderStatusPending = "pending"
|
||||
OrderStatusPaid = "paid"
|
||||
OrderStatusFailed = "failed"
|
||||
OrderStatusRefunded = "refunded"
|
||||
OrderStatusRefunding = "refunding"
|
||||
OrderStatusClosed = "closed"
|
||||
)
|
||||
|
||||
// 订单退款状态
|
||||
const (
|
||||
OrderRefundStatusPending = "pending"
|
||||
OrderRefundStatusSuccess = "success"
|
||||
OrderRefundStatusFailed = "failed"
|
||||
OrderRefundStatusClosed = "closed"
|
||||
)
|
||||
|
||||
// 查询状态
|
||||
const (
|
||||
QueryStatePending = "pending"
|
||||
QueryStateFailed = "failed"
|
||||
@@ -53,10 +66,13 @@ const (
|
||||
QueryStateCleaned = "cleaned"
|
||||
)
|
||||
|
||||
// 授权类型
|
||||
const (
|
||||
AuthorizationGrantTypeFace = "face"
|
||||
AuthorizationGrantTypeSms = "sms"
|
||||
)
|
||||
|
||||
// 授权状态
|
||||
const (
|
||||
AuthorizationStatusPending = "pending"
|
||||
AuthorizationStatusSuccess = "success"
|
||||
@@ -66,14 +82,35 @@ const (
|
||||
AuthorizationStatusRejected = "rejected"
|
||||
)
|
||||
|
||||
// 授权人脸状态
|
||||
const (
|
||||
AuthorizationFaceStatusPending = "pending"
|
||||
AuthorizationFaceStatusSuccess = "success"
|
||||
AuthorizationFaceStatusFailed = "failed"
|
||||
)
|
||||
|
||||
// 代理实名状态
|
||||
const (
|
||||
AgentRealNameStatusPending = "pending"
|
||||
AgentRealNameStatusApproved = "approved"
|
||||
AgentRealNameStatusRejected = "rejected"
|
||||
)
|
||||
|
||||
// 用户身份类型
|
||||
const (
|
||||
UserTypeTemp = 0 // 临时用户
|
||||
UserTypeNormal = 1 // 正式用户
|
||||
UserTypeAdmin = 2 // 管理员
|
||||
)
|
||||
|
||||
// 代理状态
|
||||
const (
|
||||
AgentStatusNo = 0 // 非代理
|
||||
AgentStatusYes = 1 // 是代理
|
||||
)
|
||||
const (
|
||||
TaxStatusPending = 0 // 待扣税
|
||||
TaxStatusSuccess = 1 // 已扣税
|
||||
TaxStatusExempt = 2 // 免税
|
||||
TaxStatusFailed = 3 // 扣税失败
|
||||
)
|
||||
|
||||
@@ -5,14 +5,16 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"qnc-server/app/main/model"
|
||||
jwtx "qnc-server/common/jwt"
|
||||
)
|
||||
|
||||
const CtxKeyJwtUserId = "userId"
|
||||
|
||||
// 定义错误类型
|
||||
var (
|
||||
ErrNoUserIdInCtx = errors.New("上下文中没有用户ID") // 未登录
|
||||
ErrInvalidUserId = errors.New("用户ID格式无效") // 数据异常
|
||||
ErrNoInCtx = errors.New("上下文中没有相关数据")
|
||||
ErrInvalidUserId = errors.New("用户ID格式无效") // 数据异常
|
||||
)
|
||||
|
||||
// GetUidFromCtx 从 context 中获取用户 ID
|
||||
@@ -20,7 +22,11 @@ func GetUidFromCtx(ctx context.Context) (int64, error) {
|
||||
// 尝试从上下文中获取 jwtUserId
|
||||
value := ctx.Value(CtxKeyJwtUserId)
|
||||
if value == nil {
|
||||
return 0, ErrNoUserIdInCtx
|
||||
claims, err := GetClaimsFromCtx(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return claims.UserId, nil
|
||||
}
|
||||
|
||||
// 根据值的类型进行不同处理
|
||||
@@ -47,12 +53,52 @@ func GetUidFromCtx(ctx context.Context) (int64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func GetClaimsFromCtx(ctx context.Context) (*jwtx.JwtClaims, error) {
|
||||
value := ctx.Value(jwtx.ExtraKey)
|
||||
if value == nil {
|
||||
return nil, ErrNoInCtx
|
||||
}
|
||||
|
||||
// 首先尝试直接断言为 *jwtx.JwtClaims
|
||||
if claims, ok := value.(*jwtx.JwtClaims); ok {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// 如果直接断言失败,尝试从 map[string]interface{} 中解析
|
||||
if claimsMap, ok := value.(map[string]interface{}); ok {
|
||||
return jwtx.MapToJwtClaims(claimsMap)
|
||||
}
|
||||
|
||||
return nil, ErrNoInCtx
|
||||
}
|
||||
|
||||
// IsNoUserIdError 判断是否是未登录错误
|
||||
func IsNoUserIdError(err error) bool {
|
||||
return errors.Is(err, ErrNoUserIdInCtx)
|
||||
return errors.Is(err, ErrNoInCtx)
|
||||
}
|
||||
|
||||
// IsInvalidUserIdError 判断是否是用户ID格式错误
|
||||
func IsInvalidUserIdError(err error) bool {
|
||||
return errors.Is(err, ErrInvalidUserId)
|
||||
}
|
||||
|
||||
// GetPlatformFromCtx 从 context 中获取平台
|
||||
func GetPlatformFromCtx(ctx context.Context) (string, error) {
|
||||
platform, platformOk := ctx.Value("platform").(string)
|
||||
if !platformOk {
|
||||
return "", fmt.Errorf("平台不存在: %s", platform)
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case model.PlatformWxMini:
|
||||
return model.PlatformWxMini, nil
|
||||
case model.PlatformWxH5:
|
||||
return model.PlatformWxH5, nil
|
||||
case model.PlatformApp:
|
||||
return model.PlatformApp, nil
|
||||
case model.PlatformH5:
|
||||
return model.PlatformH5, nil
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的支付平台: %s", platform)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,94 @@
|
||||
package jwtx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
// Token 生成逻辑的函数,接收 userId、过期时间和密钥,返回生成的 token
|
||||
func GenerateJwtToken(userId int64, secret string, expireTime int64) (string, error) {
|
||||
// 获取当前时间戳
|
||||
now := time.Now().Unix()
|
||||
// 定义 JWT Claims
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + expireTime, // token 过期时间
|
||||
"iat": now, // 签发时间
|
||||
"userId": userId, // 用户ID
|
||||
const ExtraKey = "extra"
|
||||
|
||||
type JwtClaims struct {
|
||||
UserId int64 `json:"userId"`
|
||||
AgentId int64 `json:"agentId"`
|
||||
Platform string `json:"platform"`
|
||||
// 用户身份类型:0-临时用户,1-正式用户
|
||||
UserType int64 `json:"userType"`
|
||||
// 是否代理:0-否,1-是
|
||||
IsAgent int64 `json:"isAgent"`
|
||||
}
|
||||
|
||||
// MapToJwtClaims 将 map[string]interface{} 转换为 JwtClaims 结构体
|
||||
func MapToJwtClaims(claimsMap map[string]interface{}) (*JwtClaims, error) {
|
||||
// 使用JSON序列化/反序列化的方式自动转换
|
||||
jsonData, err := json.Marshal(claimsMap)
|
||||
if err != nil {
|
||||
return nil, errors.New("序列化claims失败")
|
||||
}
|
||||
|
||||
// 创建新的 JWT token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
var claims JwtClaims
|
||||
if err := json.Unmarshal(jsonData, &claims); err != nil {
|
||||
return nil, errors.New("反序列化claims失败")
|
||||
}
|
||||
|
||||
// 使用密钥对 token 签名
|
||||
signedToken, err := token.SignedString([]byte(secret))
|
||||
return &claims, nil
|
||||
}
|
||||
|
||||
// GenerateJwtToken 生成JWT token
|
||||
func GenerateJwtToken(claims JwtClaims, secret string, expire int64) (string, error) {
|
||||
now := time.Now().Unix()
|
||||
|
||||
// 将 claims 结构体转换为 map[string]interface{}
|
||||
claimsBytes, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return signedToken, nil
|
||||
var claimsMap map[string]interface{}
|
||||
if err := json.Unmarshal(claimsBytes, &claimsMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
jwtClaims := jwt.MapClaims{
|
||||
"exp": now + expire,
|
||||
"iat": now,
|
||||
"userId": claims.UserId,
|
||||
ExtraKey: claimsMap,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaims)
|
||||
return token.SignedString([]byte(secret))
|
||||
}
|
||||
func ParseJwtToken(tokenStr string, secret string) (int64, error) {
|
||||
|
||||
func ParseJwtToken(tokenStr string, secret string) (*JwtClaims, error) {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
return 0, errors.New("invalid JWT")
|
||||
return nil, errors.New("invalid JWT")
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
return 0, errors.New("invalid JWT claims")
|
||||
return nil, errors.New("invalid JWT claims")
|
||||
}
|
||||
|
||||
// 从 claims 中提取 userId
|
||||
userIdRaw, ok := claims["userId"]
|
||||
if !ok {
|
||||
return 0, errors.New("userId not found in JWT")
|
||||
extraInfo, exists := claims[ExtraKey]
|
||||
if !exists {
|
||||
return nil, errors.New("extra not found in JWT")
|
||||
}
|
||||
|
||||
// 处理不同类型的 userId,确保它被转换为 int64
|
||||
switch userId := userIdRaw.(type) {
|
||||
case float64:
|
||||
return int64(userId), nil
|
||||
case int64:
|
||||
return userId, nil
|
||||
case string:
|
||||
// 如果 userId 是字符串,可以尝试将其转换为 int64
|
||||
parsedId, err := strconv.ParseInt(userId, 10, 64)
|
||||
if err != nil {
|
||||
return 0, errors.New("invalid userId in JWT")
|
||||
}
|
||||
return parsedId, nil
|
||||
default:
|
||||
return 0, errors.New("unsupported userId type in JWT")
|
||||
// 尝试直接断言为 JwtClaims 结构体
|
||||
if jwtClaims, ok := extraInfo.(JwtClaims); ok {
|
||||
return &jwtClaims, nil
|
||||
}
|
||||
|
||||
// 尝试从 map[string]interface{} 中解析
|
||||
if claimsMap, ok := extraInfo.(map[string]interface{}); ok {
|
||||
return MapToJwtClaims(claimsMap)
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported extra type in JWT")
|
||||
}
|
||||
|
||||
@@ -2,109 +2,295 @@ package jwtx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"qnc-server/app/main/model"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateAndParseJwtToken(t *testing.T) {
|
||||
// 测试参数
|
||||
userId := int64(39)
|
||||
func TestParseJwtToken(t *testing.T) {
|
||||
secret := "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA"
|
||||
expireTime := int64(2592000) // 1小时过期
|
||||
|
||||
// 生成token
|
||||
token, err := GenerateJwtToken(userId, secret, expireTime)
|
||||
if err != nil {
|
||||
t.Fatalf("生成JWT令牌失败: %v", err)
|
||||
}
|
||||
if token == "" {
|
||||
t.Fatal("生成的JWT令牌为空")
|
||||
}
|
||||
fmt.Println(token)
|
||||
// 解析token
|
||||
parsedUserId, err := ParseJwtToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTA5MjYyNTgsImlhdCI6MTc0ODMzNDI1OCwidXNlcklkIjoxNDN9.V4qV3dAjE6G-xm0KmB6QHCVhy2SmRDGAWcvl32hLNmI", secret)
|
||||
if err != nil {
|
||||
t.Fatalf("解析JWT令牌失败: %v", err)
|
||||
}
|
||||
fmt.Printf("解析出的userId: %d\n", parsedUserId)
|
||||
// 验证解析出的userId是否正确
|
||||
// if parsedUserId != userId {
|
||||
// t.Errorf("解析出的userId不匹配: 期望 %d, 实际 %d", userId, parsedUserId)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestTokenExpiration(t *testing.T) {
|
||||
// 测试参数
|
||||
userId := int64(10086)
|
||||
secret := "test_secret_key"
|
||||
expireTime := int64(1) // 1秒过期
|
||||
|
||||
// 生成token
|
||||
token, err := GenerateJwtToken(userId, secret, expireTime)
|
||||
if err != nil {
|
||||
t.Fatalf("生成JWT令牌失败: %v", err)
|
||||
}
|
||||
|
||||
// 等待令牌过期
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// 解析已过期token
|
||||
_, err = ParseJwtToken(token, secret)
|
||||
if err == nil {
|
||||
t.Error("期望令牌过期错误,但没有发生错误")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidToken(t *testing.T) {
|
||||
secret := "test_secret_key"
|
||||
|
||||
// 测试无效token
|
||||
invalidToken := "invalid.token.string"
|
||||
_, err := ParseJwtToken(invalidToken, secret)
|
||||
if err == nil {
|
||||
t.Error("期望无效令牌错误,但没有发生错误")
|
||||
}
|
||||
|
||||
// 测试密钥不匹配
|
||||
userId := int64(10086)
|
||||
expireTime := int64(3600)
|
||||
token, _ := GenerateJwtToken(userId, "original_secret", expireTime)
|
||||
_, err = ParseJwtToken(token, "wrong_secret")
|
||||
if err == nil {
|
||||
t.Error("期望密钥不匹配错误,但没有发生错误")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserIdTypes(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
setupFn func() (string, string, int64)
|
||||
expected int64
|
||||
tokenStr, err := GenerateJwtToken(JwtClaims{
|
||||
UserId: 123,
|
||||
AgentId: 0,
|
||||
Platform: "wxh5",
|
||||
UserType: 0,
|
||||
IsAgent: 0,
|
||||
}, secret, 3600)
|
||||
assert.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
tokenStr string
|
||||
secret string
|
||||
expectError bool
|
||||
expectClaims *JwtClaims
|
||||
}{
|
||||
{
|
||||
name: "正常int64类型",
|
||||
setupFn: func() (string, string, int64) {
|
||||
userId := int64(10086)
|
||||
secret := "test_secret"
|
||||
expireTime := int64(3600)
|
||||
token, _ := GenerateJwtToken(userId, secret, expireTime)
|
||||
return token, secret, userId
|
||||
},
|
||||
expected: 10086,
|
||||
name: "无效的token字符串",
|
||||
tokenStr: "invalid-token",
|
||||
secret: secret,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "空token字符串",
|
||||
tokenStr: "",
|
||||
secret: secret,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "错误的密钥",
|
||||
tokenStr: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHRyYSI6eyJ1c2VySWQiOjEyMywiYWdlbnRJZCI6NDU2LCJwbGF0Zm9ybSI6InRlc3QiLCJ1c2VyVHlwZSI6MSwiaXNBZ2VudCI6MH0sImV4cCI6MTczNTY4MDAwMCwiaWF0IjoxNzM1Njc5OTAwfQ.invalid-signature",
|
||||
secret: "wrong-secret",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "缺少extra字段",
|
||||
tokenStr: createTokenWithoutExtra(secret),
|
||||
secret: secret,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "正常解析token",
|
||||
tokenStr: tokenStr,
|
||||
secret: secret,
|
||||
expectError: false,
|
||||
expectClaims: &JwtClaims{
|
||||
UserId: 123,
|
||||
AgentId: 456,
|
||||
Platform: "test",
|
||||
UserType: 1,
|
||||
IsAgent: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "解析临时用户token",
|
||||
tokenStr: createTempUserToken(secret),
|
||||
secret: secret,
|
||||
expectError: false,
|
||||
expectClaims: &JwtClaims{
|
||||
UserId: 789,
|
||||
AgentId: 0,
|
||||
Platform: "mobile",
|
||||
UserType: 0,
|
||||
IsAgent: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "解析代理用户token",
|
||||
tokenStr: createAgentUserToken(secret),
|
||||
secret: secret,
|
||||
expectError: false,
|
||||
expectClaims: &JwtClaims{
|
||||
UserId: 999,
|
||||
AgentId: 888,
|
||||
Platform: "web",
|
||||
UserType: 1,
|
||||
IsAgent: 1,
|
||||
},
|
||||
},
|
||||
// 其他类型在实际场景中通过手动修改token内容测试,这里省略
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
token, secret, expected := tc.setupFn()
|
||||
userId, err := ParseJwtToken(token, secret)
|
||||
if err != nil {
|
||||
t.Fatalf("解析失败: %v", err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
claims, err := ParseJwtToken(tt.tokenStr, tt.secret)
|
||||
fmt.Printf("name: %s\n", tt.name)
|
||||
fmt.Printf("claims: %+v\n", claims)
|
||||
if tt.name == "正常解析token" {
|
||||
fmt.Printf("claims.UserType bool: %v\n", claims.UserType == model.UserTypeTemp)
|
||||
}
|
||||
if userId != expected {
|
||||
t.Errorf("用户ID不匹配: 期望 %d, 实际 %d", expected, userId)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, claims)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, claims)
|
||||
assert.Equal(t, tt.expectClaims.UserId, claims.UserId)
|
||||
assert.Equal(t, tt.expectClaims.AgentId, claims.AgentId)
|
||||
assert.Equal(t, tt.expectClaims.Platform, claims.Platform)
|
||||
assert.Equal(t, tt.expectClaims.UserType, claims.UserType)
|
||||
assert.Equal(t, tt.expectClaims.IsAgent, claims.IsAgent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJwtToken_Integration(t *testing.T) {
|
||||
secret := "integration-test-secret"
|
||||
|
||||
// 测试生成和解析的集成
|
||||
originalClaims := JwtClaims{
|
||||
UserId: 12345,
|
||||
AgentId: 67890,
|
||||
Platform: "integration-test",
|
||||
UserType: 1,
|
||||
IsAgent: 1,
|
||||
}
|
||||
|
||||
// 生成token
|
||||
tokenStr, err := GenerateJwtToken(originalClaims, secret, 3600)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, tokenStr)
|
||||
|
||||
// 解析token
|
||||
parsedClaims, err := ParseJwtToken(tokenStr, secret)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, parsedClaims)
|
||||
|
||||
// 验证解析结果
|
||||
assert.Equal(t, originalClaims.UserId, parsedClaims.UserId)
|
||||
assert.Equal(t, originalClaims.AgentId, parsedClaims.AgentId)
|
||||
assert.Equal(t, originalClaims.Platform, parsedClaims.Platform)
|
||||
assert.Equal(t, originalClaims.UserType, parsedClaims.UserType)
|
||||
assert.Equal(t, originalClaims.IsAgent, parsedClaims.IsAgent)
|
||||
}
|
||||
|
||||
func TestParseJwtToken_EdgeCases(t *testing.T) {
|
||||
secret := "edge-case-secret"
|
||||
|
||||
t.Run("过期的token", func(t *testing.T) {
|
||||
// 创建一个已过期的token
|
||||
expiredToken := createExpiredToken(secret)
|
||||
claims, err := ParseJwtToken(expiredToken, secret)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, claims)
|
||||
})
|
||||
|
||||
t.Run("extra字段为nil", func(t *testing.T) {
|
||||
// 创建一个extra字段为nil的token
|
||||
nilExtraToken := createTokenWithNilExtra(secret)
|
||||
claims, err := ParseJwtToken(nilExtraToken, secret)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, claims)
|
||||
})
|
||||
|
||||
t.Run("extra字段类型错误", func(t *testing.T) {
|
||||
// 创建一个extra字段类型错误的token
|
||||
wrongTypeToken := createTokenWithWrongExtraType(secret)
|
||||
claims, err := ParseJwtToken(wrongTypeToken, secret)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, claims)
|
||||
})
|
||||
}
|
||||
|
||||
// 辅助函数:创建有效的token
|
||||
func createValidToken(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 123,
|
||||
ExtraKey: map[string]interface{}{
|
||||
"userId": 123,
|
||||
"agentId": 456,
|
||||
"platform": "test",
|
||||
"userType": 1,
|
||||
"isAgent": 0,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建临时用户token
|
||||
func createTempUserToken(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 789,
|
||||
ExtraKey: map[string]interface{}{
|
||||
"userId": 789,
|
||||
"agentId": 0,
|
||||
"platform": "mobile",
|
||||
"userType": 0,
|
||||
"isAgent": 0,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建代理用户token
|
||||
func createAgentUserToken(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 999,
|
||||
ExtraKey: map[string]interface{}{
|
||||
"userId": 999,
|
||||
"agentId": 888,
|
||||
"platform": "web",
|
||||
"userType": 1,
|
||||
"isAgent": 1,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建缺少extra字段的token
|
||||
func createTokenWithoutExtra(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 123,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建已过期的token
|
||||
func createExpiredToken(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now - 3600, // 已过期
|
||||
"iat": now - 7200,
|
||||
"userId": 123,
|
||||
ExtraKey: map[string]interface{}{
|
||||
"userId": 123,
|
||||
"agentId": 456,
|
||||
"platform": "test",
|
||||
"userType": 1,
|
||||
"isAgent": 0,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建extra字段为nil的token
|
||||
func createTokenWithNilExtra(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 123,
|
||||
ExtraKey: nil,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
// 辅助函数:创建extra字段类型错误的token
|
||||
func createTokenWithWrongExtraType(secret string) string {
|
||||
now := time.Now().Unix()
|
||||
claims := jwt.MapClaims{
|
||||
"exp": now + 3600,
|
||||
"iat": now,
|
||||
"userId": 123,
|
||||
ExtraKey: "wrong-type", // 应该是map[string]interface{}
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenStr, _ := token.SignedString([]byte(secret))
|
||||
return tokenStr
|
||||
}
|
||||
|
||||
@@ -6,17 +6,44 @@ const OK uint32 = 200
|
||||
/**(前3位代表业务,后三位代表具体功能)**/
|
||||
|
||||
// 全局错误码
|
||||
// 服务器通用错误
|
||||
const SERVER_COMMON_ERROR uint32 = 100001
|
||||
|
||||
// 请求参数错误
|
||||
const REUQEST_PARAM_ERROR uint32 = 100002
|
||||
|
||||
// token过期错误
|
||||
const TOKEN_EXPIRE_ERROR uint32 = 100003
|
||||
|
||||
// token生成错误
|
||||
const TOKEN_GENERATE_ERROR uint32 = 100004
|
||||
|
||||
// 数据库错误
|
||||
const DB_ERROR uint32 = 100005
|
||||
|
||||
// 数据库更新影响行数为0错误
|
||||
const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
|
||||
|
||||
// 参数验证错误
|
||||
const PARAM_VERIFICATION_ERROR uint32 = 100007
|
||||
|
||||
// 自定义错误
|
||||
const CUSTOM_ERROR uint32 = 100008
|
||||
|
||||
// 用户不存在错误
|
||||
const USER_NOT_FOUND uint32 = 100009
|
||||
|
||||
// 用户需要绑定手机号
|
||||
const USER_NEED_BIND_MOBILE uint32 = 100010
|
||||
|
||||
// 登录失败错误
|
||||
const LOGIN_FAILED uint32 = 200001
|
||||
|
||||
// 查询等待中
|
||||
const LOGIC_QUERY_WAIT uint32 = 200002
|
||||
|
||||
// 查询错误
|
||||
const LOGIC_QUERY_ERROR uint32 = 200003
|
||||
|
||||
// 查询结果不存在
|
||||
const LOGIC_QUERY_NOT_FOUND uint32 = 200004
|
||||
|
||||
@@ -5,11 +5,11 @@ var message map[uint32]string
|
||||
func init() {
|
||||
message = make(map[uint32]string)
|
||||
message[OK] = "SUCCESS"
|
||||
message[SERVER_COMMON_ERROR] = "服务器开小差啦,稍后再来试一试"
|
||||
message[SERVER_COMMON_ERROR] = "系统正在升级,请稍后再试"
|
||||
message[REUQEST_PARAM_ERROR] = "参数错误"
|
||||
message[TOKEN_EXPIRE_ERROR] = "token失效,请重新登陆"
|
||||
message[TOKEN_GENERATE_ERROR] = "生成token失败"
|
||||
message[DB_ERROR] = "数据库繁忙,请稍后再试"
|
||||
message[DB_ERROR] = "系统维护升级中,请稍后再试"
|
||||
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func MapErrMsg(errcode uint32) string {
|
||||
if msg, ok := message[errcode]; ok {
|
||||
return msg
|
||||
} else {
|
||||
return "服务器开小差啦,稍后再来试一试"
|
||||
return "系统正在升级,请稍后再试"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ $tables = @(
|
||||
# "agent_product_config",
|
||||
# "agent_rewards",
|
||||
# "agent_wallet",
|
||||
# "agent_withdrawal",
|
||||
"agent_withdrawal"
|
||||
"agent_withdrawal_tax"
|
||||
"agent_withdrawal_tax_exemption"
|
||||
# "agent_real_name"
|
||||
# "feature",
|
||||
# "global_notifications"
|
||||
@@ -36,9 +38,10 @@ $tables = @(
|
||||
# "query_cleanup_config"
|
||||
# "user"
|
||||
# "user_auth"
|
||||
# "user_temp"
|
||||
# "example"
|
||||
"authorization"
|
||||
"authorization_face"
|
||||
# "authorization"
|
||||
# "authorization_face"
|
||||
# "admin_user"
|
||||
# "admin_user_role"
|
||||
# "admin_api",
|
||||
|
||||
@@ -1,43 +1,60 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for main
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `user`;
|
||||
|
||||
CREATE TABLE `user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`info` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`inside` tinyint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`info` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||
`inside` tinyint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_mobile` (`mobile`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for user_auth
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `user_auth`;
|
||||
|
||||
CREATE TABLE `user_auth` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`user_id` bigint NOT NULL DEFAULT '0',
|
||||
`auth_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台唯一id',
|
||||
`auth_type` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台类型',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_type_key` (`auth_type`,`auth_key`) USING BTREE,
|
||||
UNIQUE KEY `unique_userId_key` (`user_id`,`auth_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户授权表';
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`user_id` bigint NOT NULL DEFAULT '0',
|
||||
`auth_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台唯一id',
|
||||
`auth_type` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台类型',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_type_key` (`auth_type`, `auth_key`) USING BTREE,
|
||||
UNIQUE KEY `unique_userId_key` (`user_id`, `auth_type`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户授权表';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- 临时用户表(user_temp)
|
||||
CREATE TABLE `user_temp` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`auth_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台唯一id',
|
||||
`auth_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台类型',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_type_key` (`auth_type`, `auth_key`) USING BTREE
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '临时用户表';
|
||||
14
go.mod
14
go.mod
@@ -1,6 +1,8 @@
|
||||
module qnc-server
|
||||
|
||||
go 1.22.4
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/Masterminds/squirrel v1.5.4
|
||||
@@ -12,6 +14,7 @@ require (
|
||||
github.com/aliyun/credentials-go v1.4.6
|
||||
github.com/bytedance/sonic v1.13.0
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -21,8 +24,10 @@ require (
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
github.com/samber/lo v1.50.0
|
||||
github.com/shopspring/decimal v1.4.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/smartwalle/alipay/v3 v3.2.23
|
||||
github.com/sony/sonyflake v1.2.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.20
|
||||
github.com/zeromicro/go-zero v1.7.3
|
||||
@@ -47,6 +52,7 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
@@ -55,6 +61,7 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
@@ -69,6 +76,7 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
@@ -96,13 +104,15 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/image v0.28.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
12
go.sum
12
go.sum
@@ -114,6 +114,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
@@ -135,6 +137,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -236,6 +240,8 @@ github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/smartwalle/alipay/v3 v3.2.23 h1:i1VwJeu70EmwpsXXz6GZZnMAtRx5MTfn2dPoql/L3zE=
|
||||
github.com/smartwalle/alipay/v3 v3.2.23/go.mod h1:lVqFiupPf8YsAXaq5JXcwqnOUC2MCF+2/5vub+RlagE=
|
||||
github.com/smartwalle/ncrypto v1.0.4 h1:P2rqQxDepJwgeO5ShoC+wGcK2wNJDmcdBOWAksuIgx8=
|
||||
@@ -333,6 +339,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
|
||||
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@@ -403,8 +411,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestAesEcbMobileEncryption(t *testing.T) {
|
||||
// 测试手机号加密
|
||||
mobile := "15008098853"
|
||||
mobile := "18653052547"
|
||||
|
||||
keyStr := "ff83609b2b24fc73196aac3d3dfb874f"
|
||||
// 测试加密
|
||||
@@ -17,9 +17,10 @@ func TestAesEcbMobileEncryption(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("手机号加密失败: %v", err)
|
||||
}
|
||||
fmt.Println(encrypted)
|
||||
fmt.Printf("encrypted: %s\n", encrypted)
|
||||
jmstr := "m9EEeW9ZBBJmi1hx1k1uIQ=="
|
||||
// 测试解密
|
||||
decrypted, err := DecryptMobile(encrypted, keyStr)
|
||||
decrypted, err := DecryptMobile(jmstr, keyStr)
|
||||
if err != nil {
|
||||
t.Fatalf("手机号解密失败: %v", err)
|
||||
}
|
||||
|
||||
49
static/images/README.md
Normal file
49
static/images/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 推广二维码背景图片说明
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
static/images/
|
||||
├── tg_qrcode_1.jpg # promote 类型的背景图片(推广)
|
||||
├── yq_qrcode_1.png # invitation 类型的背景图片(邀请)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## 支持的二维码类型
|
||||
|
||||
- `promote`: 使用 tg_qrcode_1.jpg 作为背景(推广二维码)
|
||||
- `invitation`: 使用 yq_qrcode_1.png 作为背景(邀请二维码)
|
||||
|
||||
## 路径计算说明
|
||||
|
||||
程序运行时的路径计算:
|
||||
|
||||
- 工作目录:`D:\Code\qnc-project\qnc-server`
|
||||
- 相对路径:`static/images`
|
||||
- 完整路径示例:
|
||||
- promote: `D:\Code\qnc-project\qnc-server\static\images\tg_qrcode_1.jpg`
|
||||
- invitation: `D:\Code\qnc-project\qnc-server\static\images\yq_qrcode_1.png`
|
||||
|
||||
## 背景图片要求
|
||||
|
||||
- 格式:支持 PNG、JPEG 等常见格式
|
||||
- 尺寸:建议不小于 600x600 像素
|
||||
- 设计:考虑到二维码会放置在图片中心,请在设计时预留中心区域
|
||||
|
||||
## API 使用示例
|
||||
|
||||
```bash
|
||||
# 生成推广二维码
|
||||
GET /api/v1/agent/promotion/qrcode?qrcode_type=promote&qrcode_url=https://example.com/promotion/12345
|
||||
|
||||
# 生成邀请二维码
|
||||
GET /api/v1/agent/promotion/qrcode?qrcode_type=invitation&qrcode_url=https://example.com/invite/67890
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保 tg_qrcode_1.jpg 和 yq_qrcode_1.png 文件存在于此目录下
|
||||
2. 二维码会自动添加白色半透明背景以确保可读性
|
||||
3. 二维码大小会根据背景图片尺寸自动调整,范围在 120px-300px 之间
|
||||
4. 默认二维码类型是 `promote`
|
||||
5. 程序启动时必须在项目根目录(qnc-server)下执行
|
||||
BIN
static/images/image_placeholder.txt
Normal file
BIN
static/images/image_placeholder.txt
Normal file
Binary file not shown.
BIN
static/images/tg_qrcode_1.jpg
Normal file
BIN
static/images/tg_qrcode_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
BIN
static/images/yq_qrcode_1.png
Normal file
BIN
static/images/yq_qrcode_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 575 KiB |
Reference in New Issue
Block a user