feat(all): v1.0
This commit is contained in:
parent
d09e7a08cf
commit
6ce1a303f1
24
app/order/cmd/api/desc/order.api
Normal file
24
app/order/cmd/api/desc/order.api
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "支付服务"
|
||||||
|
desc: "支付服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: pay
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
// 微信支付回调
|
||||||
|
@handler WechatPayCallback
|
||||||
|
post /pay/wechat/callback
|
||||||
|
|
||||||
|
// 支付宝支付回调
|
||||||
|
@handler AlipayCallback
|
||||||
|
post /pay/alipay/callback
|
||||||
|
}
|
||||||
|
|
5
app/order/cmd/rpc/pb/order.proto
Normal file
5
app/order/cmd/rpc/pb/order.proto
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = "./pb";
|
||||||
|
|
||||||
|
package pb;
|
17
app/query/cmd/rpc/pb/query.proto
Normal file
17
app/query/cmd/rpc/pb/query.proto
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = "./pb";
|
||||||
|
|
||||||
|
package pb;
|
||||||
|
|
||||||
|
message StreamReq {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StreamResp {
|
||||||
|
string greet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service StreamGreeter {
|
||||||
|
rpc greet(StreamReq) returns (StreamResp);
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
root = "."
|
|
||||||
[build]
|
|
||||||
cmd = "go build -o ./tmp/main ./app/user/cmd/api/user.go" # 指定 user-api 的入口文件
|
|
||||||
bin = "./tmp/main"
|
|
||||||
include_ext = ["go", "tmpl", "html"]
|
|
||||||
exclude_dir = ["tmp", "deploy", "data"]
|
|
@ -10,7 +10,7 @@ info (
|
|||||||
type (
|
type (
|
||||||
sendSmsReq {
|
sendSmsReq {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
ActionType string `json:"actionType" validate:"required,oneof=loginCode registerCode QueryCode"`
|
ActionType string `json:"actionType" validate:"required,oneof=login register query"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
14
app/user/cmd/api/desc/main.api
Normal file
14
app/user/cmd/api/desc/main.api
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "单体服务中心"
|
||||||
|
desc: "单体服务中心"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
import "user.api"
|
||||||
|
import "query.api"
|
||||||
|
import "pay.api"
|
||||||
|
import "product.api"
|
28
app/user/cmd/api/desc/pay.api
Normal file
28
app/user/cmd/api/desc/pay.api
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "支付服务"
|
||||||
|
desc: "支付服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: pay
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
// 微信支付回调
|
||||||
|
@handler WechatPayCallback
|
||||||
|
post /pay/wechat/callback
|
||||||
|
|
||||||
|
// 支付宝支付回调
|
||||||
|
@handler AlipayCallback
|
||||||
|
post /pay/alipay/callback
|
||||||
|
|
||||||
|
// 微信退款回调
|
||||||
|
@handler WechatPayRefundCallback
|
||||||
|
post /pay/wechat/refund_callback
|
||||||
|
}
|
||||||
|
|
25
app/user/cmd/api/desc/product.api
Normal file
25
app/user/cmd/api/desc/product.api
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "产品服务"
|
||||||
|
desc: "产品服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
import (
|
||||||
|
"product/product.api"
|
||||||
|
)
|
||||||
|
@server (
|
||||||
|
prefix: api/v1/product
|
||||||
|
group: product
|
||||||
|
jwt: JwtAuth
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@handler GetProductByID
|
||||||
|
get /:id (GetProductByIDRequest) returns (ProductResponse)
|
||||||
|
|
||||||
|
@handler GetProductByEn
|
||||||
|
get /en/:product_en (GetProductByEnRequest) returns (ProductResponse)
|
||||||
|
}
|
||||||
|
|
36
app/user/cmd/api/desc/product/product.api
Normal file
36
app/user/cmd/api/desc/product/product.api
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "产品查询服务"
|
||||||
|
desc: "产品查询服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Product {
|
||||||
|
ProductName string `json:"product_name"`
|
||||||
|
ProductEn string `json:"product_en"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Notes string `json:"notes,optional"`
|
||||||
|
SellPrice float64 `json:"sell_price"`
|
||||||
|
Features []Feature `json:"features"` // 关联功能列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type Feature {
|
||||||
|
ID int64 `json:"id"` // 功能ID
|
||||||
|
ApiID string `json:"api_id"` // API标识
|
||||||
|
Name string `json:"name"` // 功能描述
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductByIDRequest {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductByEnRequest {
|
||||||
|
ProductEn string `path:"product_en"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductResponse {
|
||||||
|
Product
|
||||||
|
}
|
||||||
|
|
84
app/user/cmd/api/desc/query.api
Normal file
84
app/user/cmd/api/desc/query.api
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "产品查询服务"
|
||||||
|
desc: "产品查询服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
import (
|
||||||
|
"query/query.api"
|
||||||
|
)
|
||||||
|
|
||||||
|
//============================> query v1 <============================
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: query
|
||||||
|
jwt: JwtAuth
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "query marriage"
|
||||||
|
@handler marriage
|
||||||
|
post /query/marriage (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 家政服务查询
|
||||||
|
@doc "query home service"
|
||||||
|
@handler homeService
|
||||||
|
post /query/homeService (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 风险评估查询
|
||||||
|
@doc "query risk assessment"
|
||||||
|
@handler riskAssessment
|
||||||
|
post /query/riskAssessment (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 企业信息查询
|
||||||
|
@doc "query company info"
|
||||||
|
@handler companyInfo
|
||||||
|
post /query/companyInfo (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 租赁信息查询
|
||||||
|
@doc "query rental info"
|
||||||
|
@handler rentalInfo
|
||||||
|
post /query/rentalInfo (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 贷前背景调查
|
||||||
|
@doc "query pre-loan background check"
|
||||||
|
@handler preLoanBackgroundCheck
|
||||||
|
post /query/preLoanBackgroundCheck (QueryReq) returns (QueryResp)
|
||||||
|
|
||||||
|
// 一般背景调查
|
||||||
|
@doc "query general background check"
|
||||||
|
@handler backgroundCheck
|
||||||
|
post /query/backgroundCheck (QueryReq) returns (QueryResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: query
|
||||||
|
jwt: JwtAuth
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "查询示例"
|
||||||
|
@handler queryExample
|
||||||
|
get /query/example (QueryExampleReq) returns (QueryExampleResp)
|
||||||
|
|
||||||
|
@doc "查询列表"
|
||||||
|
@handler queryList
|
||||||
|
get /query/list (QueryListReq) returns (QueryListResp)
|
||||||
|
|
||||||
|
@doc "查询详情 按订单号 付款查询时"
|
||||||
|
@handler queryDetailByOrderId
|
||||||
|
get /query/orderId/:order_id (QueryDetailByOrderIdReq) returns (QueryDetailByOrderIdResp)
|
||||||
|
|
||||||
|
@doc "查询详情"
|
||||||
|
@handler queryDetail
|
||||||
|
get /query/:id (QueryDetailReq) returns (QueryDetailResp)
|
||||||
|
|
||||||
|
@doc "重试查询"
|
||||||
|
@handler queryRetry
|
||||||
|
post /query/retry/:id (QueryRetryReq) returns (QueryRetryResp)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
75
app/user/cmd/api/desc/query/query.api
Normal file
75
app/user/cmd/api/desc/query/query.api
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "产品查询服务"
|
||||||
|
desc: "产品查询服务"
|
||||||
|
author: "Liangzai"
|
||||||
|
email: "2440983361@qq.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryReq {
|
||||||
|
Data string `json:"data" validate:"required"`
|
||||||
|
}
|
||||||
|
QueryResp {
|
||||||
|
prepayID string `json:"prepay_id"`
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
Id int64 `json:"id"` // 主键ID
|
||||||
|
OrderId int64 `json:"order_id"` // 订单ID
|
||||||
|
UserId int64 `json:"user_id"` // 用户ID
|
||||||
|
ProductId int64 `json:"product_id"` // 产品ID
|
||||||
|
QueryData []map[string]interface{} `json:"query_data"`
|
||||||
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
|
QueryState string `json:"query_state"` // 查询状态
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryListReq {
|
||||||
|
Page int64 `form:"page"` // 页码
|
||||||
|
PageSize int64 `form:"page_size"` // 每页数据量
|
||||||
|
}
|
||||||
|
QueryListResp {
|
||||||
|
Total int64 `json:"total"` // 总记录数
|
||||||
|
List []Query `json:"list"` // 查询列表
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryExampleReq {
|
||||||
|
feature string `form:"feature"`
|
||||||
|
}
|
||||||
|
QueryExampleResp {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryDetailReq {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
QueryDetailResp {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryDetailByOrderIdReq {
|
||||||
|
OrderId int64 `path:"order_id"`
|
||||||
|
}
|
||||||
|
QueryDetailByOrderIdResp {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
QueryRetryReq {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
QueryRetryResp {}
|
||||||
|
)
|
||||||
|
|
@ -16,10 +16,10 @@ import (
|
|||||||
//============================> user v1 <============================
|
//============================> user v1 <============================
|
||||||
//no need login
|
//no need login
|
||||||
@server (
|
@server (
|
||||||
prefix: user/v1
|
prefix: api/v1
|
||||||
group: user
|
group: user
|
||||||
)
|
)
|
||||||
service user {
|
service main {
|
||||||
@doc "register"
|
@doc "register"
|
||||||
@handler register
|
@handler register
|
||||||
post /user/register (RegisterReq) returns (RegisterResp)
|
post /user/register (RegisterReq) returns (RegisterResp)
|
||||||
@ -35,14 +35,14 @@ service user {
|
|||||||
|
|
||||||
//need login
|
//need login
|
||||||
@server (
|
@server (
|
||||||
prefix: user/v1
|
prefix: api/v1
|
||||||
group: user
|
group: user
|
||||||
jwt: JwtAuth
|
jwt: JwtAuth
|
||||||
)
|
)
|
||||||
service user {
|
service main {
|
||||||
@doc "get user info"
|
@doc "get user info"
|
||||||
@handler detail
|
@handler detail
|
||||||
post /user/detail (UserInfoReq) returns (UserInfoResp)
|
get /user/detail returns (UserInfoResp)
|
||||||
|
|
||||||
@doc "wechat mini auth"
|
@doc "wechat mini auth"
|
||||||
@handler wxMiniAuth
|
@handler wxMiniAuth
|
||||||
@ -51,10 +51,10 @@ service user {
|
|||||||
|
|
||||||
//============================> auth v1 <============================
|
//============================> auth v1 <============================
|
||||||
@server (
|
@server (
|
||||||
prefix: auth/v1
|
prefix: api/v1
|
||||||
group: auth
|
group: auth
|
||||||
)
|
)
|
||||||
service user {
|
service main {
|
||||||
@doc "get mobile verify code"
|
@doc "get mobile verify code"
|
||||||
@handler sendSms
|
@handler sendSms
|
||||||
post /auth/sendSms (sendSmsReq)
|
post /auth/sendSms (sendSmsReq)
|
||||||
|
@ -64,7 +64,6 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
UserInfoReq {}
|
|
||||||
UserInfoResp {
|
UserInfoResp {
|
||||||
UserInfo User `json:"userInfo"`
|
UserInfo User `json:"userInfo"`
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
Name: user
|
|
||||||
Host: 0.0.0.0
|
|
||||||
Port: 8888
|
|
||||||
DataSource: "qnc:5vg67b3UNHu8@tcp(127.0.0.1:20001)/qnc?charset=utf8mb4&parseTime=True&loc=Local"
|
|
||||||
CacheRedis:
|
|
||||||
- Host: "127.0.0.1:20002"
|
|
||||||
Pass: "3m3WsgyCKWqz" # Redis 密码,如果未设置则留空
|
|
||||||
Type: "node" # 单节点模式
|
|
||||||
JwtAuth:
|
|
||||||
AccessSecret: "WUvoIwL-FK0qnlxhvxR9tV6SjfOpeJMpKmY2QvT99lA"
|
|
||||||
AccessExpire: 86400 # JWT过期时间
|
|
||||||
RefreshAfter: 43200 # 更新时间
|
|
||||||
VerifyCode:
|
|
||||||
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
|
||||||
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
|
||||||
EndpointURL: "dysmsapi.aliyuncs.com"
|
|
||||||
SignName: "天远数据"
|
|
||||||
TemplateCode: "SMS_474525324"
|
|
||||||
ValidTime: 300
|
|
@ -11,6 +11,11 @@ type Config struct {
|
|||||||
CacheRedis cache.CacheConf
|
CacheRedis cache.CacheConf
|
||||||
JwtAuth JwtAuth // JWT 鉴权相关配置
|
JwtAuth JwtAuth // JWT 鉴权相关配置
|
||||||
VerifyCode VerifyCode
|
VerifyCode VerifyCode
|
||||||
|
Encrypt Encrypt
|
||||||
|
Alipay AlipayConfig
|
||||||
|
Wxpay WxpayConfig
|
||||||
|
Ali AliConfig
|
||||||
|
WestConfig WestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// JwtAuth 用于 JWT 鉴权配置
|
// JwtAuth 用于 JWT 鉴权配置
|
||||||
@ -27,3 +32,32 @@ type VerifyCode struct {
|
|||||||
TemplateCode string
|
TemplateCode string
|
||||||
ValidTime int
|
ValidTime int
|
||||||
}
|
}
|
||||||
|
type Encrypt struct {
|
||||||
|
SecretKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlipayConfig struct {
|
||||||
|
AppID string
|
||||||
|
PrivateKey string
|
||||||
|
AlipayPublicKey string
|
||||||
|
IsProduction bool
|
||||||
|
NotifyUrl string
|
||||||
|
}
|
||||||
|
type WxpayConfig struct {
|
||||||
|
AppID string
|
||||||
|
MchID string
|
||||||
|
MchCertificateSerialNumber string
|
||||||
|
MchApiv3Key string
|
||||||
|
MchPrivateKeyPath string
|
||||||
|
NotifyUrl string
|
||||||
|
RefundNotifyUrl string
|
||||||
|
}
|
||||||
|
type AliConfig struct {
|
||||||
|
Code string
|
||||||
|
}
|
||||||
|
type WestConfig struct {
|
||||||
|
Url string
|
||||||
|
Key string
|
||||||
|
SecretId string
|
||||||
|
SecretSecondId string
|
||||||
|
}
|
||||||
|
@ -5,6 +5,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
auth "qnc-server/app/user/cmd/api/internal/handler/auth"
|
auth "qnc-server/app/user/cmd/api/internal/handler/auth"
|
||||||
|
pay "qnc-server/app/user/cmd/api/internal/handler/pay"
|
||||||
|
product "qnc-server/app/user/cmd/api/internal/handler/product"
|
||||||
|
query "qnc-server/app/user/cmd/api/internal/handler/query"
|
||||||
user "qnc-server/app/user/cmd/api/internal/handler/user"
|
user "qnc-server/app/user/cmd/api/internal/handler/user"
|
||||||
"qnc-server/app/user/cmd/api/internal/svc"
|
"qnc-server/app/user/cmd/api/internal/svc"
|
||||||
|
|
||||||
@ -21,7 +24,131 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
Handler: auth.SendSmsHandler(serverCtx),
|
Handler: auth.SendSmsHandler(serverCtx),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rest.WithPrefix("/auth/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/pay/alipay/callback",
|
||||||
|
Handler: pay.AlipayCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/pay/wechat/callback",
|
||||||
|
Handler: pay.WechatPayCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/pay/wechat/refund_callback",
|
||||||
|
Handler: pay.WechatPayRefundCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
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.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/product"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// query general background check
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/backgroundCheck",
|
||||||
|
Handler: query.BackgroundCheckHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query company info
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/companyInfo",
|
||||||
|
Handler: query.CompanyInfoHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query home service
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/homeService",
|
||||||
|
Handler: query.HomeServiceHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query marriage
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/marriage",
|
||||||
|
Handler: query.MarriageHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query pre-loan background check
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/preLoanBackgroundCheck",
|
||||||
|
Handler: query.PreLoanBackgroundCheckHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query rental info
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/rentalInfo",
|
||||||
|
Handler: query.RentalInfoHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// query risk assessment
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/query/riskAssessment",
|
||||||
|
Handler: query.RiskAssessmentHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 查询详情
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/query/:id",
|
||||||
|
Handler: query.QueryDetailHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 查询示例
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/query/example",
|
||||||
|
Handler: query.QueryExampleHandler(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.MethodPost,
|
||||||
|
Path: "/query/retry/:id",
|
||||||
|
Handler: query.QueryRetryHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
@ -45,14 +172,14 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
Handler: user.RegisterHandler(serverCtx),
|
Handler: user.RegisterHandler(serverCtx),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rest.WithPrefix("/user/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
// get user info
|
// get user info
|
||||||
Method: http.MethodPost,
|
Method: http.MethodGet,
|
||||||
Path: "/user/detail",
|
Path: "/user/detail",
|
||||||
Handler: user.DetailHandler(serverCtx),
|
Handler: user.DetailHandler(serverCtx),
|
||||||
},
|
},
|
||||||
@ -64,6 +191,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
rest.WithJwt(serverCtx.Config.JwtAuth.AccessSecret),
|
||||||
rest.WithPrefix("/user/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,8 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo
|
|||||||
|
|
||||||
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||||
// 检查手机号是否在一分钟内已发送过验证码
|
// 检查手机号是否在一分钟内已发送过验证码
|
||||||
redisKey := fmt.Sprintf("%s:%s", req.ActionType, req.Mobile)
|
limitCodeKey := fmt.Sprintf("limit:%s:%s", req.ActionType, req.Mobile)
|
||||||
exists, err := l.svcCtx.Redis.Exists(redisKey)
|
exists, err := l.svcCtx.Redis.Exists(limitCodeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", req.Mobile)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 读取redis缓存失败: %s", req.Mobile)
|
||||||
}
|
}
|
||||||
@ -55,14 +55,14 @@ func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
|||||||
if *smsResp.Body.Code != "OK" {
|
if *smsResp.Body.Code != "OK" {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 阿里客户端响应失败: %s", *smsResp.Body.Message)
|
||||||
}
|
}
|
||||||
|
codeKey := fmt.Sprintf("%s:%s", req.ActionType, req.Mobile)
|
||||||
// 将验证码保存到 Redis,设置过期时间
|
// 将验证码保存到 Redis,设置过期时间
|
||||||
err = l.svcCtx.Redis.Setex(req.Mobile, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
|
err = l.svcCtx.Redis.Setex(codeKey, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置过期时间失败: %+v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置过期时间失败: %+v", err)
|
||||||
}
|
}
|
||||||
// 在 Redis 中设置 1 分钟的标记,限制重复请求
|
// 在 Redis 中设置 1 分钟的标记,限制重复请求
|
||||||
err = l.svcCtx.Redis.Setex(redisKey, code, 60) // 标记 1 分钟内不能重复请求
|
err = l.svcCtx.Redis.Setex(limitCodeKey, code, 60) // 标记 1 分钟内不能重复请求
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置限制重复请求失败: %+v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短信发送, 验证码设置限制重复请求失败: %+v", err)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ func NewDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DetailLogi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DetailLogic) Detail(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
|
func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
|
||||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %+v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %+v", err)
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
jwtx "qnc-server/common/jwt"
|
jwtx "qnc-server/common/jwt"
|
||||||
"qnc-server/common/tool"
|
"qnc-server/common/tool"
|
||||||
"qnc-server/common/xerr"
|
"qnc-server/common/xerr"
|
||||||
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
@ -32,7 +33,7 @@ func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Register
|
|||||||
|
|
||||||
func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) {
|
func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterResp, err error) {
|
||||||
// 检查手机号是否在一分钟内已发送过验证码
|
// 检查手机号是否在一分钟内已发送过验证码
|
||||||
redisKey := fmt.Sprintf("%s:%s", "registerCode", req.Mobile)
|
redisKey := fmt.Sprintf("%s:%s", "register", req.Mobile)
|
||||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, redis.Nil) {
|
if errors.Is(err, redis.Nil) {
|
||||||
@ -58,7 +59,7 @@ func (l *RegisterLogic) Register(req *types.RegisterReq) (resp *types.RegisterRe
|
|||||||
user.Nickname = req.Mobile
|
user.Nickname = req.Mobile
|
||||||
}
|
}
|
||||||
if len(req.Password) > 0 {
|
if len(req.Password) > 0 {
|
||||||
user.Password = tool.Md5ByString(req.Password)
|
user.Password = lzUtils.StringToNullString(tool.Md5ByString(req.Password))
|
||||||
}
|
}
|
||||||
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
||||||
if userInsertErr != nil {
|
if userInsertErr != nil {
|
||||||
|
179
app/user/cmd/api/internal/queue/paySuccessNotify.go
Normal file
179
app/user/cmd/api/internal/queue/paySuccessNotify.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hibiken/asynq"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/svc"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
"qnc-server/app/user/model"
|
||||||
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PaySuccessNotifyUserHandler struct {
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPaySuccessNotifyUserHandler(svcCtx *svc.ServiceContext) *PaySuccessNotifyUserHandler {
|
||||||
|
return &PaySuccessNotifyUserHandler{
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload struct {
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
|
||||||
|
// 从任务的负载中解码数据
|
||||||
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||||
|
return fmt.Errorf("解析任务负载失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := l.svcCtx.OrderModel.FindOne(ctx, payload.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无效的订单ID: %d, %v", payload.OrderID, err)
|
||||||
|
}
|
||||||
|
if order.Status != "paid" {
|
||||||
|
err = fmt.Errorf("无效的订单: %d", payload.OrderID)
|
||||||
|
logx.Errorf("处理任务失败,原因: %v", err)
|
||||||
|
return asynq.SkipRetry
|
||||||
|
}
|
||||||
|
product, err := l.svcCtx.ProductModel.FindOne(ctx, order.ProductId)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("找不到相关产品: orderID: %d, productID: %d", payload.OrderID, order.ProductId)
|
||||||
|
}
|
||||||
|
|
||||||
|
query, findQueryErr := l.svcCtx.QueryModel.FindOneByOrderId(ctx, order.Id)
|
||||||
|
if findQueryErr != nil {
|
||||||
|
findQueryErr = fmt.Errorf("获取任务请求参数失败: %v", findQueryErr)
|
||||||
|
logx.Errorf("处理任务失败,原因: %v", findQueryErr)
|
||||||
|
return asynq.SkipRetry
|
||||||
|
}
|
||||||
|
if query.QueryState != "pending" {
|
||||||
|
err = fmt.Errorf("查询已处理: %d", query.Id)
|
||||||
|
logx.Errorf("处理任务失败,原因: %v", err)
|
||||||
|
return asynq.SkipRetry
|
||||||
|
}
|
||||||
|
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||||
|
key, decodeErr := hex.DecodeString(secretKey)
|
||||||
|
if decodeErr != nil {
|
||||||
|
err = fmt.Errorf("获取AES密钥失败: %v", decodeErr)
|
||||||
|
return l.handleError(ctx, err, order, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptData, aesdecryptErr := crypto.AesDecrypt(query.QueryParams, key)
|
||||||
|
if aesdecryptErr != nil {
|
||||||
|
aesdecryptErr = fmt.Errorf("加密响应信息失败: %v", aesdecryptErr)
|
||||||
|
return l.handleError(ctx, aesdecryptErr, order, query)
|
||||||
|
}
|
||||||
|
requests, exists := types.WestDexParams[product.ProductEn]
|
||||||
|
if !exists {
|
||||||
|
err = fmt.Errorf("未找到有效的参数配置: productEn: %s", product.ProductEn)
|
||||||
|
return l.handleError(ctx, err, order, query)
|
||||||
|
}
|
||||||
|
// 根据产品类型选择结构体类型
|
||||||
|
var requestData interface{}
|
||||||
|
switch product.ProductEn {
|
||||||
|
case "marriage":
|
||||||
|
requestData = &types.MarriageReq{}
|
||||||
|
case "homeservice":
|
||||||
|
requestData = &types.HomeServiceReq{}
|
||||||
|
case "riskassessment":
|
||||||
|
requestData = &types.RiskAssessmentReq{}
|
||||||
|
case "companyinfo":
|
||||||
|
requestData = &types.CompanyInfoReq{}
|
||||||
|
case "rentalinfo":
|
||||||
|
requestData = &types.RentalInfoReq{}
|
||||||
|
case "preloanbackgroundcheck":
|
||||||
|
requestData = &types.PreLoanBackgroundCheckReq{}
|
||||||
|
case "backgroundcheck":
|
||||||
|
requestData = &types.BackgroundCheckReq{}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("未支持的产品类型: productEn: %s", product.ProductEn)
|
||||||
|
return l.handleError(ctx, err, order, query)
|
||||||
|
}
|
||||||
|
unmarshalErr := json.Unmarshal(decryptData, &requestData)
|
||||||
|
if unmarshalErr != nil {
|
||||||
|
unmarshalErr = fmt.Errorf("解析参数失败: %v", unmarshalErr)
|
||||||
|
return l.handleError(ctx, unmarshalErr, order, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedResponse, err := l.svcCtx.WestDexService.ProcessRequests(requestData, requests)
|
||||||
|
if err != nil {
|
||||||
|
return l.handleError(ctx, err, order, query)
|
||||||
|
}
|
||||||
|
// 加密返回响应
|
||||||
|
encryptData, aesEncryptErr := crypto.AesEncrypt(combinedResponse, key)
|
||||||
|
if aesEncryptErr != nil {
|
||||||
|
err = fmt.Errorf("加密响应信息失败: %v", aesEncryptErr)
|
||||||
|
return l.handleError(ctx, aesEncryptErr, order, query)
|
||||||
|
}
|
||||||
|
query.QueryData = lzUtils.StringToNullString(encryptData)
|
||||||
|
updateErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
||||||
|
if updateErr != nil {
|
||||||
|
err = fmt.Errorf("保存响应数据失败: %v", updateErr)
|
||||||
|
return l.handleError(ctx, updateErr, order, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
query.QueryState = "success"
|
||||||
|
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
||||||
|
if updateQueryErr != nil {
|
||||||
|
updateQueryErr = fmt.Errorf("修改查询状态失败: %v", updateQueryErr)
|
||||||
|
return l.handleError(ctx, updateQueryErr, order, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义一个中间件函数
|
||||||
|
func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error, order *model.Order, query *model.Query) error {
|
||||||
|
logx.Errorf("处理任务失败,原因: %v", err)
|
||||||
|
|
||||||
|
if order.Status == "paid" && query.QueryState == "pending" {
|
||||||
|
// 更新查询状态为失败
|
||||||
|
query.QueryState = "failed"
|
||||||
|
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
||||||
|
if updateQueryErr != nil {
|
||||||
|
logx.Errorf("更新查询状态失败,订单ID: %d, 错误: %v", order.Id, updateQueryErr)
|
||||||
|
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"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// 直接成功
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return asynq.SkipRetry
|
||||||
|
}
|
28
app/user/cmd/api/internal/queue/routes.go
Normal file
28
app/user/cmd/api/internal/queue/routes.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hibiken/asynq"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/svc"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CronJob struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCronJob(ctx context.Context, svcCtx *svc.ServiceContext) *CronJob {
|
||||||
|
return &CronJob{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CronJob) Register() *asynq.ServeMux {
|
||||||
|
|
||||||
|
mux := asynq.NewServeMux()
|
||||||
|
|
||||||
|
mux.Handle(types.MsgPaySuccessQuery, NewPaySuccessNotifyUserHandler(l.svcCtx))
|
||||||
|
return mux
|
||||||
|
}
|
137
app/user/cmd/api/internal/service/alipayService.go
Normal file
137
app/user/cmd/api/internal/service/alipayService.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/smartwalle/alipay/v3"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"net/http"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliPayService struct {
|
||||||
|
config config.AlipayConfig
|
||||||
|
AlipayClient *alipay.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAliPayService 是一个构造函数,用于初始化 AliPayService
|
||||||
|
func NewAliPayService(c config.Config) *AliPayService {
|
||||||
|
client, err := alipay.New(c.Alipay.AppID, c.Alipay.PrivateKey, c.Alipay.IsProduction)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("创建支付宝客户端失败: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载支付宝公钥
|
||||||
|
err = client.LoadAliPayPublicKey(c.Alipay.AlipayPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("加载支付宝公钥失败: %v", err))
|
||||||
|
}
|
||||||
|
return &AliPayService{
|
||||||
|
config: c.Alipay,
|
||||||
|
AlipayClient: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, outTradeNo string) (string, error) {
|
||||||
|
client := a.AlipayClient
|
||||||
|
totalAmount := lzUtils.ToAlipayAmount(amount)
|
||||||
|
// 构造移动支付请求
|
||||||
|
p := alipay.TradeAppPay{
|
||||||
|
Trade: alipay.Trade{
|
||||||
|
Subject: subject,
|
||||||
|
OutTradeNo: outTradeNo,
|
||||||
|
TotalAmount: totalAmount,
|
||||||
|
ProductCode: "QUICK_MSECURITY_PAY", // 移动端支付专用代码
|
||||||
|
NotifyURL: a.config.NotifyUrl, // 异步回调通知地址
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取APP支付字符串,这里会签名
|
||||||
|
payStr, err := client.TradeAppPay(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建支付宝订单失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AliRefund 发起支付宝退款
|
||||||
|
func (a *AliPayService) AliRefund(ctx context.Context, outTradeNo string, refundAmount float64) (*alipay.TradeRefundRsp, error) {
|
||||||
|
refund := alipay.TradeRefund{
|
||||||
|
OutTradeNo: outTradeNo,
|
||||||
|
RefundAmount: lzUtils.ToAlipayAmount(refundAmount),
|
||||||
|
OutRequestNo: fmt.Sprintf("%s-refund", outTradeNo),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发起退款请求
|
||||||
|
refundResp, err := a.AlipayClient.TradeRefund(ctx, refund)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("支付宝退款请求错误:%v", err)
|
||||||
|
}
|
||||||
|
return refundResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleAliPaymentNotification 支付宝支付回调
|
||||||
|
func (a *AliPayService) HandleAliPaymentNotification(r *http.Request) (*alipay.Notification, error) {
|
||||||
|
// 解析表单
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析请求表单失败:%v", err)
|
||||||
|
}
|
||||||
|
// 解析并验证通知,DecodeNotification 会自动验证签名
|
||||||
|
notification, err := a.AlipayClient.DecodeNotification(r.Form)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("验证签名失败: %v", err)
|
||||||
|
}
|
||||||
|
return notification, nil
|
||||||
|
}
|
||||||
|
func (a *AliPayService) QueryOrderStatus(ctx context.Context, outTradeNo string) (*alipay.TradeQueryRsp, error) {
|
||||||
|
queryRequest := alipay.TradeQuery{
|
||||||
|
OutTradeNo: outTradeNo,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发起查询请求
|
||||||
|
resp, err := a.AlipayClient.TradeQuery(ctx, queryRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("查询支付宝订单失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回交易状态
|
||||||
|
if resp.IsSuccess() {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("查询支付宝订单失败: %v", resp.SubMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateOutTradeNo 生成唯一订单号的函数
|
||||||
|
func (a *AliPayService) GenerateOutTradeNo() string {
|
||||||
|
length := 16
|
||||||
|
// 获取当前时间戳
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
|
||||||
|
// 转换为字符串
|
||||||
|
timeStr := strconv.FormatInt(timestamp, 10)
|
||||||
|
|
||||||
|
// 生成随机数
|
||||||
|
mathrand.Seed(time.Now().UnixNano())
|
||||||
|
randomPart := strconv.Itoa(mathrand.Intn(1000000))
|
||||||
|
|
||||||
|
// 组合时间戳和随机数
|
||||||
|
combined := timeStr + randomPart
|
||||||
|
|
||||||
|
// 如果长度超出指定值,则截断;如果不够,则填充随机字符
|
||||||
|
if len(combined) >= length {
|
||||||
|
return combined[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果长度不够,填充0
|
||||||
|
for len(combined) < length {
|
||||||
|
combined += strconv.Itoa(mathrand.Intn(10)) // 填充随机数
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined
|
||||||
|
}
|
59
app/user/cmd/api/internal/service/asynqService.go
Normal file
59
app/user/cmd/api/internal/service/asynqService.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// asynq_service.go
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/hibiken/asynq"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AsynqService struct {
|
||||||
|
client *asynq.Client
|
||||||
|
config config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsynqService 创建并初始化 Asynq 客户端
|
||||||
|
func NewAsynqService(c config.Config) *AsynqService {
|
||||||
|
client := asynq.NewClient(asynq.RedisClientOpt{
|
||||||
|
Addr: c.CacheRedis[0].Host,
|
||||||
|
Password: c.CacheRedis[0].Pass,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &AsynqService{client: client, config: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close 关闭 Asynq 客户端
|
||||||
|
func (s *AsynqService) Close() error {
|
||||||
|
return s.client.Close()
|
||||||
|
}
|
||||||
|
func (s *AsynqService) SendQueryTask(orderID int64) error {
|
||||||
|
// 准备任务的 payload
|
||||||
|
payload := types.MsgPaySuccessQueryPayload{
|
||||||
|
OrderID: orderID,
|
||||||
|
}
|
||||||
|
payloadBytes, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("发送异步任务失败 (无法编码 payload): %v, 订单号: %d", err, orderID)
|
||||||
|
return err // 直接返回错误,避免继续执行
|
||||||
|
}
|
||||||
|
|
||||||
|
options := []asynq.Option{
|
||||||
|
asynq.MaxRetry(5), // 设置最大重试次数
|
||||||
|
}
|
||||||
|
// 创建任务
|
||||||
|
task := asynq.NewTask(types.MsgPaySuccessQuery, payloadBytes, options...)
|
||||||
|
|
||||||
|
// 将任务加入队列并获取任务信息
|
||||||
|
info, err := s.client.Enqueue(task)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("发送异步任务失败 (加入队列失败): %+v, 订单号: %d", err, orderID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录成功日志,带上任务 ID 和队列信息
|
||||||
|
logx.Infof("发送异步任务成功,任务ID: %s, 队列: %s, 订单号: %d", info.ID, info.Queue, orderID)
|
||||||
|
return nil
|
||||||
|
}
|
116
app/user/cmd/api/internal/service/verificationService.go
Normal file
116
app/user/cmd/api/internal/service/verificationService.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VerificationService struct {
|
||||||
|
c config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVerificationService(c config.Config) *VerificationService {
|
||||||
|
return &VerificationService{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TwoFactorVerificationRequest struct {
|
||||||
|
Name string
|
||||||
|
IDCard string
|
||||||
|
}
|
||||||
|
type TwoFactorVerificationResp struct {
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data *TwoFactorVerificationData `json:"data"` //
|
||||||
|
}
|
||||||
|
type TwoFactorVerificationData struct {
|
||||||
|
Birthday string `json:"birthday"`
|
||||||
|
Result int `json:"result"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
OrderNo string `json:"orderNo"`
|
||||||
|
Sex string `json:"sex"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerificationResult 定义校验结果结构体
|
||||||
|
type VerificationResult struct {
|
||||||
|
Passed bool
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationError 定义校验错误类型
|
||||||
|
type ValidationError struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ValidationError) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VerificationService) TwoFactorVerification(request TwoFactorVerificationRequest) (*VerificationResult, error) {
|
||||||
|
appCode := r.c.Ali.Code
|
||||||
|
requestUrl := "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check"
|
||||||
|
|
||||||
|
// 构造查询参数
|
||||||
|
data := url.Values{}
|
||||||
|
data.Add("name", request.Name)
|
||||||
|
data.Add("idcard", request.IDCard)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, requestUrl, strings.NewReader(data.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建请求失败: %+v", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "APPCODE "+appCode)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("请求失败: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("请求失败, 状态码: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("响应体读取失败:%v", err)
|
||||||
|
}
|
||||||
|
var twoFactorVerificationResp TwoFactorVerificationResp
|
||||||
|
err = json.Unmarshal(respBody, &twoFactorVerificationResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("二要素解析错误: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !twoFactorVerificationResp.Success {
|
||||||
|
return &VerificationResult{
|
||||||
|
Passed: false,
|
||||||
|
Err: &ValidationError{Message: "请输入有效的身份证号码"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if twoFactorVerificationResp.Code != 200 {
|
||||||
|
return &VerificationResult{
|
||||||
|
Passed: false,
|
||||||
|
Err: &ValidationError{Message: twoFactorVerificationResp.Msg},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if twoFactorVerificationResp.Data.Result == 1 {
|
||||||
|
return &VerificationResult{
|
||||||
|
Passed: false,
|
||||||
|
Err: &ValidationError{Message: "姓名与身份证不一致"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &VerificationResult{Passed: true, Err: nil}, nil
|
||||||
|
}
|
188
app/user/cmd/api/internal/service/wechatpayService.go
Normal file
188
app/user/cmd/api/internal/service/wechatpayService.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/app"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/utils"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"net/http"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TradeStateSuccess = "SUCCESS" // 支付成功
|
||||||
|
TradeStateRefund = "REFUND" // 转入退款
|
||||||
|
TradeStateNotPay = "NOTPAY" // 未支付
|
||||||
|
TradeStateClosed = "CLOSED" // 已关闭
|
||||||
|
TradeStateRevoked = "REVOKED" // 已撤销(付款码支付)
|
||||||
|
TradeStateUserPaying = "USERPAYING" // 用户支付中(付款码支付)
|
||||||
|
TradeStatePayError = "PAYERROR" // 支付失败(其他原因,如银行返回失败)
|
||||||
|
)
|
||||||
|
|
||||||
|
type WechatPayService struct {
|
||||||
|
config config.WxpayConfig
|
||||||
|
wechatClient *core.Client
|
||||||
|
notifyHandler *notify.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWechatPayService 初始化微信支付服务
|
||||||
|
func NewWechatPayService(c config.Config) *WechatPayService {
|
||||||
|
// 从配置中加载商户信息
|
||||||
|
mchID := c.Wxpay.MchID
|
||||||
|
mchCertificateSerialNumber := c.Wxpay.MchCertificateSerialNumber
|
||||||
|
mchAPIv3Key := c.Wxpay.MchApiv3Key
|
||||||
|
|
||||||
|
// 从文件中加载商户私钥
|
||||||
|
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(c.Wxpay.MchPrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("加载商户私钥失败: %v", err)
|
||||||
|
panic(fmt.Sprintf("初始化失败,服务停止: %v", err)) // 记录错误并停止程序
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用商户私钥和其他参数初始化微信支付客户端
|
||||||
|
opts := []core.ClientOption{
|
||||||
|
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
|
||||||
|
}
|
||||||
|
client, err := core.NewClient(context.Background(), opts...)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("创建微信支付客户端失败: %v", err)
|
||||||
|
panic(fmt.Sprintf("初始化失败,服务停止: %v", err)) // 记录错误并停止程序
|
||||||
|
}
|
||||||
|
// 在初始化时获取证书访问器并创建 notifyHandler
|
||||||
|
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||||
|
notifyHandler, err := notify.NewRSANotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("获取证书访问器失败: %v", err)
|
||||||
|
panic(fmt.Sprintf("初始化失败,服务停止: %v", err)) // 记录错误并停止程序
|
||||||
|
}
|
||||||
|
return &WechatPayService{
|
||||||
|
config: c.Wxpay,
|
||||||
|
wechatClient: client,
|
||||||
|
notifyHandler: notifyHandler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateWechatAppOrder 创建微信APP支付订单
|
||||||
|
func (w *WechatPayService) CreateWechatAppOrder(ctx context.Context, amount float64, description string, outTradeNo string) (string, error) {
|
||||||
|
totalAmount := lzUtils.ToWechatAmount(amount)
|
||||||
|
|
||||||
|
// 构建支付请求参数
|
||||||
|
payRequest := app.PrepayRequest{
|
||||||
|
Appid: core.String(w.config.AppID),
|
||||||
|
Mchid: core.String(w.config.MchID),
|
||||||
|
Description: core.String(description),
|
||||||
|
OutTradeNo: core.String(outTradeNo),
|
||||||
|
NotifyUrl: core.String(w.config.NotifyUrl),
|
||||||
|
Amount: &app.Amount{
|
||||||
|
Total: core.Int64(totalAmount),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化 AppApiService
|
||||||
|
svc := app.AppApiService{Client: w.wechatClient}
|
||||||
|
|
||||||
|
// 发起预支付请求
|
||||||
|
resp, result, err := svc.Prepay(ctx, payRequest)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回预支付交易会话标识
|
||||||
|
return *resp.PrepayId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleWechatPayNotification 处理微信支付回调
|
||||||
|
func (w *WechatPayService) HandleWechatPayNotification(ctx context.Context, req *http.Request) (*payments.Transaction, error) {
|
||||||
|
transaction := new(payments.Transaction)
|
||||||
|
_, err := w.notifyHandler.ParseNotifyRequest(ctx, req, transaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("微信支付通知处理失败: %v", err)
|
||||||
|
}
|
||||||
|
// 返回交易信息
|
||||||
|
return transaction, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleRefundNotification 处理微信退款回调
|
||||||
|
func (w *WechatPayService) HandleRefundNotification(ctx context.Context, req *http.Request) (*refunddomestic.Refund, error) {
|
||||||
|
refund := new(refunddomestic.Refund)
|
||||||
|
_, err := w.notifyHandler.ParseNotifyRequest(ctx, req, refund)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("微信退款回调通知处理失败: %v", err)
|
||||||
|
}
|
||||||
|
return refund, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryOrderStatus 主动查询订单状态
|
||||||
|
func (w *WechatPayService) QueryOrderStatus(ctx context.Context, transactionID string) (*payments.Transaction, error) {
|
||||||
|
svc := jsapi.JsapiApiService{Client: w.wechatClient}
|
||||||
|
|
||||||
|
// 调用 QueryOrderById 方法查询订单状态
|
||||||
|
resp, result, err := svc.QueryOrderById(ctx, jsapi.QueryOrderByIdRequest{
|
||||||
|
TransactionId: core.String(transactionID),
|
||||||
|
Mchid: core.String(w.config.MchID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeChatRefund 申请微信退款
|
||||||
|
func (w *WechatPayService) WeChatRefund(ctx context.Context, outTradeNo string, refundAmount float64, totalAmount float64) error {
|
||||||
|
|
||||||
|
// 生成唯一的退款单号
|
||||||
|
outRefundNo := fmt.Sprintf("%s-refund", outTradeNo)
|
||||||
|
|
||||||
|
// 初始化退款服务
|
||||||
|
svc := refunddomestic.RefundsApiService{Client: w.wechatClient}
|
||||||
|
|
||||||
|
// 创建退款请求
|
||||||
|
resp, result, err := svc.Create(ctx, refunddomestic.CreateRequest{
|
||||||
|
OutTradeNo: core.String(outTradeNo),
|
||||||
|
OutRefundNo: core.String(outRefundNo),
|
||||||
|
NotifyUrl: core.String(w.config.RefundNotifyUrl),
|
||||||
|
Amount: &refunddomestic.AmountReq{
|
||||||
|
Currency: core.String("CNY"),
|
||||||
|
Refund: core.Int64(lzUtils.ToWechatAmount(refundAmount)),
|
||||||
|
Total: core.Int64(lzUtils.ToWechatAmount(totalAmount)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("微信订单申请退款错误: %v", err)
|
||||||
|
}
|
||||||
|
// 打印退款结果
|
||||||
|
logx.Infof("退款申请成功,状态码=%d,退款单号=%s,微信退款单号=%s", result.Response.StatusCode, *resp.OutRefundNo, *resp.RefundId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateOutTradeNo 生成唯一订单号
|
||||||
|
func (w *WechatPayService) GenerateOutTradeNo() string {
|
||||||
|
length := 16
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
timeStr := strconv.FormatInt(timestamp, 10)
|
||||||
|
randomPart := strconv.Itoa(int(timestamp % 1e6))
|
||||||
|
combined := timeStr + randomPart
|
||||||
|
|
||||||
|
if len(combined) >= length {
|
||||||
|
return combined[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(combined) < length {
|
||||||
|
combined += strconv.Itoa(int(timestamp % 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined
|
||||||
|
}
|
707
app/user/cmd/api/internal/service/westdexService.go
Normal file
707
app/user/cmd/api/internal/service/westdexService.go
Normal file
@ -0,0 +1,707 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
"qnc-server/pkg/lzkit/crypto"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WestResp struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
ErrorCode *int `json:"error_code"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
type G05HZ01WestResp struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Data json.RawMessage `json:"data"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
ErrorCode *int `json:"error_code"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
type WestDexService struct {
|
||||||
|
config config.WestConfig
|
||||||
|
}
|
||||||
|
type APIResponseData struct {
|
||||||
|
ApiID string `json:"apiID"`
|
||||||
|
Data json.RawMessage `json:"data"` // 这里用 RawMessage 来存储原始的 data
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWestDexService 是一个构造函数,用于初始化 WestDexService
|
||||||
|
func NewWestDexService(c config.Config) *WestDexService {
|
||||||
|
return &WestDexService{
|
||||||
|
config: c.WestConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallAPI 调用西部数据的 API
|
||||||
|
func (w *WestDexService) CallAPI(code string, reqData map[string]interface{}) (resp []byte, err error) {
|
||||||
|
// 生成当前的13位时间戳
|
||||||
|
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||||
|
|
||||||
|
// 构造请求URL
|
||||||
|
reqUrl := fmt.Sprintf("%s/%s/%s?timestamp=%s", w.config.Url, w.config.SecretId, code, timestamp)
|
||||||
|
|
||||||
|
jsonData, marshalErr := json.Marshal(reqData)
|
||||||
|
if marshalErr != nil {
|
||||||
|
return nil, marshalErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建HTTP POST请求
|
||||||
|
req, newRequestErr := http.NewRequest("POST", reqUrl, bytes.NewBuffer(jsonData))
|
||||||
|
if newRequestErr != nil {
|
||||||
|
return nil, newRequestErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
client := &http.Client{}
|
||||||
|
httpResp, clientDoErr := client.Do(req)
|
||||||
|
if clientDoErr != nil {
|
||||||
|
return nil, clientDoErr
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
closeErr := Body.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
}(httpResp.Body)
|
||||||
|
|
||||||
|
// 检查请求是否成功
|
||||||
|
if httpResp.StatusCode == 200 {
|
||||||
|
// 读取响应体
|
||||||
|
bodyBytes, ReadErr := io.ReadAll(httpResp.Body)
|
||||||
|
if ReadErr != nil {
|
||||||
|
return nil, ReadErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法
|
||||||
|
var westDexResp WestResp
|
||||||
|
UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp)
|
||||||
|
if UnmarshalErr != nil {
|
||||||
|
return nil, UnmarshalErr
|
||||||
|
}
|
||||||
|
logx.Infof("西部数据请求响应, code: %s, response: %v", code, westDexResp)
|
||||||
|
if westDexResp.Code != "00000" {
|
||||||
|
if westDexResp.Data == "" {
|
||||||
|
return nil, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key)
|
||||||
|
if DecryptErr != nil {
|
||||||
|
return nil, DecryptErr
|
||||||
|
}
|
||||||
|
return decryptedData, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
if westDexResp.Data == "" {
|
||||||
|
return nil, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
// 解密响应数据
|
||||||
|
decryptedData, DecryptErr := crypto.WestDexDecrypt(westDexResp.Data, w.config.Key)
|
||||||
|
if DecryptErr != nil {
|
||||||
|
return nil, DecryptErr
|
||||||
|
}
|
||||||
|
// 输出解密后的数据
|
||||||
|
return decryptedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallAPI 调用西部数据的 API
|
||||||
|
func (w *WestDexService) G05HZ01CallAPI(code string, reqData map[string]interface{}) (resp []byte, err error) {
|
||||||
|
// 生成当前的13位时间戳
|
||||||
|
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||||
|
|
||||||
|
// 构造请求URL
|
||||||
|
reqUrl := fmt.Sprintf("%s/%s/%s?timestamp=%s", w.config.Url, w.config.SecretSecondId, code, timestamp)
|
||||||
|
|
||||||
|
jsonData, marshalErr := json.Marshal(reqData)
|
||||||
|
if marshalErr != nil {
|
||||||
|
return nil, marshalErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建HTTP POST请求
|
||||||
|
req, newRequestErr := http.NewRequest("POST", reqUrl, bytes.NewBuffer(jsonData))
|
||||||
|
if newRequestErr != nil {
|
||||||
|
return nil, newRequestErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
client := &http.Client{}
|
||||||
|
httpResp, clientDoErr := client.Do(req)
|
||||||
|
if clientDoErr != nil {
|
||||||
|
return nil, clientDoErr
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
closeErr := Body.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
}(httpResp.Body)
|
||||||
|
|
||||||
|
// 检查请求是否成功
|
||||||
|
if httpResp.StatusCode == 200 {
|
||||||
|
// 读取响应体
|
||||||
|
bodyBytes, ReadErr := io.ReadAll(httpResp.Body)
|
||||||
|
if ReadErr != nil {
|
||||||
|
return nil, ReadErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法
|
||||||
|
var westDexResp G05HZ01WestResp
|
||||||
|
UnmarshalErr := json.Unmarshal(bodyBytes, &westDexResp)
|
||||||
|
if UnmarshalErr != nil {
|
||||||
|
return nil, UnmarshalErr
|
||||||
|
}
|
||||||
|
logx.Infof("西部数据请求响应, code: %s, response: %v", code, westDexResp)
|
||||||
|
if westDexResp.Code != "0000" {
|
||||||
|
if westDexResp.Data == nil {
|
||||||
|
return nil, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
return westDexResp.Data, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
if westDexResp.Data == nil {
|
||||||
|
return nil, errors.New(westDexResp.Message)
|
||||||
|
}
|
||||||
|
return westDexResp.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("西部请求失败Code: %d", httpResp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptStructFields 加密字段的函数,处理不同类型,并跳过空值字段
|
||||||
|
func (w *WestDexService) EncryptStructFields(inputStruct interface{}) (map[string]interface{}, error) {
|
||||||
|
encryptedFields := make(map[string]interface{})
|
||||||
|
|
||||||
|
// 使用反射获取结构体的类型和值
|
||||||
|
v := reflect.ValueOf(inputStruct)
|
||||||
|
// 检查并解引用指针类型
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return nil, errors.New("传入的interfact不是struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历结构体字段
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field := v.Type().Field(i)
|
||||||
|
fieldValue := v.Field(i)
|
||||||
|
|
||||||
|
// 检查字段的 encrypt 标签是否为 "false"
|
||||||
|
encryptTag := field.Tag.Get("encrypt")
|
||||||
|
if encryptTag == "false" {
|
||||||
|
encryptedFields[field.Name] = fieldValue.Interface()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字段为空值,跳过
|
||||||
|
if fieldValue.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字段的值转换为字符串进行加密
|
||||||
|
strValue := fmt.Sprintf("%v", fieldValue.Interface())
|
||||||
|
|
||||||
|
// 执行加密操作
|
||||||
|
encryptedValue, err := crypto.WestDexEncrypt(strValue, w.config.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将加密后的值存入结果映射
|
||||||
|
encryptedFields[field.Name] = encryptedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return encryptedFields, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapStructToAPIRequest 字段映射
|
||||||
|
func (w *WestDexService) MapStructToAPIRequest(encryptedFields map[string]interface{}, fieldMapping map[string]string, wrapField string) map[string]interface{} {
|
||||||
|
apiRequest := make(map[string]interface{})
|
||||||
|
|
||||||
|
// 遍历字段映射表
|
||||||
|
for structField, apiField := range fieldMapping {
|
||||||
|
// 如果加密后的字段存在,才添加到请求
|
||||||
|
if structField == "InquiredAuth" {
|
||||||
|
apiRequest[apiField] = GetDateRange()
|
||||||
|
} else if structField == "TimeRange" {
|
||||||
|
apiRequest[apiField] = "5"
|
||||||
|
} else if value, exists := encryptedFields[structField]; exists {
|
||||||
|
apiRequest[apiField] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 wrapField 不为空,将 apiRequest 包裹到该字段下
|
||||||
|
if wrapField != "" {
|
||||||
|
return map[string]interface{}{
|
||||||
|
wrapField: apiRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apiRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRequests 批量处理
|
||||||
|
func (w *WestDexService) ProcessRequests(data interface{}, requests []types.WestDexServiceRequestParams) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
resultsCh = make(chan APIResponseData, len(requests))
|
||||||
|
errorsCh = make(chan error, len(requests))
|
||||||
|
ctx, cancel = context.WithCancel(context.Background())
|
||||||
|
errorCount int32
|
||||||
|
errorLimit = 4
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for i, req := range requests {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int, req types.WestDexServiceRequestParams) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// 请求参数预处理
|
||||||
|
apiRequest, preprocessErr := w.PreprocessRequestParams(req.ApiID, data)
|
||||||
|
if preprocessErr != nil {
|
||||||
|
errorsCh <- fmt.Errorf("请求预处理失败: %v", preprocessErr)
|
||||||
|
atomic.AddInt32(&errorCount, 1)
|
||||||
|
if atomic.LoadInt32(&errorCount) >= int32(errorLimit) {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp []byte
|
||||||
|
var callApiErr error
|
||||||
|
if req.ApiID == "G05HZ01" {
|
||||||
|
resp, callApiErr = w.G05HZ01CallAPI(req.ApiID, apiRequest)
|
||||||
|
} else {
|
||||||
|
resp, callApiErr = w.CallAPI(req.ApiID, apiRequest)
|
||||||
|
}
|
||||||
|
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
|
result := APIResponseData{
|
||||||
|
ApiID: req.ApiID,
|
||||||
|
Success: false,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
if callApiErr != nil {
|
||||||
|
errorsCh <- fmt.Errorf("西部请求, 请求失败: %+v", callApiErr)
|
||||||
|
atomic.AddInt32(&errorCount, 1)
|
||||||
|
if atomic.LoadInt32(&errorCount) >= int32(errorLimit) {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
result.Error = callApiErr.Error()
|
||||||
|
result.Data = resp
|
||||||
|
resultsCh <- result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
processedResp, processErr := processResponse(resp, req.ApiID)
|
||||||
|
if processErr != nil {
|
||||||
|
errorsCh <- fmt.Errorf("处理响应失败: %v", processErr)
|
||||||
|
atomic.AddInt32(&errorCount, 1)
|
||||||
|
if atomic.LoadInt32(&errorCount) >= int32(errorLimit) {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
result.Error = processErr.Error()
|
||||||
|
} else {
|
||||||
|
result.Data = processedResp
|
||||||
|
result.Success = true
|
||||||
|
}
|
||||||
|
resultsCh <- result
|
||||||
|
}(i, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(resultsCh)
|
||||||
|
close(errorsCh)
|
||||||
|
}()
|
||||||
|
// 收集所有结果并合并
|
||||||
|
var responseData []APIResponseData
|
||||||
|
for result := range resultsCh {
|
||||||
|
responseData = append(responseData, result)
|
||||||
|
}
|
||||||
|
if atomic.LoadInt32(&errorCount) >= int32(errorLimit) {
|
||||||
|
var allErrors []error
|
||||||
|
for err := range errorsCh {
|
||||||
|
allErrors = append(allErrors, err)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("请求失败次数超过 %d 次: %v", errorLimit, allErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedResponse, err := json.Marshal(responseData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("响应数据转 JSON 失败: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------请求处理器--------------------------
|
||||||
|
var requestProcessors = map[string]func(*WestDexService, interface{}) (map[string]interface{}, error){
|
||||||
|
"G09SC02": (*WestDexService).ProcessG09SC02Request,
|
||||||
|
"G27BJ05": (*WestDexService).ProcessG27BJ05Request,
|
||||||
|
"G26BJ05": (*WestDexService).ProcessG26BJ05Request,
|
||||||
|
"G34BJ03": (*WestDexService).ProcessG34BJ03Request,
|
||||||
|
"G35SC01": (*WestDexService).ProcessG35SC01Request,
|
||||||
|
"G28BJ05": (*WestDexService).ProcessG28BJ05Request,
|
||||||
|
"G05HZ01": (*WestDexService).ProcessG05HZ01Request,
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreprocessRequestParams 调用指定的请求处理函数
|
||||||
|
func (w *WestDexService) PreprocessRequestParams(apiID string, params interface{}) (map[string]interface{}, error) {
|
||||||
|
if processor, exists := requestProcessors[apiID]; exists {
|
||||||
|
return processor(w, params) // 调用 WestDexService 方法
|
||||||
|
}
|
||||||
|
|
||||||
|
var request map[string]interface{}
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// / 将处理函数作为 WestDexService 的方法
|
||||||
|
func (w *WestDexService) ProcessG09SC02Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G09SC02FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG27BJ05Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G27BJ05FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG26BJ05Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
// 特殊名单 G26BJ05
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G26BJ05FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG34BJ03Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
// 个人不良 G34BJ03
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G34BJ03FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG35SC01Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
// 个人涉诉 G35SC01
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G35SC01FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG28BJ05Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
// 借贷行为 G28BJ05
|
||||||
|
encryptedFields, err := w.EncryptStructFields(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("西部请求, 生成请求数据失败: %+v", err)
|
||||||
|
}
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G28BJ05FieldMapping, "data")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WestDexService) ProcessG05HZ01Request(params interface{}) (map[string]interface{}, error) {
|
||||||
|
// 使用 reflect 获取 params 的值和类型
|
||||||
|
val := reflect.ValueOf(params)
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem() // 如果是指针,获取指向的实际值
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("请求参数必须是结构体类型")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化一个 map 来存储加密后的字段
|
||||||
|
encryptedFields := make(map[string]interface{})
|
||||||
|
|
||||||
|
// 遍历结构体字段,将其转换为 map[string]interface{}
|
||||||
|
valType := val.Type()
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
field := val.Field(i)
|
||||||
|
fieldName := valType.Field(i).Name
|
||||||
|
|
||||||
|
// 如果字段名为 "IDCard",对其值进行加密
|
||||||
|
if fieldName == "IDCard" {
|
||||||
|
if field.Kind() != reflect.String {
|
||||||
|
return nil, fmt.Errorf("IDCard 字段不是字符串类型")
|
||||||
|
}
|
||||||
|
idCard := field.String()
|
||||||
|
encryptedIDCard := crypto.Md5Encrypt(idCard)
|
||||||
|
encryptedFields[fieldName] = encryptedIDCard
|
||||||
|
} else {
|
||||||
|
// 否则直接将字段值添加到 map 中
|
||||||
|
encryptedFields[fieldName] = field.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用字段映射表生成最终的 API 请求
|
||||||
|
apiRequest := w.MapStructToAPIRequest(encryptedFields, types.G05HZ01FieldMapping, "")
|
||||||
|
return apiRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// 响应处理器
|
||||||
|
var responseProcessors = map[string]func([]byte) ([]byte, error){
|
||||||
|
"G09SC02": processG09SC02Response, // 单人婚姻
|
||||||
|
"G27BJ05": processG27BJ05Response, // 借贷意向
|
||||||
|
"G28BJ05": processG28BJ05Response, // 借贷行为
|
||||||
|
"G26BJ05": processG26BJ05Response, // 特殊名单
|
||||||
|
"G05HZ01": processG05HZ01Response, // 股东人企关系
|
||||||
|
"G34BJ03": processG34BJ03Response, // 个人不良
|
||||||
|
"G35SC01": processG35SC01Response, // 个人涉诉
|
||||||
|
}
|
||||||
|
|
||||||
|
// processResponse 处理响应数据
|
||||||
|
func processResponse(resp []byte, apiID string) ([]byte, error) {
|
||||||
|
if processor, exists := responseProcessors[apiID]; exists {
|
||||||
|
return processor(resp)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG09SC02Response(resp []byte) ([]byte, error) {
|
||||||
|
// 使用 GJSON 递归搜索 "maritalStatus" 字段
|
||||||
|
result := gjson.GetBytes(resp, "data.0.maritalStatus")
|
||||||
|
|
||||||
|
if result.Exists() {
|
||||||
|
// 如果字段存在,构造包含 "status" 的 JSON 响应
|
||||||
|
responseMap := map[string]string{"status": result.String()}
|
||||||
|
jsonResponse, err := json.Marshal(responseMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jsonResponse, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("查询为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG27BJ05Response(resp []byte) ([]byte, error) {
|
||||||
|
// 获取 code 字段
|
||||||
|
codeResult := gjson.GetBytes(resp, "code")
|
||||||
|
if !codeResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("code 字段不存在")
|
||||||
|
}
|
||||||
|
if codeResult.String() != "00" {
|
||||||
|
return nil, fmt.Errorf("未匹配到相关结果")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 data 字段
|
||||||
|
dataResult := gjson.GetBytes(resp, "data")
|
||||||
|
if !dataResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("data 字段不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 data 字段解析为 map
|
||||||
|
var dataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析 data 字段失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定字段
|
||||||
|
delete(dataMap, "swift_number")
|
||||||
|
delete(dataMap, "DataStrategy")
|
||||||
|
|
||||||
|
// 重新编码为 JSON
|
||||||
|
modifiedData, err := json.Marshal(dataMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("编码修改后的 data 失败: %v", err)
|
||||||
|
}
|
||||||
|
return modifiedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG28BJ05Response(resp []byte) ([]byte, error) {
|
||||||
|
// 处理借贷行为的响应数据
|
||||||
|
// 获取 code 字段
|
||||||
|
codeResult := gjson.GetBytes(resp, "code")
|
||||||
|
if !codeResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("code 字段不存在")
|
||||||
|
}
|
||||||
|
if codeResult.String() != "00" {
|
||||||
|
return nil, fmt.Errorf("未匹配到相关结果")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 data 字段
|
||||||
|
dataResult := gjson.GetBytes(resp, "data")
|
||||||
|
if !dataResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("data 字段不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 data 字段解析为 map
|
||||||
|
var dataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析 data 字段失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定字段
|
||||||
|
delete(dataMap, "swift_number")
|
||||||
|
delete(dataMap, "DataStrategy")
|
||||||
|
|
||||||
|
// 重新编码为 JSON
|
||||||
|
modifiedData, err := json.Marshal(dataMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("编码修改后的 data 失败: %v", err)
|
||||||
|
}
|
||||||
|
return modifiedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG26BJ05Response(resp []byte) ([]byte, error) {
|
||||||
|
// 处理特殊名单的响应数据
|
||||||
|
// 获取 code 字段
|
||||||
|
codeResult := gjson.GetBytes(resp, "code")
|
||||||
|
if !codeResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("code 字段不存在")
|
||||||
|
}
|
||||||
|
if codeResult.String() != "00" {
|
||||||
|
return nil, fmt.Errorf("未匹配到相关结果")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 data 字段
|
||||||
|
dataResult := gjson.GetBytes(resp, "data")
|
||||||
|
if !dataResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("data 字段不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 data 字段解析为 map
|
||||||
|
var dataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(dataResult.Raw), &dataMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析 data 字段失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定字段
|
||||||
|
delete(dataMap, "swift_number")
|
||||||
|
delete(dataMap, "DataStrategy")
|
||||||
|
|
||||||
|
// 重新编码为 JSON
|
||||||
|
modifiedData, err := json.Marshal(dataMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("编码修改后的 data 失败: %v", err)
|
||||||
|
}
|
||||||
|
return modifiedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG05HZ01Response(resp []byte) ([]byte, error) {
|
||||||
|
// 处理股东人企关系的响应数据
|
||||||
|
code := gjson.GetBytes(resp, "code")
|
||||||
|
if !code.Exists() {
|
||||||
|
return nil, fmt.Errorf("响应中缺少 code 字段")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断 code 是否等于 "0000"
|
||||||
|
if code.String() == "0000" {
|
||||||
|
// 获取 data 字段的值
|
||||||
|
data := gjson.GetBytes(resp, "data")
|
||||||
|
if !data.Exists() {
|
||||||
|
return nil, fmt.Errorf("响应中缺少 data 字段")
|
||||||
|
}
|
||||||
|
// 返回 data 字段的内容
|
||||||
|
return []byte(data.Raw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// code 不等于 "0000",返回错误
|
||||||
|
return nil, fmt.Errorf("响应code错误%s", code.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG34BJ03Response(resp []byte) ([]byte, error) {
|
||||||
|
// 处理个人不良的响应数据
|
||||||
|
dataResult := gjson.GetBytes(resp, "negative_info.data.risk_level")
|
||||||
|
if dataResult.Exists() {
|
||||||
|
// 如果字段存在,构造包含 "status" 的 JSON 响应
|
||||||
|
responseMap := map[string]string{"risk_level": dataResult.String()}
|
||||||
|
jsonResponse, err := json.Marshal(responseMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jsonResponse, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("查询为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processG35SC01Response(resp []byte) ([]byte, error) {
|
||||||
|
// 第一步:提取外层的 data 字段
|
||||||
|
dataResult := gjson.GetBytes(resp, "data")
|
||||||
|
if !dataResult.Exists() {
|
||||||
|
return nil, fmt.Errorf("外层 data 字段不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二步:解析外层 data 的 JSON 字符串
|
||||||
|
var outerDataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(dataResult.String()), &outerDataMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析外层 data 字段失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三步:提取内层的 data 字段
|
||||||
|
innerData, ok := outerDataMap["data"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("内层 data 字段不存在或类型错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第四步:解析内层 data 的 JSON 字符串
|
||||||
|
var finalDataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(innerData), &finalDataMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析内层 data 字段失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将最终的 JSON 对象编码为字节数组返回
|
||||||
|
finalDataBytes, err := json.Marshal(finalDataMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("编码最终的 JSON 对象失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalDataBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDateRange 返回今天到明天的日期范围,格式为 "yyyyMMdd-yyyyMMdd"
|
||||||
|
func GetDateRange() string {
|
||||||
|
today := time.Now().Format("20060102") // 获取今天的日期
|
||||||
|
tomorrow := time.Now().Add(24 * time.Hour).Format("20060102") // 获取明天的日期
|
||||||
|
return fmt.Sprintf("%s-%s", today, tomorrow) // 拼接日期范围并返回
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
package svc
|
package svc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hibiken/asynq"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
"qnc-server/app/user/cmd/api/internal/config"
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/service"
|
||||||
"qnc-server/app/user/model"
|
"qnc-server/app/user/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,19 +15,56 @@ type ServiceContext struct {
|
|||||||
Redis *redis.Redis
|
Redis *redis.Redis
|
||||||
UserModel model.UserModel
|
UserModel model.UserModel
|
||||||
UserAuthModel model.UserAuthModel
|
UserAuthModel model.UserAuthModel
|
||||||
|
ProductModel model.ProductModel
|
||||||
|
FeatureModel model.FeatureModel
|
||||||
|
ProductFeatureModel model.ProductFeatureModel
|
||||||
|
OrderModel model.OrderModel
|
||||||
|
QueryModel model.QueryModel
|
||||||
|
AlipayService *service.AliPayService
|
||||||
|
WechatPayService *service.WechatPayService
|
||||||
|
WestDexService *service.WestDexService
|
||||||
|
AsynqServer *asynq.Server // 服务端
|
||||||
|
AsynqService *service.AsynqService // 客户端
|
||||||
|
VerificationService *service.VerificationService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
db := sqlx.NewMysql(c.DataSource) // 创建数据库连接
|
db := sqlx.NewMysql(c.DataSource)
|
||||||
redisConf := redis.RedisConf{
|
redisConf := redis.RedisConf{
|
||||||
Host: c.CacheRedis[0].Host,
|
Host: c.CacheRedis[0].Host,
|
||||||
Pass: c.CacheRedis[0].Pass,
|
Pass: c.CacheRedis[0].Pass,
|
||||||
Type: c.CacheRedis[0].Type, // Redis 节点类型,如 "node"
|
Type: c.CacheRedis[0].Type,
|
||||||
}
|
}
|
||||||
|
asynqServer := asynq.NewServer(
|
||||||
|
asynq.RedisClientOpt{Addr: c.CacheRedis[0].Host, Password: c.CacheRedis[0].Pass},
|
||||||
|
asynq.Config{
|
||||||
|
IsFailure: func(err error) bool {
|
||||||
|
logx.Errorf("异步任务失败: %+v \n", err)
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
Concurrency: 10,
|
||||||
|
},
|
||||||
|
)
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
Redis: redis.MustNewRedis(redisConf),
|
Redis: redis.MustNewRedis(redisConf),
|
||||||
|
AlipayService: service.NewAliPayService(c),
|
||||||
|
WechatPayService: service.NewWechatPayService(c),
|
||||||
|
WestDexService: service.NewWestDexService(c),
|
||||||
|
VerificationService: service.NewVerificationService(c),
|
||||||
|
AsynqServer: asynqServer,
|
||||||
|
AsynqService: service.NewAsynqService(c),
|
||||||
UserModel: model.NewUserModel(db, c.CacheRedis),
|
UserModel: model.NewUserModel(db, c.CacheRedis),
|
||||||
UserAuthModel: model.NewUserAuthModel(db, c.CacheRedis),
|
UserAuthModel: model.NewUserAuthModel(db, c.CacheRedis),
|
||||||
|
ProductModel: model.NewProductModel(db, c.CacheRedis),
|
||||||
|
OrderModel: model.NewOrderModel(db, c.CacheRedis),
|
||||||
|
QueryModel: model.NewQueryModel(db, c.CacheRedis),
|
||||||
|
FeatureModel: model.NewFeatureModel(db, c.CacheRedis),
|
||||||
|
ProductFeatureModel: model.NewProductFeatureModel(db, c.CacheRedis),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *ServiceContext) Close() {
|
||||||
|
if s.AsynqService != nil {
|
||||||
|
s.AsynqService.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
app/user/cmd/api/internal/types/payload.go
Normal file
5
app/user/cmd/api/internal/types/payload.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type MsgPaySuccessQueryPayload struct {
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
}
|
61
app/user/cmd/api/internal/types/query.go
Normal file
61
app/user/cmd/api/internal/types/query.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type MarriageReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
type HomeServiceReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RiskAssessment 查询请求结构
|
||||||
|
type RiskAssessmentReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompanyInfo 查询请求结构
|
||||||
|
type CompanyInfoReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RentalInfo 查询请求结构
|
||||||
|
type RentalInfoReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreLoanBackgroundCheck 查询请求结构
|
||||||
|
type PreLoanBackgroundCheckReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackgroundCheck 查询请求结构
|
||||||
|
type BackgroundCheckReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
PayMethod string `json:"pay_method" validate:"required,payMethod"`
|
||||||
|
}
|
47
app/user/cmd/api/internal/types/queryMap.go
Normal file
47
app/user/cmd/api/internal/types/queryMap.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// 特殊名单 G26BJ05
|
||||||
|
var G26BJ05FieldMapping = map[string]string{
|
||||||
|
"IDCard": "id",
|
||||||
|
"Name": "name",
|
||||||
|
"Mobile": "cell",
|
||||||
|
"TimeRange": "time_range",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 个人不良
|
||||||
|
var G34BJ03FieldMapping = map[string]string{
|
||||||
|
"IDCard": "id_card",
|
||||||
|
"Name": "name",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 个人涉诉 G35SC01
|
||||||
|
var G35SC01FieldMapping = map[string]string{
|
||||||
|
"Name": "name",
|
||||||
|
"IDCard": "idcard",
|
||||||
|
"InquiredAuth": "inquired_auth",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单人婚姻 G09SC02
|
||||||
|
var G09SC02FieldMapping = map[string]string{
|
||||||
|
"IDCard": "certNumMan",
|
||||||
|
"Name": "nameMan",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 借贷意向 G27BJ05
|
||||||
|
var G27BJ05FieldMapping = map[string]string{
|
||||||
|
"IDCard": "id",
|
||||||
|
"Name": "name",
|
||||||
|
"Mobile": "cell",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 借贷行为 G28BJ05
|
||||||
|
var G28BJ05FieldMapping = map[string]string{
|
||||||
|
"IDCard": "id",
|
||||||
|
"Name": "name",
|
||||||
|
"Mobile": "cell",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 股东人企关系精准版 G05HZ01
|
||||||
|
var G05HZ01FieldMapping = map[string]string{
|
||||||
|
"IDCard": "pid",
|
||||||
|
}
|
73
app/user/cmd/api/internal/types/queryParams.go
Normal file
73
app/user/cmd/api/internal/types/queryParams.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type WestDexServiceRequestParams struct {
|
||||||
|
FieldMapping map[string]string
|
||||||
|
ApiID string
|
||||||
|
}
|
||||||
|
|
||||||
|
var WestDexParams = map[string][]WestDexServiceRequestParams{
|
||||||
|
"marriage": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
|
||||||
|
},
|
||||||
|
"backgroundcheck": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
"companyinfo": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
"homeservice": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
"preloanbackgroundcheck": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
"rentalinfo": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
"riskassessment": {
|
||||||
|
{FieldMapping: G09SC02FieldMapping, ApiID: "G09SC02"}, // 单人婚姻
|
||||||
|
{FieldMapping: G27BJ05FieldMapping, ApiID: "G27BJ05"}, // 借贷意向
|
||||||
|
{FieldMapping: G28BJ05FieldMapping, ApiID: "G28BJ05"}, // 借贷行为
|
||||||
|
{FieldMapping: G26BJ05FieldMapping, ApiID: "G26BJ05"}, // 特殊名单
|
||||||
|
{FieldMapping: G05HZ01FieldMapping, ApiID: "G05HZ01"}, // 股东人企关系
|
||||||
|
{FieldMapping: G34BJ03FieldMapping, ApiID: "G34BJ03"}, // 个人不良
|
||||||
|
{FieldMapping: G35SC01FieldMapping, ApiID: "G35SC01"}, // 个人涉诉
|
||||||
|
},
|
||||||
|
}
|
3
app/user/cmd/api/internal/types/taskname.go
Normal file
3
app/user/cmd/api/internal/types/taskname.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
const MsgPaySuccessQuery = "msg:pay_success:query"
|
@ -1,6 +1,20 @@
|
|||||||
// Code generated by goctl. DO NOT EDIT.
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
type Feature struct {
|
||||||
|
ID int64 `json:"id"` // 功能ID
|
||||||
|
ApiID string `json:"api_id"` // API标识
|
||||||
|
Name string `json:"name"` // 功能描述
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductByEnRequest struct {
|
||||||
|
ProductEn string `path:"product_en"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductByIDRequest struct {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
type MobileCodeLoginReq struct {
|
type MobileCodeLoginReq struct {
|
||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
Code string `json:"code" validate:"required"`
|
Code string `json:"code" validate:"required"`
|
||||||
@ -23,6 +37,80 @@ type MobileLoginResp struct {
|
|||||||
RefreshAfter int64 `json:"refreshAfter"`
|
RefreshAfter int64 `json:"refreshAfter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
ProductName string `json:"product_name"`
|
||||||
|
ProductEn string `json:"product_en"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Notes string `json:"notes,optional"`
|
||||||
|
SellPrice float64 `json:"sell_price"`
|
||||||
|
Features []Feature `json:"features"` // 关联功能列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductResponse struct {
|
||||||
|
Product
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
Id int64 `json:"id"` // 主键ID
|
||||||
|
OrderId int64 `json:"order_id"` // 订单ID
|
||||||
|
UserId int64 `json:"user_id"` // 用户ID
|
||||||
|
ProductId int64 `json:"product_id"` // 产品ID
|
||||||
|
QueryData []map[string]interface{} `json:"query_data"`
|
||||||
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
|
QueryState string `json:"query_state"` // 查询状态
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDetailByOrderIdReq struct {
|
||||||
|
OrderId int64 `path:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDetailByOrderIdResp struct {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDetailReq struct {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryDetailResp struct {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryExampleReq struct {
|
||||||
|
Feature string `form:"feature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryExampleResp struct {
|
||||||
|
Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryListReq struct {
|
||||||
|
Page int64 `form:"page"` // 页码
|
||||||
|
PageSize int64 `form:"page_size"` // 每页数据量
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryListResp struct {
|
||||||
|
Total int64 `json:"total"` // 总记录数
|
||||||
|
List []Query `json:"list"` // 查询列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryReq struct {
|
||||||
|
Data string `json:"data" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResp struct {
|
||||||
|
PrepayID string `json:"prepay_id"`
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryRetryReq struct {
|
||||||
|
Id int64 `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryRetryResp struct {
|
||||||
|
}
|
||||||
|
|
||||||
type RegisterReq struct {
|
type RegisterReq struct {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
Password string `json:"password" validate:"required,min=11,max=11,password"`
|
||||||
@ -41,9 +129,6 @@ type User struct {
|
|||||||
NickName string `json:"nickName"`
|
NickName string `json:"nickName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfoReq struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserInfoResp struct {
|
type UserInfoResp struct {
|
||||||
UserInfo User `json:"userInfo"`
|
UserInfo User `json:"userInfo"`
|
||||||
}
|
}
|
||||||
@ -62,5 +147,5 @@ type WXMiniAuthResp struct {
|
|||||||
|
|
||||||
type SendSmsReq struct {
|
type SendSmsReq struct {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
ActionType string `json:"actionType" validate:"required,oneof=loginCode registerCode QueryCode"`
|
ActionType string `json:"actionType" validate:"required,oneof=login register query"`
|
||||||
}
|
}
|
||||||
|
28
app/user/cmd/api/merchant/apiclient_key.pem
Normal file
28
app/user/cmd/api/merchant/apiclient_key.pem
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCP6fWm1vXXybH
|
||||||
|
m3Ne6PjacGrN2+iMrzWZlzdHCZ31udDPqSUYaZ+78b441KZK/CJFQWeSJ/1h//A+
|
||||||
|
BGsQDKvE/fj2QzN1KkOuQ8WJXNGpixE5uu5bv/QTN/ukurGdA1aO2aFCANumlOmB
|
||||||
|
HkB/B2so57ii8iQQjwK2xM4r3oOU/IfcFGKL+9/QjLGFFp9PJXCDBCgrxxlZGaj1
|
||||||
|
3wowlfVOzlaX94gemQsCYVkuAFIYMAnFHs9cKNZQIU80somW/yy2Gy38N6n7NnbD
|
||||||
|
nvFSaq4GoDROqRgKbRZ5e706d/p7A3aS/2oRqq1jomUIugK8g++LmoHFTgfhfQkI
|
||||||
|
v1aG/nPzAgMBAAECggEAD2RN31J2J42xm/V0YdviBCUOQXugZK1peN8jkSxw6Myt
|
||||||
|
gBbuCo4sCw9vvD8VYjGyYXx6QXmLuV03YyKkfSQT5EsflBvlEu6jaEaUe3rwXhfX
|
||||||
|
6JQoWPrP00oHVZk5g7CFBlK2VW2N+hgonIOSJr6mvhoGZlr7gphiZasYjx9Vm9N3
|
||||||
|
Pbnfru5ttzplYNniwH3DF6ph8VmdbD1nnbWSKLXvHCsXQT2wBcnsIagIH3vyq6K1
|
||||||
|
pc5abWsQJrixOPebpI8jD5w0HxHAqVLx58H/OC2zW/roAw1WS2AkueJ1j7dQ7Z0C
|
||||||
|
mc9Xexz5gcAP0nMAQv+LP7iYqsa/niFhfcTFWfdxkQKBgQD5JkKNmInU2/IVYCwO
|
||||||
|
c483MCSv1+MnbRXlb7vut8T0IupHTU6hCge6C3q3HsjbKSBn8bRChtPUzvw9JFxK
|
||||||
|
QWKiQqQDPLDJ08AIKhfQD2JiLtoikkZN0bF6OTL+Soney1yGx51mlfHM194+PcCJ
|
||||||
|
jF7iWdMVbcBwHbgydNxxIS5cKQKBgQDHlvQ4lw6gvLILpGK494/vNYSJP/Jmd66V
|
||||||
|
3oSGYi84YRKTSwH4NlbBVVieb3Dv+pPugbsXEuFHBif7WsivbYgNTE9++8Yvt0gh
|
||||||
|
duB1G4yh7m/ylQeSuipgQU9tozrU/15cWwmcCRV50wWXBGoVEM0kf7mzEKSxmjYk
|
||||||
|
Qzko/zxSuwKBgQCY6Bc+SViFz3qSDdTcBaXma+CIHsmlH7ipd9px1kzEvEzl95cD
|
||||||
|
FGHLl1H34qfIgUQHJvrHPXHyEBoT+CW/2MMM7DM2XV/ubctT92ln4pkxwqlTQExv
|
||||||
|
Y/s1FLesAtj8Z/hgK0/5bprYab9WmZV5lTGCXzhB1XqeFE9AgCHuODv4iQKBgQC8
|
||||||
|
g2uwd5ytXQydymokYk9klJvWNrvw5GHV1BJAC0Smb6lnzZTSqCBRAxdsrb1yLK7E
|
||||||
|
u2vGY2K7/qiM1DZw23eBd+4t9gg+0VIjqXBfq+GsoNTDvtckUwnrWER5PY831ut9
|
||||||
|
N89fvYS3SAUjmlvIAdKBAtKWusWTqiAxJ/05J7oGOQKBgB5PSr5i0LlupIbKui9t
|
||||||
|
XtXnRqGPxxrZZUpTkyrGOAnlCz/zq2QiwFpBWo/NMHOp0KmxzJpQ8yEY2LWlRZ61
|
||||||
|
Oc9m0J/HtPw3Ohi1treBosEVG/0NOI9Tq1Obny23N51MVibdW6zEIyGUp/DbFS8h
|
||||||
|
5DljdOYX9IYIHHn3Ig4GeTGe
|
||||||
|
-----END PRIVATE KEY-----
|
@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"qnc-server/app/user/cmd/api/internal/config"
|
|
||||||
"qnc-server/app/user/cmd/api/internal/handler"
|
|
||||||
"qnc-server/app/user/cmd/api/internal/svc"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/conf"
|
|
||||||
"github.com/zeromicro/go-zero/rest"
|
|
||||||
)
|
|
||||||
|
|
||||||
var configFile = flag.String("f", "etc/user.yaml", "the config file")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
var c config.Config
|
|
||||||
conf.MustLoad(*configFile, &c)
|
|
||||||
|
|
||||||
ctx := svc.NewServiceContext(c)
|
|
||||||
server := rest.MustNewServer(c.RestConf)
|
|
||||||
|
|
||||||
defer server.Stop()
|
|
||||||
|
|
||||||
handler.RegisterHandlers(server, ctx)
|
|
||||||
|
|
||||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
|
||||||
server.Start()
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ UserAuthModel = (*customUserAuthModel)(nil)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// UserAuthModel is an interface to be customized, add more methods here,
|
|
||||||
// and implement the added methods in customUserAuthModel.
|
|
||||||
UserAuthModel interface {
|
|
||||||
userAuthModel
|
|
||||||
}
|
|
||||||
|
|
||||||
customUserAuthModel struct {
|
|
||||||
*defaultUserAuthModel
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewUserAuthModel returns a model for the database table.
|
|
||||||
func NewUserAuthModel(conn sqlx.SqlConn, c cache.CacheConf) UserAuthModel {
|
|
||||||
return &customUserAuthModel{
|
|
||||||
defaultUserAuthModel: newUserAuthModel(conn, c),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,435 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
userAuthFieldNames = builder.RawFieldNames(&UserAuth{})
|
|
||||||
userAuthRows = strings.Join(userAuthFieldNames, ",")
|
|
||||||
userAuthRowsExpectAutoSet = strings.Join(stringx.Remove(userAuthFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
|
|
||||||
userAuthRowsWithPlaceHolder = strings.Join(stringx.Remove(userAuthFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
|
||||||
|
|
||||||
cacheUserAuthIdPrefix = "cache:userAuth:id:"
|
|
||||||
cacheUserAuthAuthTypeAuthKeyPrefix = "cache:userAuth:authType:authKey:"
|
|
||||||
cacheUserAuthUserIdAuthTypePrefix = "cache:userAuth:userId:authType:"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
userAuthModel interface {
|
|
||||||
Insert(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error)
|
|
||||||
FindOne(ctx context.Context, id int64) (*UserAuth, error)
|
|
||||||
FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserAuth, error)
|
|
||||||
FindOneByUserIdAuthType(ctx context.Context, userId int64, authType string) (*UserAuth, error)
|
|
||||||
Update(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error)
|
|
||||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *UserAuth) 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 *UserAuth) 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) ([]*UserAuth, error)
|
|
||||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, error)
|
|
||||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, int64, error)
|
|
||||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserAuth, error)
|
|
||||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserAuth, error)
|
|
||||||
Delete(ctx context.Context, session sqlx.Session, id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultUserAuthModel struct {
|
|
||||||
sqlc.CachedConn
|
|
||||||
table string
|
|
||||||
}
|
|
||||||
|
|
||||||
UserAuth struct {
|
|
||||||
Id int64 `db:"id"`
|
|
||||||
CreateTime time.Time `db:"create_time"`
|
|
||||||
UpdateTime time.Time `db:"update_time"`
|
|
||||||
DeleteTime time.Time `db:"delete_time"`
|
|
||||||
DelState int64 `db:"del_state"`
|
|
||||||
Version int64 `db:"version"` // 版本号
|
|
||||||
UserId int64 `db:"user_id"`
|
|
||||||
AuthKey string `db:"auth_key"` // 平台唯一id
|
|
||||||
AuthType string `db:"auth_type"` // 平台类型
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newUserAuthModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserAuthModel {
|
|
||||||
return &defaultUserAuthModel{
|
|
||||||
CachedConn: sqlc.NewConn(conn, c),
|
|
||||||
table: "`user_auth`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) Insert(ctx context.Context, session sqlx.Session, data *UserAuth) (sql.Result, error) {
|
|
||||||
data.DeleteTime = time.Unix(0, 0)
|
|
||||||
data.DelState = globalkey.DelStateNo
|
|
||||||
userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
|
||||||
userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id)
|
|
||||||
userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType)
|
|
||||||
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, userAuthRowsExpectAutoSet)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.AuthKey, data.AuthType)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.UserId, data.AuthKey, data.AuthType)
|
|
||||||
}, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) FindOne(ctx context.Context, id int64) (*UserAuth, error) {
|
|
||||||
userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, id)
|
|
||||||
var resp UserAuth
|
|
||||||
err := m.QueryRowCtx(ctx, &resp, userAuthIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userAuthRows, 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 *defaultUserAuthModel) FindOneByAuthTypeAuthKey(ctx context.Context, authType string, authKey string) (*UserAuth, error) {
|
|
||||||
userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, authType, authKey)
|
|
||||||
var resp UserAuth
|
|
||||||
err := m.QueryRowIndexCtx(ctx, &resp, userAuthAuthTypeAuthKeyKey, 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", userAuthRows, 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 *defaultUserAuthModel) FindOneByUserIdAuthType(ctx context.Context, userId int64, authType string) (*UserAuth, error) {
|
|
||||||
userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, userId, authType)
|
|
||||||
var resp UserAuth
|
|
||||||
err := m.QueryRowIndexCtx(ctx, &resp, userAuthUserIdAuthTypeKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `user_id` = ? and `auth_type` = ? and del_state = ? limit 1", userAuthRows, m.table)
|
|
||||||
if err := conn.QueryRowCtx(ctx, &resp, query, userId, authType, 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 *defaultUserAuthModel) Update(ctx context.Context, session sqlx.Session, newData *UserAuth) (sql.Result, error) {
|
|
||||||
data, err := m.FindOne(ctx, newData.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
|
||||||
userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id)
|
|
||||||
userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType)
|
|
||||||
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, userAuthRowsWithPlaceHolder)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id)
|
|
||||||
}, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *UserAuth) 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
|
|
||||||
}
|
|
||||||
userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
|
||||||
userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, data.Id)
|
|
||||||
userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType)
|
|
||||||
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, userAuthRowsWithPlaceHolder)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id, oldVersion)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.UserId, newData.AuthKey, newData.AuthType, newData.Id, oldVersion)
|
|
||||||
}, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
updateCount, err := sqlResult.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if updateCount == 0 {
|
|
||||||
return ErrNoRowsUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *UserAuth) error {
|
|
||||||
data.DelState = globalkey.DelStateYes
|
|
||||||
data.DeleteTime = time.Now()
|
|
||||||
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
|
|
||||||
return errors.Wrapf(errors.New("delete soft failed "), "UserAuthModel delete err : %+v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) 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 *defaultUserAuthModel) 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 *defaultUserAuthModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*UserAuth, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userAuthRows)
|
|
||||||
|
|
||||||
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 []*UserAuth
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userAuthRows)
|
|
||||||
|
|
||||||
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 []*UserAuth
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*UserAuth, int64, error) {
|
|
||||||
|
|
||||||
total, err := m.FindCount(ctx, builder, "id")
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = builder.Columns(userAuthRows)
|
|
||||||
|
|
||||||
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 []*UserAuth
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, total, nil
|
|
||||||
default:
|
|
||||||
return nil, total, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*UserAuth, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userAuthRows)
|
|
||||||
|
|
||||||
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 []*UserAuth
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*UserAuth, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userAuthRows)
|
|
||||||
|
|
||||||
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 []*UserAuth
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) 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 *defaultUserAuthModel) SelectBuilder() squirrel.SelectBuilder {
|
|
||||||
return squirrel.Select().From(m.table)
|
|
||||||
}
|
|
||||||
func (m *defaultUserAuthModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
|
|
||||||
data, err := m.FindOne(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userAuthAuthTypeAuthKeyKey := fmt.Sprintf("%s%v:%v", cacheUserAuthAuthTypeAuthKeyPrefix, data.AuthType, data.AuthKey)
|
|
||||||
userAuthIdKey := fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, id)
|
|
||||||
userAuthUserIdAuthTypeKey := fmt.Sprintf("%s%v:%v", cacheUserAuthUserIdAuthTypePrefix, data.UserId, data.AuthType)
|
|
||||||
_, 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)
|
|
||||||
}, userAuthAuthTypeAuthKeyKey, userAuthIdKey, userAuthUserIdAuthTypeKey)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
func (m *defaultUserAuthModel) formatPrimary(primary interface{}) string {
|
|
||||||
return fmt.Sprintf("%s%v", cacheUserAuthIdPrefix, primary)
|
|
||||||
}
|
|
||||||
func (m *defaultUserAuthModel) 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", userAuthRows, m.table)
|
|
||||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserAuthModel) tableName() string {
|
|
||||||
return m.table
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/cache"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ UserModel = (*customUserModel)(nil)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// UserModel is an interface to be customized, add more methods here,
|
|
||||||
// and implement the added methods in customUserModel.
|
|
||||||
UserModel interface {
|
|
||||||
userModel
|
|
||||||
}
|
|
||||||
|
|
||||||
customUserModel struct {
|
|
||||||
*defaultUserModel
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewUserModel returns a model for the database table.
|
|
||||||
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
|
|
||||||
return &customUserModel{
|
|
||||||
defaultUserModel: newUserModel(conn, c),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,410 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
userFieldNames = builder.RawFieldNames(&User{})
|
|
||||||
userRows = strings.Join(userFieldNames, ",")
|
|
||||||
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
|
|
||||||
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
|
|
||||||
|
|
||||||
cacheUserIdPrefix = "cache:user:id:"
|
|
||||||
cacheUserMobilePrefix = "cache:user:mobile:"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
userModel interface {
|
|
||||||
Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error)
|
|
||||||
FindOne(ctx context.Context, id int64) (*User, error)
|
|
||||||
FindOneByMobile(ctx context.Context, mobile string) (*User, error)
|
|
||||||
Update(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error)
|
|
||||||
UpdateWithVersion(ctx context.Context, session sqlx.Session, data *User) 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 *User) 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) ([]*User, error)
|
|
||||||
FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, error)
|
|
||||||
FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, int64, error)
|
|
||||||
FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*User, error)
|
|
||||||
FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*User, error)
|
|
||||||
Delete(ctx context.Context, session sqlx.Session, id int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultUserModel struct {
|
|
||||||
sqlc.CachedConn
|
|
||||||
table string
|
|
||||||
}
|
|
||||||
|
|
||||||
User struct {
|
|
||||||
Id int64 `db:"id"`
|
|
||||||
CreateTime time.Time `db:"create_time"`
|
|
||||||
UpdateTime time.Time `db:"update_time"`
|
|
||||||
DeleteTime time.Time `db:"delete_time"`
|
|
||||||
DelState int64 `db:"del_state"`
|
|
||||||
Version int64 `db:"version"` // 版本号
|
|
||||||
Mobile string `db:"mobile"`
|
|
||||||
Password string `db:"password"`
|
|
||||||
Nickname string `db:"nickname"`
|
|
||||||
Info string `db:"info"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel {
|
|
||||||
return &defaultUserModel{
|
|
||||||
CachedConn: sqlc.NewConn(conn, c),
|
|
||||||
table: "`user`",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) Insert(ctx context.Context, session sqlx.Session, data *User) (sql.Result, error) {
|
|
||||||
data.DeleteTime = time.Unix(0, 0)
|
|
||||||
data.DelState = globalkey.DelStateNo
|
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info)
|
|
||||||
}, userIdKey, userMobileKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) {
|
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
|
||||||
var resp User
|
|
||||||
err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table)
|
|
||||||
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 *defaultUserModel) FindOneByMobile(ctx context.Context, mobile string) (*User, error) {
|
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
|
|
||||||
var resp User
|
|
||||||
err := m.QueryRowIndexCtx(ctx, &resp, userMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `mobile` = ? and del_state = ? limit 1", userRows, m.table)
|
|
||||||
if err := conn.QueryRowCtx(ctx, &resp, query, mobile, 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 *defaultUserModel) Update(ctx context.Context, session sqlx.Session, newData *User) (sql.Result, error) {
|
|
||||||
data, err := m.FindOne(ctx, newData.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id)
|
|
||||||
}, userIdKey, userMobileKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, newData *User) 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
|
|
||||||
}
|
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
|
||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userRowsWithPlaceHolder)
|
|
||||||
if session != nil {
|
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id, oldVersion)
|
|
||||||
}
|
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Id, oldVersion)
|
|
||||||
}, userIdKey, userMobileKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
updateCount, err := sqlResult.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if updateCount == 0 {
|
|
||||||
return ErrNoRowsUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *User) error {
|
|
||||||
data.DelState = globalkey.DelStateYes
|
|
||||||
data.DeleteTime = time.Now()
|
|
||||||
if err := m.UpdateWithVersion(ctx, session, data); err != nil {
|
|
||||||
return errors.Wrapf(errors.New("delete soft failed "), "UserModel delete err : %+v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) 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 *defaultUserModel) 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 *defaultUserModel) FindAll(ctx context.Context, builder squirrel.SelectBuilder, orderBy string) ([]*User, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userRows)
|
|
||||||
|
|
||||||
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 []*User
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) FindPageListByPage(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userRows)
|
|
||||||
|
|
||||||
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 []*User
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) FindPageListByPageWithTotal(ctx context.Context, builder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*User, int64, error) {
|
|
||||||
|
|
||||||
total, err := m.FindCount(ctx, builder, "id")
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = builder.Columns(userRows)
|
|
||||||
|
|
||||||
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 []*User
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, total, nil
|
|
||||||
default:
|
|
||||||
return nil, total, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) FindPageListByIdDESC(ctx context.Context, builder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*User, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userRows)
|
|
||||||
|
|
||||||
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 []*User
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) FindPageListByIdASC(ctx context.Context, builder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*User, error) {
|
|
||||||
|
|
||||||
builder = builder.Columns(userRows)
|
|
||||||
|
|
||||||
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 []*User
|
|
||||||
err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return resp, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) 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 *defaultUserModel) SelectBuilder() squirrel.SelectBuilder {
|
|
||||||
return squirrel.Select().From(m.table)
|
|
||||||
}
|
|
||||||
func (m *defaultUserModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
|
|
||||||
data, err := m.FindOne(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
|
||||||
_, 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)
|
|
||||||
}, userIdKey, userMobileKey)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
|
|
||||||
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
|
|
||||||
}
|
|
||||||
func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
|
|
||||||
query := fmt.Sprintf("select %s from %s where `id` = ? and del_state = ? limit 1", userRows, m.table)
|
|
||||||
return conn.QueryRowCtx(ctx, v, query, primary, globalkey.DelStateNo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultUserModel) tableName() string {
|
|
||||||
return m.table
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 UserAuthTypeWxOfficialAccount string = "wx_official_account"
|
|
@ -15,4 +15,6 @@ const DB_UPDATE_AFFECTED_ZERO_ERROR uint32 = 100006
|
|||||||
const PARAM_VERIFICATION_ERROR uint32 = 100007
|
const PARAM_VERIFICATION_ERROR uint32 = 100007
|
||||||
const CUSTOM_ERROR uint32 = 100008
|
const CUSTOM_ERROR uint32 = 100008
|
||||||
|
|
||||||
//用户模块
|
const LOGIN_FAILED uint32 = 200001
|
||||||
|
const LOGIC_QUERY_WAIT uint32 = 200002
|
||||||
|
const LOGIC_QUERY_ERROR uint32 = 200003
|
||||||
|
25
deploy/script/genModel.ps1
Normal file
25
deploy/script/genModel.ps1
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 使用方法:
|
||||||
|
# .\genModel.ps1 user user
|
||||||
|
# .\genModel.ps1 user user_auth
|
||||||
|
# 再将 .\genModel 下的文件剪切到对应服务的 model 目录里面,记得改 package
|
||||||
|
# goctl model mysql datasource -url="qnc:5vg67b3UNHu8@tcp(127.0.0.1:20001)/qnc" -table="product" -dir="./model" --home="../template" -cache=true --style=goZero
|
||||||
|
param (
|
||||||
|
[string]$database,
|
||||||
|
[string]$tables
|
||||||
|
)
|
||||||
|
|
||||||
|
# 生成的表名
|
||||||
|
$modeldir = "./genModel"
|
||||||
|
$templateDir = Join-Path -Path (Resolve-Path "$PSScriptRoot/..") -ChildPath "template"
|
||||||
|
# 数据库配置
|
||||||
|
$host = "127.0.0.1"
|
||||||
|
$port = "20001"
|
||||||
|
$dbname = "$database"
|
||||||
|
$username = "qnc"
|
||||||
|
$passwd = "5vg67b3UNHu8"
|
||||||
|
|
||||||
|
Write-Output "开始创建库:$dbname 的表:$tables"
|
||||||
|
|
||||||
|
# 执行 goctl 命令生成 model
|
||||||
|
$command = "goctl model mysql datasource -url=`"$username`:$passwd`@tcp($host`:$port)/$dbname`" -table=`"$tables`" -dir=`"$modeldir`" --home=`"$templateDir`" -cache=true --style=goZero"
|
||||||
|
Invoke-Expression $command
|
25
deploy/sql/order.sql
Normal file
25
deploy/sql/order.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
CREATE TABLE `order` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`order_no` varchar(32) NOT NULL COMMENT '自生成的订单号',
|
||||||
|
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||||
|
`product_id` bigint NOT NULL COMMENT '产品ID(软关联到产品表)',
|
||||||
|
`payment_platform` enum('alipay', 'wechat', 'other') NOT NULL COMMENT '支付平台(支付宝、微信、其他)',
|
||||||
|
`payment_scene` enum('app', 'h5', 'mini_program', 'public_account') NOT NULL COMMENT '支付场景(App、H5、微信小程序、公众号)',
|
||||||
|
`platform_order_id` varchar(64) DEFAULT NULL COMMENT '支付平台订单号',
|
||||||
|
`amount` decimal(10, 2) NOT NULL COMMENT '支付金额',
|
||||||
|
`status` enum('pending', 'paid', 'failed', 'refunded', 'closed') NOT NULL DEFAULT 'pending' COMMENT '支付状态',
|
||||||
|
`del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态',
|
||||||
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
|
||||||
|
`refund_time` datetime DEFAULT NULL COMMENT '退款时间',
|
||||||
|
`close_time` datetime DEFAULT NULL COMMENT '订单关闭时间',
|
||||||
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_order_no` (`order_no`),
|
||||||
|
KEY `idx_user_id` (`user_id`),
|
||||||
|
KEY `idx_product_id` (`product_id`),
|
||||||
|
KEY `idx_payment_platform` (`payment_platform`),
|
||||||
|
KEY `idx_payment_scene` (`payment_scene`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单表';
|
99
deploy/sql/product.sql
Normal file
99
deploy/sql/product.sql
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for product
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `product`;
|
||||||
|
CREATE TABLE `product` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
|
`del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态',
|
||||||
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
|
`product_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '服务名',
|
||||||
|
`product_en` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '英文名',
|
||||||
|
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
|
||||||
|
`notes` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '备注',
|
||||||
|
`cost_price` DECIMAL(10, 2) NOT NULL DEFAULT '1.00' COMMENT '成本',
|
||||||
|
`sell_price` DECIMAL(10, 2) NOT NULL DEFAULT '1.00' COMMENT '售价',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
UNIQUE KEY `unique_product_name` (`product_name`),
|
||||||
|
UNIQUE KEY `unique_product_en` (`product_en`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='产品表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records for product
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `product` (`product_name`, `product_en`, `description`, `notes`, `cost_price`, `sell_price`) VALUES
|
||||||
|
('背景调查', 'backgroundchecklogic', '', '', 1, 1),
|
||||||
|
('企业报告', 'companyinfologic', '', '', 1, 1),
|
||||||
|
('家政服务', 'homeservicelogic', '', '', 1, 1),
|
||||||
|
('婚姻状态', 'marriagelogic', '', '', 1, 1),
|
||||||
|
('贷前背调', 'preloanbackgroundchecklogic', '', '', 1, 1),
|
||||||
|
('租赁服务', 'rentalinfologic', '', '', 1, 1),
|
||||||
|
('个人风险评估', 'riskassessmentlogic', '', '', 1, 1);
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for feature
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `feature`;
|
||||||
|
CREATE TABLE `feature` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
|
`del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态',
|
||||||
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
|
`api_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'API标识',
|
||||||
|
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_api_id` (`api_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='功能表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for product_feature
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `product_feature`;
|
||||||
|
CREATE TABLE `product_feature` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`product_id` bigint NOT NULL COMMENT '产品ID',
|
||||||
|
`feature_id` bigint NOT NULL COMMENT '功能ID',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
|
`del_state` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态',
|
||||||
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_product_feature` (`product_id`, `feature_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='产品与功能关联表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records for feature
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `feature` (`api_id`, `name`) VALUES
|
||||||
|
('G09SC02', '单人婚姻'),
|
||||||
|
('G27BJ05', '借贷意向'),
|
||||||
|
('G28BJ05', '借贷行为'),
|
||||||
|
('G26BJ05', '特殊名单'),
|
||||||
|
('G34BJ03', '个人不良'),
|
||||||
|
('G35SC01', '个人涉诉'),
|
||||||
|
('G05HZ01', '股东人企关系');
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 插入每个产品与每个功能的对应关系
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
INSERT INTO `product_feature` (`product_id`, `feature_id`)
|
||||||
|
SELECT
|
||||||
|
p.id AS product_id,
|
||||||
|
f.id AS feature_id
|
||||||
|
FROM
|
||||||
|
product p
|
||||||
|
CROSS JOIN
|
||||||
|
feature f;
|
18
deploy/sql/query.sql
Normal file
18
deploy/sql/query.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE `query` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`order_id` BIGINT NOT NULL COMMENT '订单ID(软关联到订单表)',
|
||||||
|
`user_id` BIGINT NOT NULL COMMENT '用户ID(直接关联到用户)',
|
||||||
|
`product_id` BIGINT NOT NULL COMMENT '产品ID(直接关联到产品)',
|
||||||
|
`query_params` TEXT NOT NULL COMMENT '查询params数据',
|
||||||
|
`query_data` LONGTEXT COMMENT '查询结果数据',
|
||||||
|
`query_state` ENUM('pending', 'success', 'failed') NOT NULL DEFAULT 'pending' COMMENT '查询状态',
|
||||||
|
`del_state` TINYINT NOT NULL DEFAULT '0' COMMENT '删除状态',
|
||||||
|
`version` BIGINT NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
|
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`delete_time` DATETIME DEFAULT NULL COMMENT '删除时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_order_id` (`order_id`),
|
||||||
|
KEY `idx_user_id` (`user_id`),
|
||||||
|
KEY `idx_product_id` (`product_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='查询结果表,存储关联订单的查询数据';
|
@ -9,15 +9,15 @@ CREATE TABLE `user` (
|
|||||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
`delete_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
`mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
`mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||||
`password` varchar(255) 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 '',
|
`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 '',
|
`info` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `idx_mobile` (`mobile`)
|
UNIQUE KEY `unique_mobile` (`mobile`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@ -28,15 +28,15 @@ CREATE TABLE `user_auth` (
|
|||||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
`delete_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`delete_time` datetime DEFAULT NULL COMMENT '删除时间',
|
||||||
`del_state` tinyint NOT NULL DEFAULT '0',
|
`del_state` tinyint NOT NULL DEFAULT '0',
|
||||||
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
`version` bigint NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||||
`user_id` bigint NOT NULL DEFAULT '0',
|
`user_id` bigint NOT NULL DEFAULT '0',
|
||||||
`auth_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台唯一id',
|
`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 '平台类型',
|
`auth_type` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '平台类型',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `idx_type_key` (`auth_type`,`auth_key`) USING BTREE,
|
UNIQUE KEY `unique_type_key` (`auth_type`,`auth_key`) USING BTREE,
|
||||||
UNIQUE KEY `idx_userId_key` (`user_id`,`auth_type`)
|
UNIQUE KEY `unique_userId_key` (`user_id`,`auth_type`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户授权表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户授权表';
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result,error) {
|
func (m *default{{.upperStartCamelObject}}Model) Insert(ctx context.Context,session sqlx.Session, data *{{.upperStartCamelObject}}) (sql.Result,error) {
|
||||||
data.DeleteTime = time.Unix(0,0)
|
|
||||||
data.DelState = globalkey.DelStateNo
|
data.DelState = globalkey.DelStateNo
|
||||||
{{if .withCache}}{{.keys}}
|
{{if .withCache}}{{.keys}}
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
@ -65,7 +65,7 @@ func (m *default{{.upperStartCamelObject}}Model) UpdateWithVersion(ctx context.C
|
|||||||
|
|
||||||
func (m *default{{.upperStartCamelObject}}Model) DeleteSoft(ctx context.Context,session sqlx.Session,data *{{.upperStartCamelObject}}) error {
|
func (m *default{{.upperStartCamelObject}}Model) DeleteSoft(ctx context.Context,session sqlx.Session,data *{{.upperStartCamelObject}}) error {
|
||||||
data.DelState = globalkey.DelStateYes
|
data.DelState = globalkey.DelStateYes
|
||||||
data.DeleteTime = time.Now()
|
data.DeleteTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||||
if err:= m.UpdateWithVersion(ctx,session, data);err!= nil{
|
if err:= m.UpdateWithVersion(ctx,session, data);err!= nil{
|
||||||
return errors.Wrapf(errors.New("delete soft failed "),"{{.upperStartCamelObject}}Model delete err : %+v",err)
|
return errors.Wrapf(errors.New("delete soft failed "),"{{.upperStartCamelObject}}Model delete err : %+v",err)
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
- qnc_net
|
- qnc_net
|
||||||
|
- 1panel-network
|
||||||
|
|
||||||
#redis容器 - Redis container
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.4.0
|
image: redis:7.4.0
|
||||||
container_name: qnc_redis
|
container_name: qnc_redis
|
||||||
@ -50,8 +50,22 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- qnc_net
|
- qnc_net
|
||||||
|
|
||||||
|
asynqmon:
|
||||||
|
image: hibiken/asynqmon:latest
|
||||||
|
container_name: qnc_asynqmon
|
||||||
|
ports:
|
||||||
|
- "20003:8080"
|
||||||
|
command:
|
||||||
|
- '--redis-addr=qnc_redis:6379'
|
||||||
|
- '--redis-password=3m3WsgyCKWqz'
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- qnc_net
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
qnc_net:
|
qnc_net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
1panel-network:
|
||||||
|
external: true
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
qnc_user_api:
|
|
||||||
image: cosmtrek/air
|
|
||||||
container_name: qnc_user_api
|
|
||||||
environment:
|
|
||||||
- TZ=Asia/Shanghai
|
|
||||||
working_dir: /app # 将工作目录设置为根目录
|
|
||||||
ports:
|
|
||||||
- 31001:8888
|
|
||||||
volumes:
|
|
||||||
- .:/app # 将项目根目录挂载到容器根目录
|
|
||||||
entrypoint: air -c ./app/user/cmd/api/.air.toml
|
|
||||||
privileged: true
|
|
||||||
restart: always
|
|
||||||
networks:
|
|
||||||
- qnc_net
|
|
||||||
|
|
||||||
|
|
||||||
networks:
|
|
||||||
qnc_net:
|
|
||||||
driver: bridge
|
|
||||||
|
|
16
go.mod
16
go.mod
@ -10,9 +10,12 @@ require (
|
|||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
||||||
github.com/go-playground/validator/v10 v10.22.1
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/shopspring/decimal v1.4.0
|
github.com/shopspring/decimal v1.4.0
|
||||||
|
github.com/smartwalle/alipay/v3 v3.2.23
|
||||||
github.com/sony/sonyflake v1.2.0
|
github.com/sony/sonyflake v1.2.0
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.20
|
||||||
github.com/zeromicro/go-zero v1.7.3
|
github.com/zeromicro/go-zero v1.7.3
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.67.1
|
||||||
)
|
)
|
||||||
@ -40,7 +43,7 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/jinzhu/copier v0.4.0 // indirect
|
github.com/hibiken/asynq v0.25.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
@ -52,13 +55,22 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||||
|
github.com/panjf2000/ants/v2 v2.10.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.55.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
|
github.com/smartwalle/ncrypto v1.0.4 // indirect
|
||||||
|
github.com/smartwalle/ngx v1.0.9 // indirect
|
||||||
|
github.com/smartwalle/nsign v1.0.9 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
|
github.com/tidwall/gjson v1.18.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||||
@ -74,8 +86,10 @@ require (
|
|||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
golang.org/x/crypto v0.28.0 // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
|
golang.org/x/sync v0.9.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.19.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/api v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
google.golang.org/protobuf v1.35.1 // indirect
|
google.golang.org/protobuf v1.35.1 // indirect
|
||||||
|
34
go.sum
34
go.sum
@ -6,6 +6,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl
|
|||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||||
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
|
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
|
||||||
|
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
|
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
|
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||||
@ -134,6 +136,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||||
|
github.com/hibiken/asynq v0.25.0 h1:VCPyRRrrjFChsTSI8x5OCPu51MlEz6Rk+1p0kHKnZug=
|
||||||
|
github.com/hibiken/asynq v0.25.0/go.mod h1:DYQ1etBEl2Y+uSkqFElGYbk3M0ujLVwCfWE+TlvxtEk=
|
||||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
@ -173,6 +177,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
|||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
||||||
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
||||||
|
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
|
||||||
|
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@ -192,10 +198,20 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
|||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
|
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=
|
||||||
|
github.com/smartwalle/ncrypto v1.0.4/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk=
|
||||||
|
github.com/smartwalle/ngx v1.0.9 h1:pUXDvWRZJIHVrCKA1uZ15YwNti+5P4GuJGbpJ4WvpMw=
|
||||||
|
github.com/smartwalle/ngx v1.0.9/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0=
|
||||||
|
github.com/smartwalle/nsign v1.0.9 h1:8poAgG7zBd8HkZy9RQDwasC6XZvJpDGQWSjzL2FZL6E=
|
||||||
|
github.com/smartwalle/nsign v1.0.9/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@ -203,6 +219,8 @@ github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
|
|||||||
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
|
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@ -214,12 +232,23 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.20 h1:gS8oFn1bHGnyapR2Zb4aqTV6l4kJWgbtqjCq6k1L9DQ=
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
@ -301,6 +330,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||||
|
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -339,6 +371,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
#user
|
|
||||||
app/user/cmd/api/**/*.go {
|
|
||||||
prep: go build -o data/server/user-api -v app/user/cmd/api/user.go
|
|
||||||
daemon +sigkill: ./data/server/user-api -f app/user/cmd/api/etc/user.yaml
|
|
||||||
}
|
|
105
pkg/lzkit/crypto/crypto.go
Normal file
105
pkg/lzkit/crypto/crypto.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PKCS7填充
|
||||||
|
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(ciphertext)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(ciphertext, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除PKCS7填充
|
||||||
|
func PKCS7UnPadding(origData []byte) ([]byte, error) {
|
||||||
|
length := len(origData)
|
||||||
|
if length == 0 {
|
||||||
|
return nil, errors.New("input data error")
|
||||||
|
}
|
||||||
|
unpadding := int(origData[length-1])
|
||||||
|
if unpadding > length {
|
||||||
|
return nil, errors.New("unpadding size is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查填充字节是否一致
|
||||||
|
for i := 0; i < unpadding; i++ {
|
||||||
|
if origData[length-1-i] != byte(unpadding) {
|
||||||
|
return nil, errors.New("invalid padding")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return origData[:(length - unpadding)], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES CBC模式加密,Base64传入传出
|
||||||
|
func AesEncrypt(plainText, key []byte) (string, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
blockSize := block.BlockSize()
|
||||||
|
plainText = PKCS7Padding(plainText, blockSize)
|
||||||
|
|
||||||
|
cipherText := make([]byte, blockSize+len(plainText))
|
||||||
|
iv := cipherText[:blockSize] // 使用前blockSize字节作为IV
|
||||||
|
_, err = io.ReadFull(rand.Reader, iv)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(cipherText[blockSize:], plainText)
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(cipherText), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES CBC模式解密,Base64传入传出
|
||||||
|
func AesDecrypt(cipherTextBase64 string, key []byte) ([]byte, error) {
|
||||||
|
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockSize := block.BlockSize()
|
||||||
|
if len(cipherText) < blockSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := cipherText[:blockSize]
|
||||||
|
cipherText = cipherText[blockSize:]
|
||||||
|
|
||||||
|
if len(cipherText)%blockSize != 0 {
|
||||||
|
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
mode.CryptBlocks(cipherText, cipherText)
|
||||||
|
|
||||||
|
plainText, err := PKCS7UnPadding(cipherText)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plainText, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Md5Encrypt 用于对传入的message进行MD5加密
|
||||||
|
func Md5Encrypt(message string) string {
|
||||||
|
hash := md5.New()
|
||||||
|
hash.Write([]byte(message)) // 将字符串转换为字节切片并写入
|
||||||
|
return hex.EncodeToString(hash.Sum(nil)) // 将哈希值转换为16进制字符串并返回
|
||||||
|
}
|
63
pkg/lzkit/crypto/generate.go
Normal file
63
pkg/lzkit/crypto/generate.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 生成AES-128密钥的函数,符合市面规范
|
||||||
|
func GenerateSecretKey() (string, error) {
|
||||||
|
key := make([]byte, 16) // 16字节密钥
|
||||||
|
_, err := io.ReadFull(rand.Reader, key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateSecretId() (string, error) {
|
||||||
|
// 创建一个字节数组,用于存储随机数据
|
||||||
|
bytes := make([]byte, 8) // 因为每个字节表示两个16进制字符
|
||||||
|
|
||||||
|
// 读取随机字节到数组中
|
||||||
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将字节数组转换为16进制字符串
|
||||||
|
return hex.EncodeToString(bytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTransactionID 生成16位数的交易单号
|
||||||
|
func GenerateTransactionID() string {
|
||||||
|
length := 16
|
||||||
|
// 获取当前时间戳
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
|
||||||
|
// 转换为字符串
|
||||||
|
timeStr := strconv.FormatInt(timestamp, 10)
|
||||||
|
|
||||||
|
// 生成随机数
|
||||||
|
mathrand.Seed(time.Now().UnixNano())
|
||||||
|
randomPart := strconv.Itoa(mathrand.Intn(1000000))
|
||||||
|
|
||||||
|
// 组合时间戳和随机数
|
||||||
|
combined := timeStr + randomPart
|
||||||
|
|
||||||
|
// 如果长度超出指定值,则截断;如果不够,则填充随机字符
|
||||||
|
if len(combined) >= length {
|
||||||
|
return combined[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果长度不够,填充0
|
||||||
|
for len(combined) < length {
|
||||||
|
combined += strconv.Itoa(mathrand.Intn(10)) // 填充随机数
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined
|
||||||
|
}
|
150
pkg/lzkit/crypto/west_crypto.go
Normal file
150
pkg/lzkit/crypto/west_crypto.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KEY_SIZE = 16 // AES-128, 16 bytes
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encrypt encrypts the given data using AES encryption in ECB mode with PKCS5 padding
|
||||||
|
func WestDexEncrypt(data, secretKey string) (string, error) {
|
||||||
|
key := generateAESKey(KEY_SIZE*8, []byte(secretKey))
|
||||||
|
ciphertext, err := aesEncrypt([]byte(data), key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the given base64-encoded string using AES encryption in ECB mode with PKCS5 padding
|
||||||
|
func WestDexDecrypt(encodedData, secretKey string) ([]byte, error) {
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(encodedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key := generateAESKey(KEY_SIZE*8, []byte(secretKey))
|
||||||
|
plaintext, err := aesDecrypt(ciphertext, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAESKey generates a key for AES encryption using a SHA-1 based PRNG
|
||||||
|
func generateAESKey(length int, password []byte) []byte {
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write(password)
|
||||||
|
state := h.Sum(nil)
|
||||||
|
|
||||||
|
keyBytes := make([]byte, 0, length/8)
|
||||||
|
for len(keyBytes) < length/8 {
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write(state)
|
||||||
|
state = h.Sum(nil)
|
||||||
|
keyBytes = append(keyBytes, state...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyBytes[:length/8]
|
||||||
|
}
|
||||||
|
|
||||||
|
// aesEncrypt encrypts plaintext using AES in ECB mode with PKCS5 padding
|
||||||
|
func aesEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paddedPlaintext := pkcs5Padding(plaintext, block.BlockSize())
|
||||||
|
ciphertext := make([]byte, len(paddedPlaintext))
|
||||||
|
mode := newECBEncrypter(block)
|
||||||
|
mode.CryptBlocks(ciphertext, paddedPlaintext)
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// aesDecrypt decrypts ciphertext using AES in ECB mode with PKCS5 padding
|
||||||
|
func aesDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plaintext := make([]byte, len(ciphertext))
|
||||||
|
mode := newECBDecrypter(block)
|
||||||
|
mode.CryptBlocks(plaintext, ciphertext)
|
||||||
|
return pkcs5Unpadding(plaintext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs5Padding pads the input to a multiple of the block size using PKCS5 padding
|
||||||
|
func pkcs5Padding(src []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(src)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(src, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs5Unpadding removes PKCS5 padding from the input
|
||||||
|
func pkcs5Unpadding(src []byte) []byte {
|
||||||
|
length := len(src)
|
||||||
|
unpadding := int(src[length-1])
|
||||||
|
return src[:(length - unpadding)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECB mode encryption/decryption
|
||||||
|
type ecb struct {
|
||||||
|
b cipher.Block
|
||||||
|
blockSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newECB(b cipher.Block) *ecb {
|
||||||
|
return &ecb{
|
||||||
|
b: b,
|
||||||
|
blockSize: b.BlockSize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecbEncrypter ecb
|
||||||
|
|
||||||
|
func newECBEncrypter(b cipher.Block) cipher.BlockMode {
|
||||||
|
return (*ecbEncrypter)(newECB(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
|
||||||
|
|
||||||
|
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||||
|
if len(src)%x.blockSize != 0 {
|
||||||
|
panic("crypto/cipher: input not full blocks")
|
||||||
|
}
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
panic("crypto/cipher: output smaller than input")
|
||||||
|
}
|
||||||
|
for len(src) > 0 {
|
||||||
|
x.b.Encrypt(dst, src[:x.blockSize])
|
||||||
|
src = src[x.blockSize:]
|
||||||
|
dst = dst[x.blockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ecbDecrypter ecb
|
||||||
|
|
||||||
|
func newECBDecrypter(b cipher.Block) cipher.BlockMode {
|
||||||
|
return (*ecbDecrypter)(newECB(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
|
||||||
|
|
||||||
|
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||||
|
if len(src)%x.blockSize != 0 {
|
||||||
|
panic("crypto/cipher: input not full blocks")
|
||||||
|
}
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
panic("crypto/cipher: output smaller than input")
|
||||||
|
}
|
||||||
|
for len(src) > 0 {
|
||||||
|
x.b.Decrypt(dst, src[:x.blockSize])
|
||||||
|
src = src[x.blockSize:]
|
||||||
|
dst = dst[x.blockSize:]
|
||||||
|
}
|
||||||
|
}
|
65
pkg/lzkit/delay/ProgressiveDelay.go
Normal file
65
pkg/lzkit/delay/ProgressiveDelay.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package delay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProgressiveDelay 用于管理渐进式延迟策略
|
||||||
|
type ProgressiveDelay struct {
|
||||||
|
initialDelay time.Duration // 初始延迟时间
|
||||||
|
growthFactor float64 // 延迟增长因子 (例如 1.5)
|
||||||
|
maxDelay time.Duration // 最大延迟时间
|
||||||
|
maxRetryDuration time.Duration // 最大重试时间
|
||||||
|
currentDelay time.Duration // 当前延迟时间
|
||||||
|
startTime time.Time // 重试开始时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// New 创建一个新的渐进式延迟对象
|
||||||
|
func New(initialDelay, maxDelay, maxRetryDuration time.Duration, growthFactor float64) (*ProgressiveDelay, error) {
|
||||||
|
// 参数校验
|
||||||
|
if initialDelay <= 0 {
|
||||||
|
return nil, errors.New("initialDelay must be greater than zero")
|
||||||
|
}
|
||||||
|
if maxDelay <= 0 {
|
||||||
|
return nil, errors.New("maxDelay must be greater than zero")
|
||||||
|
}
|
||||||
|
if maxRetryDuration <= 0 {
|
||||||
|
return nil, errors.New("maxRetryDuration must be greater than zero")
|
||||||
|
}
|
||||||
|
if growthFactor <= 1.0 {
|
||||||
|
return nil, errors.New("growthFactor must be greater than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化并返回
|
||||||
|
return &ProgressiveDelay{
|
||||||
|
initialDelay: initialDelay,
|
||||||
|
maxDelay: maxDelay,
|
||||||
|
maxRetryDuration: maxRetryDuration,
|
||||||
|
growthFactor: growthFactor,
|
||||||
|
currentDelay: initialDelay,
|
||||||
|
startTime: time.Now(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextDelay 计算并返回下次的延迟时间
|
||||||
|
func (pd *ProgressiveDelay) NextDelay() (time.Duration, error) {
|
||||||
|
// 检查最大重试时间是否已过
|
||||||
|
if time.Since(pd.startTime) > pd.maxRetryDuration {
|
||||||
|
return 0, fmt.Errorf("最大重试时间超过限制: %v", pd.maxRetryDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回当前延迟时间
|
||||||
|
delay := pd.currentDelay
|
||||||
|
|
||||||
|
// 计算下一个延迟时间并更新 currentDelay
|
||||||
|
pd.currentDelay = time.Duration(float64(pd.currentDelay) * pd.growthFactor)
|
||||||
|
|
||||||
|
// 如果下次延迟超过最大延迟时间,限制在最大值
|
||||||
|
if pd.currentDelay > pd.maxDelay {
|
||||||
|
pd.currentDelay = pd.maxDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
return delay, nil
|
||||||
|
}
|
38
pkg/lzkit/lzUtils/sqlutls.go
Normal file
38
pkg/lzkit/lzUtils/sqlutls.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package lzUtils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringToNullString 将 string 转换为 sql.NullString
|
||||||
|
func StringToNullString(s string) sql.NullString {
|
||||||
|
return sql.NullString{
|
||||||
|
String: s,
|
||||||
|
Valid: s != "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NullStringToString 将 sql.NullString 转换为 string
|
||||||
|
func NullStringToString(ns sql.NullString) string {
|
||||||
|
if ns.Valid {
|
||||||
|
return ns.String
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeToNullTime 将 time.Time 转换为 sql.NullTime
|
||||||
|
func TimeToNullTime(t time.Time) sql.NullTime {
|
||||||
|
return sql.NullTime{
|
||||||
|
Time: t,
|
||||||
|
Valid: !t.IsZero(), // 仅当 t 不是零值时才设置为有效
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NullTimeToTime 将 sql.NullTime 转换为 time.Time
|
||||||
|
func NullTimeToTime(nt sql.NullTime) time.Time {
|
||||||
|
if nt.Valid {
|
||||||
|
return nt.Time
|
||||||
|
}
|
||||||
|
return time.Time{} // 返回零值时间
|
||||||
|
}
|
15
pkg/lzkit/lzUtils/utils.go
Normal file
15
pkg/lzkit/lzUtils/utils.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package lzUtils
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ToWechatAmount 将金额从元转换为微信支付 SDK 需要的分(int64 类型)
|
||||||
|
func ToWechatAmount(amount float64) int64 {
|
||||||
|
// 将金额从元转换为分,并四舍五入
|
||||||
|
return int64(amount*100 + 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAlipayAmount 将金额从元转换为支付宝支付 SDK 需要的字符串格式,保留两位小数
|
||||||
|
func ToAlipayAmount(amount float64) string {
|
||||||
|
// 格式化为字符串,保留两位小数
|
||||||
|
return fmt.Sprintf("%.2f", amount)
|
||||||
|
}
|
@ -47,6 +47,9 @@ func init() {
|
|||||||
if err := validate.RegisterValidation("password", validatePassword); err != nil {
|
if err := validate.RegisterValidation("password", validatePassword); err != nil {
|
||||||
panic(fmt.Sprintf("注册 password 验证器时发生错误: %v", err))
|
panic(fmt.Sprintf("注册 password 验证器时发生错误: %v", err))
|
||||||
}
|
}
|
||||||
|
if err := validate.RegisterValidation("payMethod", validatePayMethod); err != nil {
|
||||||
|
panic(fmt.Sprintf("注册 payMethod 验证器时发生错误: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,3 +155,20 @@ func validatePassword(fl validator.FieldLevel) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 支付方式
|
||||||
|
func validatePayMethod(fl validator.FieldLevel) bool {
|
||||||
|
payMethod := fl.Field().String()
|
||||||
|
|
||||||
|
if payMethod == "" {
|
||||||
|
return true // 如果为空,认为是有效的
|
||||||
|
}
|
||||||
|
|
||||||
|
validTypes := map[string]bool{
|
||||||
|
"alipay": true, // 中国电信
|
||||||
|
"wechatpay": true, // 中国移动
|
||||||
|
}
|
||||||
|
|
||||||
|
return validTypes[payMethod]
|
||||||
|
|
||||||
|
}
|
||||||
|
148
test/test.go
Normal file
148
test/test.go
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user