Compare commits
63 Commits
76784c3c1b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bdb851b701 | |||
| a32ee2052d | |||
| e29e0f4d0f | |||
| d8b59829f1 | |||
| 063fb158c9 | |||
| 02cffff608 | |||
| c24f54ce8c | |||
| e18fe543cd | |||
| 6e77c88f9d | |||
| c229ea2d5a | |||
| a81f5198e3 | |||
| 867a1022dd | |||
| 0d476fa477 | |||
| 9e12db0cd4 | |||
| b0313b951e | |||
| 0431ee605b | |||
| 940bf64546 | |||
| 4c49724353 | |||
| 467f081a1a | |||
| 9c4fd95955 | |||
| fe18036478 | |||
| f2ac09acdd | |||
| d68184fa59 | |||
| 5d51b7a400 | |||
| 6d4d1b2aed | |||
| a09ba16e8c | |||
| ad1ceaef17 | |||
| 13dc22cfa9 | |||
| 8ccda35a97 | |||
| 71eb38e66f | |||
| 66d8d660f2 | |||
| 6e3c268b82 | |||
| 6b8e7eada6 | |||
| 6a627dc474 | |||
| ed35631900 | |||
| d80076e2c7 | |||
| 2d40d589e2 | |||
| 2d7e241b76 | |||
| de78857139 | |||
| 15c0d20508 | |||
| e1f62efecd | |||
| 86bda66271 | |||
| f5e8fa6558 | |||
| be6aef3730 | |||
| 356b422879 | |||
| c2591eec44 | |||
| 3d531301cb | |||
| 7aa875af19 | |||
| 2b8add736f | |||
| ce34e426c4 | |||
| ca6dcc1b24 | |||
| 07bf234b30 | |||
| 33a8baefb7 | |||
| dc4fcbf857 | |||
| 9648fcd1ec | |||
| 7beac0d733 | |||
| a92f8a777f | |||
| b3f2c5c6b6 | |||
| d74a1514ab | |||
| c1b3709ba1 | |||
| a4f8e17f32 | |||
| bcbecc402d | |||
| 35d9ecbb6c |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -5,6 +5,13 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
|
# 忽略 IDE/调试生成的临时二进制文件
|
||||||
|
__debug_bin*
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
#deploy data
|
#deploy data
|
||||||
|
|
||||||
data/*
|
data/*
|
||||||
@@ -20,3 +27,6 @@ data/*
|
|||||||
/tmp/
|
/tmp/
|
||||||
|
|
||||||
/app/api
|
/app/api
|
||||||
|
**/__debug_bin*.exe
|
||||||
|
**/api.exe
|
||||||
|
**/*.exe
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ type (
|
|||||||
Amount float64 `json:"amount"` // 金额
|
Amount float64 `json:"amount"` // 金额
|
||||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
||||||
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
||||||
|
TaxRate float64 `json:"tax_rate"` // 扣税比例,如 0.06 表示 6%
|
||||||
Status int64 `json:"status"` // 状态
|
Status int64 `json:"status"` // 状态
|
||||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
PayeeAccount string `json:"payee_account"` // 收款账户
|
||||||
Remark string `json:"remark"` // 备注
|
Remark string `json:"remark"` // 备注
|
||||||
@@ -471,6 +472,7 @@ type (
|
|||||||
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
||||||
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
||||||
Remark string `json:"remark"` // 备注(拒绝时必填)
|
Remark string `json:"remark"` // 备注(拒绝时必填)
|
||||||
|
TaxRate *float64 `json:"tax_rate,optional"` // 扣税比例,如 0.06 表示 6%,不传则默认 6%
|
||||||
}
|
}
|
||||||
|
|
||||||
// 银行卡提现审核响应
|
// 银行卡提现审核响应
|
||||||
|
|||||||
@@ -41,7 +41,12 @@ type AdminGetQueryDetailByOrderIdResp {
|
|||||||
Id int64 `json:"id"` // 主键ID
|
Id int64 `json:"id"` // 主键ID
|
||||||
OrderId int64 `json:"order_id"` // 订单ID
|
OrderId int64 `json:"order_id"` // 订单ID
|
||||||
UserId int64 `json:"user_id"` // 用户ID
|
UserId int64 `json:"user_id"` // 用户ID
|
||||||
ProductName string `json:"product_name"` // 产品ID
|
ProductName string `json:"product_name"` // 产品名称
|
||||||
|
OrderNo string `json:"order_no"` // 商户订单号
|
||||||
|
PlatformOrderId string `json:"platform_order_id"` // 支付订单号
|
||||||
|
PaymentStatus string `json:"payment_status"` // 支付状态
|
||||||
|
PayTime string `json:"pay_time"` // 支付时间
|
||||||
|
RefundTime string `json:"refund_time"` // 退款时间(有退款时)
|
||||||
QueryParams map[string]interface{} `json:"query_params"`
|
QueryParams map[string]interface{} `json:"query_params"`
|
||||||
QueryData []AdminQueryItem `json:"query_data"`
|
QueryData []AdminQueryItem `json:"query_data"`
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ type (
|
|||||||
Nickname string `json:"nickname"` // 昵称
|
Nickname string `json:"nickname"` // 昵称
|
||||||
Info string `json:"info"` // 备注信息
|
Info string `json:"info"` // 备注信息
|
||||||
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable int64 `json:"disable"` // 是否封禁 0-可用 1-禁用
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
UpdateTime string `json:"update_time"` // 更新时间
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
}
|
}
|
||||||
@@ -77,6 +78,7 @@ type (
|
|||||||
Nickname string `json:"nickname"` // 昵称
|
Nickname string `json:"nickname"` // 昵称
|
||||||
Info string `json:"info"` // 备注信息
|
Info string `json:"info"` // 备注信息
|
||||||
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable int64 `json:"disable"` // 是否封禁 0-可用 1-禁用
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
UpdateTime string `json:"update_time"` // 更新时间
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
}
|
}
|
||||||
@@ -103,6 +105,7 @@ type (
|
|||||||
Nickname *string `json:"nickname,optional"` // 昵称
|
Nickname *string `json:"nickname,optional"` // 昵称
|
||||||
Info *string `json:"info,optional"` // 备注信息
|
Info *string `json:"info,optional"` // 备注信息
|
||||||
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
|
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable *int64 `json:"disable,optional"` // 是否封禁 0-可用 1-禁用
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新响应
|
// 更新响应
|
||||||
|
|||||||
@@ -363,6 +363,7 @@ type (
|
|||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
payeeAccount string `json:"payee_account"`
|
payeeAccount string `json:"payee_account"`
|
||||||
CreateTime string `json:"create_time"`
|
CreateTime string `json:"create_time"`
|
||||||
|
TaxRate float64 `json:"tax_rate"` // 扣税比例,如 0.06 表示 6%
|
||||||
}
|
}
|
||||||
GetWithdrawalReq {
|
GetWithdrawalReq {
|
||||||
Page int64 `form:"page"` // 页码
|
Page int64 `form:"page"` // 页码
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ service main {
|
|||||||
type (
|
type (
|
||||||
PaymentReq {
|
PaymentReq {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
PayMethod string `json:"pay_method"`
|
PayMethod string `json:"pay_method" validate:"required,oneof=wechat alipay appleiap test"`
|
||||||
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"`
|
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"`
|
||||||
}
|
}
|
||||||
PaymentResp {
|
PaymentResp {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ type (
|
|||||||
Data string `json:"data" validate:"required"`
|
Data string `json:"data" validate:"required"`
|
||||||
AgentIdentifier string `json:"agent_identifier,optional"`
|
AgentIdentifier string `json:"agent_identifier,optional"`
|
||||||
App bool `json:"app,optional"`
|
App bool `json:"app,optional"`
|
||||||
|
CaptchaVerifyParam string `json:"captchaVerifyParam,optional"`
|
||||||
}
|
}
|
||||||
QueryServiceResp {
|
QueryServiceResp {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@@ -139,6 +140,7 @@ type (
|
|||||||
QueryListReq {
|
QueryListReq {
|
||||||
Page int64 `form:"page"` // 页码
|
Page int64 `form:"page"` // 页码
|
||||||
PageSize int64 `form:"page_size"` // 每页数据量
|
PageSize int64 `form:"page_size"` // 每页数据量
|
||||||
|
Source string `form:"source,optional"` // 来源: miniapp 小程序过滤非车辆产品
|
||||||
}
|
}
|
||||||
QueryListResp {
|
QueryListResp {
|
||||||
Total int64 `json:"total"` // 总记录数
|
Total int64 `json:"total"` // 总记录数
|
||||||
|
|||||||
19
app/main/api/desc/front/tianyuan.api
Normal file
19
app/main/api/desc/front/tianyuan.api
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "天远异步回调"
|
||||||
|
desc: "天远车辆类接口异步回调入口"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 该服务只提供第三方回调入口,不依赖登录态
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: tianyuan
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "天远车辆类接口异步回调"
|
||||||
|
@handler vehicleCallback
|
||||||
|
post /tianyuan/vehicle/callback
|
||||||
|
}
|
||||||
|
|
||||||
32
app/main/api/desc/front/toolbox.api
Normal file
32
app/main/api/desc/front/toolbox.api
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "工具箱服务"
|
||||||
|
desc: "免费小工具:手机号归属地、VIN解析、车牌归属地等"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ==================== 通用请求/响应 ====================
|
||||||
|
|
||||||
|
type (
|
||||||
|
ToolboxQueryReq {
|
||||||
|
ToolKey string `json:"tool_key" validate:"required"`
|
||||||
|
Params map[string]interface{} `json:"params"`
|
||||||
|
}
|
||||||
|
ToolboxQueryResp {
|
||||||
|
ToolKey string `json:"tool_key"`
|
||||||
|
Result map[string]interface{} `json:"result"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ==================== 免费接口(无需登录) ====================
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: toolbox
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "通用工具查询"
|
||||||
|
@handler toolboxQuery
|
||||||
|
post /toolbox/query (ToolboxQueryReq) returns (ToolboxQueryResp)
|
||||||
|
}
|
||||||
39
app/main/api/desc/front/upload.api
Normal file
39
app/main/api/desc/front/upload.api
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
info (
|
||||||
|
title: "上传"
|
||||||
|
desc: "图片上传,用于行驶证等需 URL 的接口"
|
||||||
|
version: "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
UploadImageReq {
|
||||||
|
ImageBase64 string `json:"image_base64" validate:"required"` // 图片 base64(不含 data URL 前缀)
|
||||||
|
}
|
||||||
|
UploadImageResp {
|
||||||
|
Url string `json:"url"` // 可公网访问的图片 URL
|
||||||
|
}
|
||||||
|
ServeUploadedFileReq {
|
||||||
|
FileName string `path:"fileName"` // 文件名,如 uuid.jpg
|
||||||
|
}
|
||||||
|
// 实际由 Handler 根据 FilePath/ContentType 写文件流,不返回 JSON
|
||||||
|
ServeUploadedFileResp {
|
||||||
|
FilePath string `json:"-"` // 内部:本地文件路径
|
||||||
|
ContentType string `json:"-"` // 内部:Content-Type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: upload
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "上传图片,返回可访问 URL(如行驶证);限制 3MB"
|
||||||
|
@handler UploadImage
|
||||||
|
post /upload/image (UploadImageReq) returns (UploadImageResp)
|
||||||
|
|
||||||
|
@doc "访问已上传文件(供第三方或前端通过返回的 URL 拉取)"
|
||||||
|
@handler ServeUploadedFile
|
||||||
|
get /upload/file/:fileName (ServeUploadedFileReq) returns (ServeUploadedFileResp)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -143,6 +143,24 @@ type (
|
|||||||
sendSmsReq {
|
sendSmsReq {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
||||||
|
CaptchaVerifyParam string `json:"captchaVerifyParam"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//============================> captcha v1 <============================
|
||||||
|
@server (
|
||||||
|
prefix: api/v1
|
||||||
|
group: captcha
|
||||||
|
)
|
||||||
|
service main {
|
||||||
|
@doc "get encrypted scene id for aliyun captcha"
|
||||||
|
@handler getEncryptedSceneId
|
||||||
|
post /captcha/encryptedSceneId returns (GetEncryptedSceneIdResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
GetEncryptedSceneIdResp {
|
||||||
|
EncryptedSceneId string `json:"encryptedSceneId"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import "./front/product.api"
|
|||||||
import "./front/agent.api"
|
import "./front/agent.api"
|
||||||
import "./front/app.api"
|
import "./front/app.api"
|
||||||
import "./front/authorization.api"
|
import "./front/authorization.api"
|
||||||
|
import "./front/upload.api"
|
||||||
|
import "./front/tianyuan.api"
|
||||||
// 后台
|
// 后台
|
||||||
import "./admin/auth.api"
|
import "./admin/auth.api"
|
||||||
import "./admin/menu.api"
|
import "./admin/menu.api"
|
||||||
|
|||||||
@@ -14,32 +14,45 @@ VerifyCode:
|
|||||||
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
EndpointURL: "dysmsapi.aliyuncs.com"
|
EndpointURL: "dysmsapi.aliyuncs.com"
|
||||||
SignName: "天远查"
|
SignName: "海南海宇大数据"
|
||||||
TemplateCode: "SMS_302641455"
|
TemplateCode: "SMS_302641455"
|
||||||
ValidTime: 300
|
ValidTime: 300
|
||||||
|
Captcha:
|
||||||
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
|
EndpointURL: "captcha.cn-shanghai.aliyuncs.com"
|
||||||
|
SceneID: "wynt39to"
|
||||||
|
EKey: "xdhf5JbWVmFXx+2K+6kBk2aH++GtQBEI8Gmzdeen90o="
|
||||||
Encrypt:
|
Encrypt:
|
||||||
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
||||||
Alipay:
|
Alipay:
|
||||||
AppID: "2021004161631930"
|
AppID: "2021006121698606"
|
||||||
PrivateKey: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
|
PrivateKey: "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCx3TqaCUgZIMfG8Lu8fqkKqKLzONB9NBp4qCM2CAgxkUK1fd48XZ3ANDzfzNoeIbLbR/VL3ZUgyX/F8jO7Cit71JS58X9b1ngeRyBdaiBhLZgHb7Ngg0f1zDJuq8H6/i3Tiu9PaH0iOFKeaFCCpkakFyXjp//rqkcj2bPfa6lX0bD4YcILz8DC3iPy/cXHrHwaBryhYouYA3bKFROHykfF5D+tS2PZdR13BiDVElwDuquZCMdRdISfTw8snp5HSxA0TyRZIiGrtU3WvSKxYi2s8smZl/uYN1uj4w4GOivnTrkBdLy6DBqvgqRsCeYDDV5UyjtbAhvksZH60EWS3HTfAgMBAAECggEAK4dv+xjAa13pZpet6nC5ICGrV4kVBT9GJzdG/scycicRw2cdh3qFy+884qy4yN0Ib8AJmVqOT6rguWoQHPtdLv4Us/kVaT1wwkA3/ISnjgDhjxhYNwuKBe7GfO1OGQYx4u7CqJVy4ngUSC5RXdghu7Dqle+co2lV5cE20zv/Ar2nYo4p9fxcl/XAttqdRyby3ge6zJZP46Ru4CHzFzyUsrJYC575R3Jq68Zrr/+v0BmpEm/wAmoQE8W0ElMMH+Jw0twj+4l6PaUcq9oUVIL9wzl7ay1B6dKyxEOyinGYrmd65NzsTu8HhEpFdvQ+1O27XyahLsNWpSamJlV23ns/EQKBgQDd2UiQQRrj+itfa1/Zehy8MstEICw73LMDk129yNG9cU8U4y5vaTJkAcDTfme08B3uVVhsIUhJsFdgnh4ayyYW3jsB5BBBgszhwYQciH7f/3nE3rcX/TIs6yQAQkPhQi2RNSdn3SrCIhxgu9TwrZIDyQYJ6kk/9qEmZd93RrhOBwKBgQDNPpS1jFt4l08kzCHzfTvTbIu5z/6NPxmRSpcrdkoOAUfzlKSkRnKJuzkFSJEmWpFXILfNiza1dBYncg60Lu58yXpaGlLbVmxeFem+DdQm55lgl28d8Ssg6nlCTqE/tjB5LZ6qn8KEkWSj6gXH37pU1XFZh0A3+EIytnL7NnrsaQKBgGMaGUw3iSemLZHmiV7BKez4U80PAjOLl3xVbF7HQsp5v3X5NlkWiSgbkGPp57HwQa6h+Wn0RDKGz8GdYJ1fephklb92fbyGDbgblkSYxPSTT3Yed3QD61IdiGuFLoWF5o0jTYMcTWmDi2G7BpitMLj4J/Zt7mLgbYSVpYnG0bYpAoGAS7Tfya/CNdMqQFqD03rITI5nY9zS+mriFXO8Gy4A1vWmArU7ndTWfvNubwJ7d/hEUC0jX1AQmBH/8gDiZ5hAJAt1dDLtiTZxtqrCk3YqYUdgjf6N4C+LRxL2M30pgYTEkI5BTpKrf5bZ1pSGGVnvM0egDfQTvhF26ZnfA8buxLECgYAssWTLQ2Ou2NJ8N3HJx54kpKLwf218ulINsH6opcot22hbVfj0rc7lAcMe9FTGgmSgrbZG9QqAC2BpSHDeSC4C57iSYhG86tq/jwvNsV1miPV9RRj3CvwZkbHzCwqhRvCPS/QUgTzG3Z9pljYLkZjuMkb9jp814GbwEGPeoucefw=="
|
||||||
AlipayPublicKey: ""
|
AlipayPublicKey: ""
|
||||||
AppCertPath: "etc/merchant/appCertPublicKey_2021004161631930.crt"
|
AppCertPath: "etc/merchant/appCertPublicKey_2021006121698606.crt"
|
||||||
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
|
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
|
||||||
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
|
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
|
||||||
|
AppIDBak: "2021004161631930"
|
||||||
|
PrivateKeyBak: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
|
||||||
|
AlipayPublicKeyBak: ""
|
||||||
|
AppCertPathBak: "etc/merchant/bak/appCertPublicKey_2021004161631930.crt"
|
||||||
|
AlipayCertPathBak: "etc/merchant/bak/alipayCertPublicKey_RSA2.crt"
|
||||||
|
AlipayRootCertPathBak: "etc/merchant/bak/alipayRootCert.crt"
|
||||||
IsProduction: true
|
IsProduction: true
|
||||||
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
|
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
|
||||||
ReturnURL: "https://www.tianyuancha.cn/payment/result"
|
ReturnURL: "https://www.tianyuancha.cn/payment/result"
|
||||||
Wxpay:
|
Wxpay:
|
||||||
AppID: "wxa581992dc74d860e"
|
AppID: "wxd391e40295bd9dfb"
|
||||||
MchID: "1682635136"
|
MchID: "1105276690"
|
||||||
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
|
MchCertificateSerialNumber: "4F1738D21CAEB7F76193A770CDBA6D7002ED1CFD"
|
||||||
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
|
MchApiv3Key: "K2d8F5gJ1sP7zQ3bN9mR4xV6c0hL5tU2"
|
||||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||||
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601"
|
MchPublicKeyID: "PUB_KEY_ID_0111052766902026050900112134001605"
|
||||||
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
||||||
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781"
|
MchPlatformRAS: "PUB_KEY_ID_0111052766902026050900112134001605"
|
||||||
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/wechat/callback"
|
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/wechat/callback"
|
||||||
RefundNotifyUrl: "https://www.tianyuancha.cn/api/v1/wechat/refund_callback"
|
RefundNotifyUrl: "https://www.tianyuancha.cn/api/v1/wechat/refund_callback"
|
||||||
|
|
||||||
Applepay:
|
Applepay:
|
||||||
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
@@ -54,13 +67,14 @@ SystemConfig:
|
|||||||
ThreeVerify: false
|
ThreeVerify: false
|
||||||
CommissionSafeMode: false # 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
CommissionSafeMode: false # 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
||||||
WechatH5:
|
WechatH5:
|
||||||
AppID: "wxa581992dc74d860e"
|
AppID: "wxd391e40295bd9dfb"
|
||||||
AppSecret: "4de1fbf521712247542d49907fcd5dbf"
|
AppSecret: "f0fa74f7ed8c3c9953677465d44a4c0c"
|
||||||
WechatMini:
|
WechatMini:
|
||||||
AppID: "wx781abb66b3368963" # 小程序的AppID
|
# AppID: "wx5bacc94add2da981" # 小程序的AppID
|
||||||
AppSecret: "c7d02cdb0fc23c35c93187af9243b00d" # 小程序的AppSecret
|
# AppSecret: "48a2c1e8ff1b7d4c0ff82fbefa64d2d0" # 小程序的AppSecret
|
||||||
TycAppID: "wxe74617f3dd56c196"
|
# TYC
|
||||||
TycAppSecret: "c8207e54aef5689b2a7c1f91ed7ae8a0"
|
AppID: "wxe74617f3dd56c196"
|
||||||
|
AppSecret: "c8207e54aef5689b2a7c1f91ed7ae8a0"
|
||||||
Query:
|
Query:
|
||||||
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
||||||
AdminConfig:
|
AdminConfig:
|
||||||
@@ -79,4 +93,14 @@ Tianyuanapi:
|
|||||||
Timeout: 60
|
Timeout: 60
|
||||||
Authorization:
|
Authorization:
|
||||||
FileBaseURL: "https://www.tianyuancha.cn/api/v1/auth-docs" # 授权书文件访问基础URL
|
FileBaseURL: "https://www.tianyuancha.cn/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||||
|
Upload:
|
||||||
|
FileBaseURL: "https://www.tianyuancha.cn/api/v1/upload/file" # 上传图片访问基础 URL
|
||||||
|
TempFileMaxAgeH: 24 # 临时文件保留时长(小时),超时自动删除,0 表示默认 24
|
||||||
|
PublicBaseURL: "https://www.tianyuancha.cn"
|
||||||
ExtensionTime: 24 # 佣金解冻延迟时间,单位:24小时
|
ExtensionTime: 24 # 佣金解冻延迟时间,单位:24小时
|
||||||
|
|
||||||
|
|
||||||
|
tianxingjuhe:
|
||||||
|
url: "https://apis.tianapi.com"
|
||||||
|
key: "4ceffb1ffb95b83230b9a9c9df2467e1"
|
||||||
|
timeout: 30
|
||||||
@@ -16,9 +16,19 @@ VerifyCode:
|
|||||||
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
EndpointURL: "dysmsapi.aliyuncs.com"
|
EndpointURL: "dysmsapi.aliyuncs.com"
|
||||||
SignName: "天远查"
|
SignName: "海南海宇大数据"
|
||||||
TemplateCode: "SMS_302641455"
|
TemplateCode: "SMS_302641455"
|
||||||
ValidTime: 300
|
ValidTime: 300
|
||||||
|
Captcha:
|
||||||
|
# 建议与短信相同的 AccessKey,或单独为验证码创建子账号
|
||||||
|
AccessKeyID: "LTAI5tKGB3TVJbMHSoZN3yr9"
|
||||||
|
AccessKeySecret: "OCQ30GWp4yENMjmfOAaagksE18bp65"
|
||||||
|
# 验证码服务 Endpoint,国内一般为 captcha.cn-shanghai.aliyuncs.com
|
||||||
|
EndpointURL: "captcha.cn-shanghai.aliyuncs.com"
|
||||||
|
# 阿里云控制台中该场景的 SceneId,请替换为真实值
|
||||||
|
SceneID: "wynt39to"
|
||||||
|
# 验证码控制台中的 ekey(通常为 Base64 字符串),用于生成 EncryptedSceneId
|
||||||
|
EKey: ""
|
||||||
Encrypt:
|
Encrypt:
|
||||||
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
SecretKey: "ff83609b2b24fc73196aac3d3dfb874f"
|
||||||
WestConfig:
|
WestConfig:
|
||||||
@@ -31,24 +41,31 @@ YushanConfig:
|
|||||||
AcctID: "YSSJ843926726"
|
AcctID: "YSSJ843926726"
|
||||||
Url: "https://api.yushanshuju.com/credit-gw/service"
|
Url: "https://api.yushanshuju.com/credit-gw/service"
|
||||||
Alipay:
|
Alipay:
|
||||||
AppID: "2021004161631930"
|
AppID: "2021006121698606"
|
||||||
PrivateKey: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
|
PrivateKey: "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCx3TqaCUgZIMfG8Lu8fqkKqKLzONB9NBp4qCM2CAgxkUK1fd48XZ3ANDzfzNoeIbLbR/VL3ZUgyX/F8jO7Cit71JS58X9b1ngeRyBdaiBhLZgHb7Ngg0f1zDJuq8H6/i3Tiu9PaH0iOFKeaFCCpkakFyXjp//rqkcj2bPfa6lX0bD4YcILz8DC3iPy/cXHrHwaBryhYouYA3bKFROHykfF5D+tS2PZdR13BiDVElwDuquZCMdRdISfTw8snp5HSxA0TyRZIiGrtU3WvSKxYi2s8smZl/uYN1uj4w4GOivnTrkBdLy6DBqvgqRsCeYDDV5UyjtbAhvksZH60EWS3HTfAgMBAAECggEAK4dv+xjAa13pZpet6nC5ICGrV4kVBT9GJzdG/scycicRw2cdh3qFy+884qy4yN0Ib8AJmVqOT6rguWoQHPtdLv4Us/kVaT1wwkA3/ISnjgDhjxhYNwuKBe7GfO1OGQYx4u7CqJVy4ngUSC5RXdghu7Dqle+co2lV5cE20zv/Ar2nYo4p9fxcl/XAttqdRyby3ge6zJZP46Ru4CHzFzyUsrJYC575R3Jq68Zrr/+v0BmpEm/wAmoQE8W0ElMMH+Jw0twj+4l6PaUcq9oUVIL9wzl7ay1B6dKyxEOyinGYrmd65NzsTu8HhEpFdvQ+1O27XyahLsNWpSamJlV23ns/EQKBgQDd2UiQQRrj+itfa1/Zehy8MstEICw73LMDk129yNG9cU8U4y5vaTJkAcDTfme08B3uVVhsIUhJsFdgnh4ayyYW3jsB5BBBgszhwYQciH7f/3nE3rcX/TIs6yQAQkPhQi2RNSdn3SrCIhxgu9TwrZIDyQYJ6kk/9qEmZd93RrhOBwKBgQDNPpS1jFt4l08kzCHzfTvTbIu5z/6NPxmRSpcrdkoOAUfzlKSkRnKJuzkFSJEmWpFXILfNiza1dBYncg60Lu58yXpaGlLbVmxeFem+DdQm55lgl28d8Ssg6nlCTqE/tjB5LZ6qn8KEkWSj6gXH37pU1XFZh0A3+EIytnL7NnrsaQKBgGMaGUw3iSemLZHmiV7BKez4U80PAjOLl3xVbF7HQsp5v3X5NlkWiSgbkGPp57HwQa6h+Wn0RDKGz8GdYJ1fephklb92fbyGDbgblkSYxPSTT3Yed3QD61IdiGuFLoWF5o0jTYMcTWmDi2G7BpitMLj4J/Zt7mLgbYSVpYnG0bYpAoGAS7Tfya/CNdMqQFqD03rITI5nY9zS+mriFXO8Gy4A1vWmArU7ndTWfvNubwJ7d/hEUC0jX1AQmBH/8gDiZ5hAJAt1dDLtiTZxtqrCk3YqYUdgjf6N4C+LRxL2M30pgYTEkI5BTpKrf5bZ1pSGGVnvM0egDfQTvhF26ZnfA8buxLECgYAssWTLQ2Ou2NJ8N3HJx54kpKLwf218ulINsH6opcot22hbVfj0rc7lAcMe9FTGgmSgrbZG9QqAC2BpSHDeSC4C57iSYhG86tq/jwvNsV1miPV9RRj3CvwZkbHzCwqhRvCPS/QUgTzG3Z9pljYLkZjuMkb9jp814GbwEGPeoucefw=="
|
||||||
AlipayPublicKey: ""
|
AlipayPublicKey: ""
|
||||||
AppCertPath: "etc/merchant/appCertPublicKey_2021004161631930.crt"
|
AppCertPath: "etc/merchant/appCertPublicKey_2021006121698606.crt"
|
||||||
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
|
AlipayCertPath: "etc/merchant/alipayCertPublicKey_RSA2.crt"
|
||||||
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
|
AlipayRootCertPath: "etc/merchant/alipayRootCert.crt"
|
||||||
|
|
||||||
|
AppIDBak: "2021004161631930"
|
||||||
|
PrivateKeyBak: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCRrZNr8DNs4LhPSulTLEg4RLREWVSFGS+Nl5Q2FxQ8DgkUYV+p3kfi4XmB2W/Ruz4egPxEB0V/xj75OktVphVKY8rI6OaNnVoFVe5NqGa5MTj3wLwBIv/hMHA1VAru2KLIv9R1FR7LpWmreHSkpJ65CD2mZqYuMCekOfzMQZIGgSagEU4my0bLbFWw7M3qZz4vm2KUtm4Ew28OUJDkqygjPzXgS5l5niYQvqPjiQNdnTtoIcNcHo07tS8lmf/hdgq9EtVfY7Y0brESfgvOoVJeg1hTHEj0hyWnnWPeA4HD2izANP/5ObRX4ZVqpVju+7PSpbeFd71fxbR1blAVnrqTAgMBAAECggEASpkwHN3r9507xJ7/zG+oq+fCyB1WgrHbAA7W/rviyL4HOECE1F/XP/9mUXAfKq9PqB81D0EJ/dxu8wE/AqUB0g44EZnyNiKVrpXKakoKEFt8aKJxo8NgdNhxHV3kG1skQNi62xntoysZaY1NbeI+xVHLACMghhZytk5bfd02Ac3rMBz3X8Cl1R+3mgU0zFc5f476VRxywiRQM+QNJIaHDNB4vw1TKI0K92mEKD8lOuNZD7d5TCBZi3r08t8FFAkMjIMDiFvFRFmAqMg3NyaIGUkLVDU2zUP0Vlzmo9ghCV9hluqDqeP4RhxQydOw+rxGBk+crYQBhPyYOI/I9PFXAQKBgQDHSRRTPqYbCfztmwk3AIH7VN6izyU3FljEXAsdf+UVJpRa8429J3e+sB96jxhiwVlCzX4CDjsa/Pu0iQQx22a0AZs5GTE0MJ1FVydfGlyqF6/hRS4TswSkklW3be7/KDAjgj2+/wap+mN7rRmDkdvxgCJG6MiWuRAthhg/g16wIwKBgQC7Iu7D4yQXRKheL6p6pbMtE+oD58/EJ2vO8ZUz3LiPc9pZ6+bp4nkBP6JOuYiB5jkzWQifKe6hsXpv06kWzaBEzz4f4SUpWDmdBchNoct3pB/k66FaxHLO/pG4RV86hqscqTdutmdC62bbwM6yCtJ+3rS5rlCxDGQkGJbM+wM60QKBgH5nQyYeCbwC1NRdTzX883VYerLoEyHi4cEC5OX8NnD4/IbIDzJYc2KXUhAp7XzOSPDPaMqi/ih7KKh1dByvnnA0yKEp8oS5BThzNHzlOruEtMF9YOGL3jkIvKfRahOcCRSsyr94AWEVeb57qEBE5y5CaPtzMbAwiCtn779xc0DjAoGAZwEGXWokDm6rIhSoiJO2OQSyFW4+LSDptWHCF2bRa5yAPmiblHck1awaAa0b1yxKpdnG5hzljbirxOvDMZsDMXzFHDUICGbYZ3asVxbMcNE1AQM1sElbTFZRDRWaIhPIEaGOsnDSC8KYvjK1UsikLlMVNPMe1SUV5cxnDPLJR1ECgYEAw8M09uLylPtfGq7oyE2R6xC2kUA8EJ6aapJgUs/UZ6dtjtvudbYzUo0Cgnb12hpN3hfLc5O0/P4nRzZ72Hm43cMiRNLJi4BYCa0m/mCxq+RcoBWYQTIraHnR17yIQhxt5IBRVjgbvYCnryx5Jd5wjOvv7DdnGFJLepzSJwlGqeU="
|
||||||
|
AlipayPublicKeyBak: ""
|
||||||
|
AppCertPathBak: "etc/merchant/bak/appCertPublicKey_2021004161631930.crt"
|
||||||
|
AlipayCertPathBak: "etc/merchant/bak/alipayCertPublicKey_RSA2.crt"
|
||||||
|
AlipayRootCertPathBak: "etc/merchant/bak/alipayRootCert.crt"
|
||||||
IsProduction: true
|
IsProduction: true
|
||||||
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
|
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/alipay/callback"
|
||||||
ReturnURL: "https://www.tianyuancha.cn/payment/result"
|
ReturnURL: "https://www.tianyuancha.cn/payment/result"
|
||||||
Wxpay:
|
Wxpay:
|
||||||
AppID: "wxa581992dc74d860e"
|
AppID: "wxd391e40295bd9dfb"
|
||||||
MchID: "1682635136"
|
MchID: "1105276690"
|
||||||
MchCertificateSerialNumber: "5369B8AEEBDCF7AF274510252E6A8C0659C30F61"
|
MchCertificateSerialNumber: "4F1738D21CAEB7F76193A770CDBA6D7002ED1CFD"
|
||||||
MchApiv3Key: "e3ea4cf0765f1e71b01bb387dfcdbc9f"
|
MchApiv3Key: "K2d8F5gJ1sP7zQ3bN9mR4xV6c0hL5tU2"
|
||||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||||
MchPublicKeyID: "PUB_KEY_ID_0116826351362025060900382267001601"
|
MchPublicKeyID: "PUB_KEY_ID_0111052766902026050900112134001605"
|
||||||
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
MchPublicKeyPath: "etc/merchant/pub_key.pem"
|
||||||
MchPlatformRAS: "1FFEC3F62E31885FAB4C91ADCB8D7557E9488781"
|
MchPlatformRAS: "PUB_KEY_ID_0111052766902026050900112134001605"
|
||||||
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/wechat/callback"
|
NotifyUrl: "https://www.tianyuancha.cn/api/v1/pay/wechat/callback"
|
||||||
RefundNotifyUrl: "https://www.tianyuancha.cn/api/v1/wechat/refund_callback"
|
RefundNotifyUrl: "https://www.tianyuancha.cn/api/v1/wechat/refund_callback"
|
||||||
Applepay:
|
Applepay:
|
||||||
@@ -65,11 +82,14 @@ SystemConfig:
|
|||||||
ThreeVerify: true
|
ThreeVerify: true
|
||||||
CommissionSafeMode: false # 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
CommissionSafeMode: false # 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
||||||
WechatH5:
|
WechatH5:
|
||||||
AppID: "wxa581992dc74d860e"
|
AppID: "wxd391e40295bd9dfb"
|
||||||
AppSecret: "4de1fbf521712247542d49907fcd5dbf"
|
AppSecret: "f0fa74f7ed8c3c9953677465d44a4c0c"
|
||||||
WechatMini:
|
WechatMini:
|
||||||
AppID: "wx781abb66b3368963" # 小程序的AppID
|
# AppID: "wx5bacc94add2da981" # 小程序的AppID
|
||||||
AppSecret: "c7d02cdb0fc23c35c93187af9243b00d" # 小程序的AppSecret
|
# AppSecret: "48a2c1e8ff1b7d4c0ff82fbefa64d2d0" # 小程序的AppSecret
|
||||||
|
# TYC
|
||||||
|
AppID: "wxe74617f3dd56c196"
|
||||||
|
AppSecret: "c8207e54aef5689b2a7c1f91ed7ae8a0"
|
||||||
Query:
|
Query:
|
||||||
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒
|
||||||
AdminConfig:
|
AdminConfig:
|
||||||
@@ -86,6 +106,15 @@ Tianyuanapi:
|
|||||||
Key: "74902aff197d72d1caa5593560cb281e"
|
Key: "74902aff197d72d1caa5593560cb281e"
|
||||||
BaseURL: "https://api.tianyuanapi.com"
|
BaseURL: "https://api.tianyuanapi.com"
|
||||||
Timeout: 60
|
Timeout: 60
|
||||||
|
tianxingjuhe:
|
||||||
|
url: "https://apis.tianapi.com"
|
||||||
|
key: "4ceffb1ffb95b83230b9a9c9df2467e1"
|
||||||
|
timeout: 30
|
||||||
Authorization:
|
Authorization:
|
||||||
FileBaseURL: "https://www.tianyuancha.cn/api/v1/auth-docs" # 授权书文件访问基础URL
|
FileBaseURL: "https://www.tianyuancha.cn/api/v1/auth-docs" # 授权书文件访问基础URL
|
||||||
|
Upload:
|
||||||
|
FileBaseURL: "https://www.tianyuanfcha.cn/api/v1/upload/file" # 上传图片访问基础 URL(行驶证等)
|
||||||
|
TempFileMaxAgeH: 24
|
||||||
|
|
||||||
|
PublicBaseURL: "https://www.tianyuancha.cn"
|
||||||
ExtensionTime: 24 # 佣金解冻延迟时间,单位:24小时
|
ExtensionTime: 24 # 佣金解冻延迟时间,单位:24小时
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDsjCCApqgAwIBAgIQICYBJSGeFVvaAxyDb0s3iTANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
|
MIIDsjCCApqgAwIBAgIQICYBIfYzsX0sOqARgYshuDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
|
||||||
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
|
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
|
||||||
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
|
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
|
||||||
YXNzIDIgUjEwHhcNMjYwMTI1MDc1MTEyWhcNMzEwMTI0MDc1MTEyWjCBkjELMAkGA1UEBhMCQ04x
|
YXNzIDIgUjEwHhcNMjYwMTIxMDk0NDI2WhcNMzEwMTIwMDk0NDI2WjCBkjELMAkGA1UEBhMCQ04x
|
||||||
LTArBgNVBAoMJOW5v+ilv+emj+mTree9kee7nOenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG
|
LTArBgNVBAoMJOW5v+ilv+emj+mTree9kee7nOenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG
|
||||||
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
|
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
|
||||||
uC0yMDg4ODQxMzMwNzE0NjM4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomw6g4rB
|
uC0yMDg4MjUxNjk3MTI0MTM5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhzfJRbBY
|
||||||
mCr/QoX3NI3DVLyDpkaUytZ2uFhdfQaegIDAuUfZfgpTCASlAtO82t8ISAbSOSyp9CUpwdGV4EYO
|
47FJpj2SQ2I4u1Z3KdyrtPG+jfX+wrVoubzVNC5f6AoULEeJcmDDXwj1t7Arz7F7F9Xbrn7JoSsn
|
||||||
iCBbLxMYB6taaHPiIjJ1zNT1EakJzWgU53hz1AVeABB9kdAvMqSvjH6KLoVupmqm4Li8ZwDW9M2A
|
x6nuxVW8XXK/J9emy/SK7LS+Oofe2vwwvRSos8iYleYFYDYlYQB/+kK/Zsn9Qv4M5SfpWK7nU7AD
|
||||||
NAmyDfKgiF0Lt4aUUnaZktoCrTWTkpmtfRZCHNACj851IllvN2wyC4OL7dJq5UzOFxmn07Dy/2z4
|
A4lvaV7VCszSYYFsqtZ1uEdqJIpUdTuR3IxM29AE8oqSpAV1OIhlLGN7NXsRrl3kjYijxnmwUEcl
|
||||||
UAhaaSAyRVawpOui5AIYJTXZERLYL3KMyRnMuZoFq3xltzVTzRPM06nRa9RfeVNVwWVtGBIe/r8t
|
2ODJnIHF3oMmCdMU3hMr14CsEd95VVfVsc3UR2VxEylTO0P0ZZfRi17P9KI3aWNaNVLcFcYvpBkM
|
||||||
cg5wyhI57KUszGNOmUIm/se6G2lnAQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
|
/8GawCmi0aWkPRgw9SinJtjh8x082QIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
|
||||||
AQELBQADggEBAFWBZ6o+6o1l0HRt3ZzMcFb9IqeWslnmbaE8bfvnk1qaxQmSKQGuTZOkK9jigXs5
|
AQELBQADggEBAISexDU5a00sssj4Oy4atS2kcUktdloapzp8w8t37Svd5iCSOtcN9qONmBTKjybs
|
||||||
APgs0o5Wtoxbg1+zKbXRjL1+l7l1zEvMtx3goUruXoEPTgBNpJPh0JAwWaX0r+9H1SgOK+o3zbHD
|
zEHF4LJZRaum/V3i2WHXDX3WzJBhvmhk+Ryyp5dr+IiA3qR1+jdkQrHJVPD5phvrsk9Nj9Fw4jCW
|
||||||
xRjfAYYPbJeIyta8Rkvu2m7U5lVxQlDMZREpslAMZXLvbn0KP9OGwEoYqJNmklCncY4VpxpBYEci
|
3tSQLD7Y2oVPymiKhIRisoUNg39SHpAMak283KV7vNR9SRdUqTEJLmN7C5WasZ68X+6ZY0a/+3T6
|
||||||
cSairY4cFp8TSifV4jYkkG5VAWQLOjOFoi9n404ZKON2s1+R/uBDOrGelivu4h/rJA0en7aZivCG
|
dDKIfFqztBYtdZ2CJs27fw6WBr8/zV64fkk0XinxiOhRQdF5D+GgtDhwa+d6I8QtNJTL+FBZgkFZ
|
||||||
UQVfym1r1HyaZLAdxWBOGY22NaEcJmQGjKe2uEdNNgvp7kFmOmE=
|
H59XbYUpUd8BCfjuNImlXHxAGw65LoafjQR7Dbi11eghynqdcOU=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
|
MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
|
||||||
|
|||||||
BIN
app/main/api/etc/merchant/apiclient_cert.p12
Normal file
BIN
app/main/api/etc/merchant/apiclient_cert.p12
Normal file
Binary file not shown.
25
app/main/api/etc/merchant/apiclient_cert.pem
Normal file
25
app/main/api/etc/merchant/apiclient_cert.pem
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEKDCCAxCgAwIBAgIUTxc40hyut/dhk6dwzbptcALtHP0wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
|
||||||
|
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
|
||||||
|
Q0EwHhcNMjYwNTA5MDU0MTQyWhcNMzEwNTA4MDU0MTQyWjCBgTETMBEGA1UEAwwK
|
||||||
|
MTEwNTI3NjY5MDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL
|
||||||
|
DCTlub/opb/npo/pk63nvZHnu5znp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYT
|
||||||
|
AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||||
|
AQoCggEBANOAJ29Xxux4wzqDqkOWa/dqwJNt/WkK8vecWhbKSLLVDLBYcMSaklU2
|
||||||
|
Cw5pHGeNZSF9uB+8T7mGzrmhDg2643hcrBN60EMDEQvmtQp8ftkTl8rpToOTZ3yT
|
||||||
|
qlSW1BdCFKgiZJc3uel0q83kAqlnXz/AQ8lFp967Zjmp0DVGO1Aj4ihIcVL3njM8
|
||||||
|
xvNwVe+E1aCKb2T07M1MoECtUC3+U/ypD2Rl17FJHaaTaLlr0buTvvHwRNbDCZHb
|
||||||
|
MRcwQoVNJZyX3cq6+AZqzDSyBBlYzFWVw1jgI3QtdqRXAADJGUv8dddUDrTK//O9
|
||||||
|
gP8XQ5M70TXnovTtZDXt6JyggTDMyq8CAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG
|
||||||
|
A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu
|
||||||
|
aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC
|
||||||
|
MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB
|
||||||
|
OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQAqmiey
|
||||||
|
BVEG0EZVTsb41feExEANDiKAc1gL/coqZRqf4+zWHX8tARshK+HsgEM2d+pOkGAW
|
||||||
|
XK6T79gqUyXLaO1dwVNeg4zaPGQaTddGvwExkyBvxMSBggGqDNdoXc2DPJQsLFGL
|
||||||
|
0q7JPvzEFlupjlk1SM9XWJ24KzWBl1HZZPQASBmi/O4CoHTqy5b4gqi7EDzYN4gz
|
||||||
|
kNFwuU7eTKEPlqbgPY4beDsCMvF4NUu1YSo3hUJz9vKKIL9xIXw1Snf2YbwlVJyl
|
||||||
|
sjtzndM+oxfc5VvoGC96xILWP8yEgKadsPZrcO7J4JRv3eqU24pNgQnqtWQ5ghiJ
|
||||||
|
nCl6uF83dT49XUlC
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
-----BEGIN PRIVATE KEY-----
|
||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCP6fWm1vXXybH
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTgCdvV8bseMM6
|
||||||
m3Ne6PjacGrN2+iMrzWZlzdHCZ31udDPqSUYaZ+78b441KZK/CJFQWeSJ/1h//A+
|
g6pDlmv3asCTbf1pCvL3nFoWykiy1QywWHDEmpJVNgsOaRxnjWUhfbgfvE+5hs65
|
||||||
BGsQDKvE/fj2QzN1KkOuQ8WJXNGpixE5uu5bv/QTN/ukurGdA1aO2aFCANumlOmB
|
oQ4NuuN4XKwTetBDAxEL5rUKfH7ZE5fK6U6Dk2d8k6pUltQXQhSoImSXN7npdKvN
|
||||||
HkB/B2so57ii8iQQjwK2xM4r3oOU/IfcFGKL+9/QjLGFFp9PJXCDBCgrxxlZGaj1
|
5AKpZ18/wEPJRafeu2Y5qdA1RjtQI+IoSHFS954zPMbzcFXvhNWgim9k9OzNTKBA
|
||||||
3wowlfVOzlaX94gemQsCYVkuAFIYMAnFHs9cKNZQIU80somW/yy2Gy38N6n7NnbD
|
rVAt/lP8qQ9kZdexSR2mk2i5a9G7k77x8ETWwwmR2zEXMEKFTSWcl93KuvgGasw0
|
||||||
nvFSaq4GoDROqRgKbRZ5e706d/p7A3aS/2oRqq1jomUIugK8g++LmoHFTgfhfQkI
|
sgQZWMxVlcNY4CN0LXakVwAAyRlL/HXXVA60yv/zvYD/F0OTO9E156L07WQ17eic
|
||||||
v1aG/nPzAgMBAAECggEAD2RN31J2J42xm/V0YdviBCUOQXugZK1peN8jkSxw6Myt
|
oIEwzMqvAgMBAAECggEAB3o3arNSwBeA53eyNFOEG6o1hsDTvbv6XRC1Cqs6KMBp
|
||||||
gBbuCo4sCw9vvD8VYjGyYXx6QXmLuV03YyKkfSQT5EsflBvlEu6jaEaUe3rwXhfX
|
g26NBhZk6AhSGb9TlsGj+qwkL1MMCoYelud/xLJ/ykiUeNfVtxjLqH0Ol4FRRsFv
|
||||||
6JQoWPrP00oHVZk5g7CFBlK2VW2N+hgonIOSJr6mvhoGZlr7gphiZasYjx9Vm9N3
|
k2fuM66pcPkTg4wVkYDvlAYrQkFhHETYsh3GjlCuSGW2v83rom9DrPQ4/TkvbaTW
|
||||||
Pbnfru5ttzplYNniwH3DF6ph8VmdbD1nnbWSKLXvHCsXQT2wBcnsIagIH3vyq6K1
|
md3at3pfEN/UPhL6jYnIEYfZoFFrE3aMCCAc5v2VUMZlX8R6ziK+CXyAQHegzGFH
|
||||||
pc5abWsQJrixOPebpI8jD5w0HxHAqVLx58H/OC2zW/roAw1WS2AkueJ1j7dQ7Z0C
|
zJp6XDvxuVjo/axD4LvKCvpzZIO9DUo1J5NA3/tar60AYCjMF9vBpkWRSwwekNbX
|
||||||
mc9Xexz5gcAP0nMAQv+LP7iYqsa/niFhfcTFWfdxkQKBgQD5JkKNmInU2/IVYCwO
|
lT1/r2/iRDhNyHWSz7Wo0CnrhNhRfV4HB6p8pzZIUQKBgQD/9+LvfFuLRqIEh/R8
|
||||||
c483MCSv1+MnbRXlb7vut8T0IupHTU6hCge6C3q3HsjbKSBn8bRChtPUzvw9JFxK
|
wNaZTWRDkp7paSc9kN7YXrKDkn20iDV4huk2Z+fL53Asvu5jMJzWJD7QU9bnasdE
|
||||||
QWKiQqQDPLDJ08AIKhfQD2JiLtoikkZN0bF6OTL+Soney1yGx51mlfHM194+PcCJ
|
fL9oUGOldqj15mpi+2dpLwwSnkNIah3Nxwr/DmCEKq6YjzRDIw1QWPjCe+2NgFTD
|
||||||
jF7iWdMVbcBwHbgydNxxIS5cKQKBgQDHlvQ4lw6gvLILpGK494/vNYSJP/Jmd66V
|
GACilmpw2G0ZsI3CgOenqLCVmQKBgQDThtuqIS3bivHr3QprMKHni9aaSm5jMnfd
|
||||||
3oSGYi84YRKTSwH4NlbBVVieb3Dv+pPugbsXEuFHBif7WsivbYgNTE9++8Yvt0gh
|
eYqD12sop6HkSetdOCAFttJP7J+wegpvUwdPdgvL8A2ICNwrjfL4i3V6LtJOBuRo
|
||||||
duB1G4yh7m/ylQeSuipgQU9tozrU/15cWwmcCRV50wWXBGoVEM0kf7mzEKSxmjYk
|
lbl8LHTegVQFd2nOXe57VtmxzADpmEao6rzc+IpYamFaRl8kvswx2MBL/c20vAKF
|
||||||
Qzko/zxSuwKBgQCY6Bc+SViFz3qSDdTcBaXma+CIHsmlH7ipd9px1kzEvEzl95cD
|
moE9OY9/hwKBgQCE9UQh1dzPWZ8q71vluV0/QF4GY5C7+Wzyo9+9UGM1yNAXCHGN
|
||||||
FGHLl1H34qfIgUQHJvrHPXHyEBoT+CW/2MMM7DM2XV/ubctT92ln4pkxwqlTQExv
|
67YUFqDm3477DypQx52P9j/SgtosVuCvCIV8L9WyyxI0uL0mMSSkFFYXDbIxWT2l
|
||||||
Y/s1FLesAtj8Z/hgK0/5bprYab9WmZV5lTGCXzhB1XqeFE9AgCHuODv4iQKBgQC8
|
9/AUUGl9/ZW3lRs2jGyMAcslaq4YRELDHCKoUa10dFcncvgPwlmSUyN2cQKBgC/p
|
||||||
g2uwd5ytXQydymokYk9klJvWNrvw5GHV1BJAC0Smb6lnzZTSqCBRAxdsrb1yLK7E
|
40YVP+rz/TqdFigTmwj07waCB6EmpGohhtO4bwiFeDFa4Rp7hR1mPRtnkQCLlGOF
|
||||||
u2vGY2K7/qiM1DZw23eBd+4t9gg+0VIjqXBfq+GsoNTDvtckUwnrWER5PY831ut9
|
HinMpn1qgdYnk5+2BqxwAotLoc2U2BYsnBhZhZuFFgAq/WeGdWew05znhelj2dWX
|
||||||
N89fvYS3SAUjmlvIAdKBAtKWusWTqiAxJ/05J7oGOQKBgB5PSr5i0LlupIbKui9t
|
XnJ7RfMjHawyNoj/QwejfmN4OSlQUQ3sYov4xt1vAoGBAKEq1xI1J1p/t+4j6Esc
|
||||||
XtXnRqGPxxrZZUpTkyrGOAnlCz/zq2QiwFpBWo/NMHOp0KmxzJpQ8yEY2LWlRZ61
|
frDvu+xW8IJSMHGNBz1Z4xA5KX2DmKH+aCjzTHMO6CJ353RH/2TNP44S5bHobAzK
|
||||||
Oc9m0J/HtPw3Ohi1treBosEVG/0NOI9Tq1Obny23N51MVibdW6zEIyGUp/DbFS8h
|
GqNisApHHCMf7Q+R1dOdXhiAKa1Cm6CUztfDXr2mTEcyqkrVAqTMrP2TnX9cuRVE
|
||||||
5DljdOYX9IYIHHn3Ig4GeTGe
|
xAft+Nal9d1gfPoGL5AcXLHI
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDsjCCApqgAwIBAgIQICYBIfYzsX0sOqARgYshuDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
|
MIIDsjCCApqgAwIBAgIQICYBJSGeFVvaAxyDb0s3iTANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
|
||||||
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
|
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
|
||||||
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
|
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
|
||||||
YXNzIDIgUjEwHhcNMjYwMTIxMDk0NDI2WhcNMzEwMTIwMDk0NDI2WjCBkjELMAkGA1UEBhMCQ04x
|
YXNzIDIgUjEwHhcNMjYwMTI1MDc1MTEyWhcNMzEwMTI0MDc1MTEyWjCBkjELMAkGA1UEBhMCQ04x
|
||||||
LTArBgNVBAoMJOW5v+ilv+emj+mTree9kee7nOenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG
|
LTArBgNVBAoMJOW5v+ilv+emj+mTree9kee7nOenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG
|
||||||
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
|
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
|
||||||
uC0yMDg4MjUxNjk3MTI0MTM5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhzfJRbBY
|
uC0yMDg4ODQxMzMwNzE0NjM4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomw6g4rB
|
||||||
47FJpj2SQ2I4u1Z3KdyrtPG+jfX+wrVoubzVNC5f6AoULEeJcmDDXwj1t7Arz7F7F9Xbrn7JoSsn
|
mCr/QoX3NI3DVLyDpkaUytZ2uFhdfQaegIDAuUfZfgpTCASlAtO82t8ISAbSOSyp9CUpwdGV4EYO
|
||||||
x6nuxVW8XXK/J9emy/SK7LS+Oofe2vwwvRSos8iYleYFYDYlYQB/+kK/Zsn9Qv4M5SfpWK7nU7AD
|
iCBbLxMYB6taaHPiIjJ1zNT1EakJzWgU53hz1AVeABB9kdAvMqSvjH6KLoVupmqm4Li8ZwDW9M2A
|
||||||
A4lvaV7VCszSYYFsqtZ1uEdqJIpUdTuR3IxM29AE8oqSpAV1OIhlLGN7NXsRrl3kjYijxnmwUEcl
|
NAmyDfKgiF0Lt4aUUnaZktoCrTWTkpmtfRZCHNACj851IllvN2wyC4OL7dJq5UzOFxmn07Dy/2z4
|
||||||
2ODJnIHF3oMmCdMU3hMr14CsEd95VVfVsc3UR2VxEylTO0P0ZZfRi17P9KI3aWNaNVLcFcYvpBkM
|
UAhaaSAyRVawpOui5AIYJTXZERLYL3KMyRnMuZoFq3xltzVTzRPM06nRa9RfeVNVwWVtGBIe/r8t
|
||||||
/8GawCmi0aWkPRgw9SinJtjh8x082QIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
|
cg5wyhI57KUszGNOmUIm/se6G2lnAQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
|
||||||
AQELBQADggEBAISexDU5a00sssj4Oy4atS2kcUktdloapzp8w8t37Svd5iCSOtcN9qONmBTKjybs
|
AQELBQADggEBAFWBZ6o+6o1l0HRt3ZzMcFb9IqeWslnmbaE8bfvnk1qaxQmSKQGuTZOkK9jigXs5
|
||||||
zEHF4LJZRaum/V3i2WHXDX3WzJBhvmhk+Ryyp5dr+IiA3qR1+jdkQrHJVPD5phvrsk9Nj9Fw4jCW
|
APgs0o5Wtoxbg1+zKbXRjL1+l7l1zEvMtx3goUruXoEPTgBNpJPh0JAwWaX0r+9H1SgOK+o3zbHD
|
||||||
3tSQLD7Y2oVPymiKhIRisoUNg39SHpAMak283KV7vNR9SRdUqTEJLmN7C5WasZ68X+6ZY0a/+3T6
|
xRjfAYYPbJeIyta8Rkvu2m7U5lVxQlDMZREpslAMZXLvbn0KP9OGwEoYqJNmklCncY4VpxpBYEci
|
||||||
dDKIfFqztBYtdZ2CJs27fw6WBr8/zV64fkk0XinxiOhRQdF5D+GgtDhwa+d6I8QtNJTL+FBZgkFZ
|
cSairY4cFp8TSifV4jYkkG5VAWQLOjOFoi9n404ZKON2s1+R/uBDOrGelivu4h/rJA0en7aZivCG
|
||||||
H59XbYUpUd8BCfjuNImlXHxAGw65LoafjQR7Dbi11eghynqdcOU=
|
UQVfym1r1HyaZLAdxWBOGY22NaEcJmQGjKe2uEdNNgvp7kFmOmE=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
|
MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
-----BEGIN PUBLIC KEY-----
|
-----BEGIN PUBLIC KEY-----
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwSy7dS/ICZV38tI0HxM
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4/k/xM+ybCzcWPi73Es5
|
||||||
SAIE7+Ug92qryuNlkNyaNDRjfsykHsrPCSsUUQEZblBNmZOLfLQxmAaWC+cQqWCv
|
XHPPqR2oGFdI7TPvbSnNcCpUAdewiuLFJZ3x7/6uTPKRuaxJPA9n0wjQ9x3Crwpt
|
||||||
zfy4rXGAHE1widWFkHGzQzaw6cB0VdDXatK9yAt1PgXdp5jzBRzOn9Z3u4t0s771
|
t+XZW8+gHU8kq1vG1cg+oBZedyS3434i+FCovK8iFQnsr5W+ROtVq8pxys0w5P3Z
|
||||||
2zjuxCnLxMq84DovNgh2y0LBiuorWbtuTFTd8SXUGk2Jyuojq/02U3KTuyh+7SmW
|
rYsdTZ7TxQ/3h/5hd7poehFUIIX1ZgO8tjCqq9wsvb70FRn4hf8NvyuxMtg+Va8U
|
||||||
ffJXKrzhrKwSpGh59e/fFxqX2xGlVoJ1kdohMZPo/7k+e5jP7qjrf93l7JVeUKYa
|
XdvDiUCa8NeCkvxpfPjEoZTNPsNbEYyYEJ1NSlte/5bLUBKm0rU6QGITuzXdPcF/
|
||||||
V27hNVowJ4oho21WVCJ1AYo41IbPJWI+6WxlaVeoR4zKix0Mb2timaWayyLoN53y
|
R5PweiMXFNy0l5A4eNFyO4I0evC36mB58geZ2rgdq92LZMQQTS6Rf63AtC2ECs5B
|
||||||
aQIDAQAB
|
wQIDAQAB
|
||||||
-----END PUBLIC KEY-----
|
-----END PUBLIC KEY-----
|
||||||
|
|||||||
@@ -11,20 +11,25 @@ type Config struct {
|
|||||||
CacheRedis cache.CacheConf
|
CacheRedis cache.CacheConf
|
||||||
JwtAuth JwtAuth // JWT 鉴权相关配置
|
JwtAuth JwtAuth // JWT 鉴权相关配置
|
||||||
VerifyCode VerifyCode
|
VerifyCode VerifyCode
|
||||||
|
Captcha CaptchaConfig
|
||||||
Encrypt Encrypt
|
Encrypt Encrypt
|
||||||
Alipay AlipayConfig
|
Alipay AlipayConfig
|
||||||
Wxpay WxpayConfig
|
Wxpay WxpayConfig
|
||||||
Applepay ApplepayConfig
|
Applepay ApplepayConfig
|
||||||
Ali AliConfig
|
Ali AliConfig
|
||||||
Tianyuanapi TianyuanapiConfig
|
Tianyuanapi TianyuanapiConfig
|
||||||
|
Tianxingjuhe TianxingjuheConfig
|
||||||
SystemConfig SystemConfig
|
SystemConfig SystemConfig
|
||||||
WechatH5 WechatH5Config
|
WechatH5 WechatH5Config
|
||||||
Authorization AuthorizationConfig // 授权书配置
|
Authorization AuthorizationConfig // 授权书配置
|
||||||
|
Upload UploadConfig // 图片上传(行驶证等)配置
|
||||||
WechatMini WechatMiniConfig
|
WechatMini WechatMiniConfig
|
||||||
Query QueryConfig
|
Query QueryConfig
|
||||||
AdminConfig AdminConfig
|
AdminConfig AdminConfig
|
||||||
AdminPromotion AdminPromotion
|
AdminPromotion AdminPromotion
|
||||||
TaxConfig TaxConfig
|
TaxConfig TaxConfig
|
||||||
|
// PublicBaseURL 前台访问域名,用于构造对外可访问链接(上传图片、回调等),例如 https://www.tianyuancha.cn
|
||||||
|
PublicBaseURL string
|
||||||
ExtensionTime int64
|
ExtensionTime int64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +47,13 @@ type VerifyCode struct {
|
|||||||
TemplateCode string
|
TemplateCode string
|
||||||
ValidTime int
|
ValidTime int
|
||||||
}
|
}
|
||||||
|
type CaptchaConfig struct {
|
||||||
|
AccessKeyID string
|
||||||
|
AccessKeySecret string
|
||||||
|
EndpointURL string
|
||||||
|
SceneID string
|
||||||
|
EKey string
|
||||||
|
}
|
||||||
type Encrypt struct {
|
type Encrypt struct {
|
||||||
SecretKey string
|
SecretKey string
|
||||||
}
|
}
|
||||||
@@ -53,6 +65,13 @@ type AlipayConfig struct {
|
|||||||
AppCertPath string // 应用公钥证书路径
|
AppCertPath string // 应用公钥证书路径
|
||||||
AlipayCertPath string // 支付宝公钥证书路径
|
AlipayCertPath string // 支付宝公钥证书路径
|
||||||
AlipayRootCertPath string // 根证书路径
|
AlipayRootCertPath string // 根证书路径
|
||||||
|
// Bak 仅用于 [2026-01-25 16:38:17, 2026-02-02 18:26) 区间内订单退款
|
||||||
|
AppIDBak string
|
||||||
|
PrivateKeyBak string
|
||||||
|
AlipayPublicKeyBak string
|
||||||
|
AppCertPathBak string
|
||||||
|
AlipayCertPathBak string
|
||||||
|
AlipayRootCertPathBak string
|
||||||
IsProduction bool
|
IsProduction bool
|
||||||
NotifyUrl string
|
NotifyUrl string
|
||||||
ReturnURL string
|
ReturnURL string
|
||||||
@@ -129,3 +148,16 @@ type TianyuanapiConfig struct {
|
|||||||
type AuthorizationConfig struct {
|
type AuthorizationConfig struct {
|
||||||
FileBaseURL string // 授权书文件访问基础URL
|
FileBaseURL string // 授权书文件访问基础URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadConfig 图片上传(行驶证等)配置,临时存储,按 hash 去重
|
||||||
|
type UploadConfig struct {
|
||||||
|
FileBaseURL string `json:"fileBaseURL,omitempty"` // 上传文件访问基础 URL,如 https://xxx/api/v1/upload/file
|
||||||
|
TempFileMaxAgeH int `json:"tempFileMaxAgeH,omitempty"` // 临时文件保留时长(小时),超时删除,0 表示默认 24 小时
|
||||||
|
}
|
||||||
|
|
||||||
|
// TianxingjuheConfig 天行聚合API配置
|
||||||
|
type TianxingjuheConfig struct {
|
||||||
|
URL string // API基础URL
|
||||||
|
Key string // API密钥
|
||||||
|
Timeout int // 超时时间(秒),默认30秒
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"tyc-server/app/main/api/internal/logic/auth"
|
"tyc-server/app/main/api/internal/logic/auth"
|
||||||
@@ -23,7 +24,8 @@ func SendSmsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
result.ParamValidateErrorResult(r, w, err)
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := auth.NewSendSmsLogic(r.Context(), svcCtx)
|
ctx := context.WithValue(r.Context(), auth.UserAgentContextKey, r.Header.Get("User-Agent"))
|
||||||
|
l := auth.NewSendSmsLogic(ctx, svcCtx)
|
||||||
err := l.SendSms(&req)
|
err := l.SendSms(&req)
|
||||||
result.HttpResult(r, w, nil, err)
|
result.HttpResult(r, w, nil, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/logic/captcha"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEncryptedSceneIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := captcha.NewGetEncryptedSceneIdLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GetEncryptedSceneId()
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,10 +22,14 @@ import (
|
|||||||
app "tyc-server/app/main/api/internal/handler/app"
|
app "tyc-server/app/main/api/internal/handler/app"
|
||||||
auth "tyc-server/app/main/api/internal/handler/auth"
|
auth "tyc-server/app/main/api/internal/handler/auth"
|
||||||
authorization "tyc-server/app/main/api/internal/handler/authorization"
|
authorization "tyc-server/app/main/api/internal/handler/authorization"
|
||||||
|
captcha "tyc-server/app/main/api/internal/handler/captcha"
|
||||||
notification "tyc-server/app/main/api/internal/handler/notification"
|
notification "tyc-server/app/main/api/internal/handler/notification"
|
||||||
pay "tyc-server/app/main/api/internal/handler/pay"
|
pay "tyc-server/app/main/api/internal/handler/pay"
|
||||||
product "tyc-server/app/main/api/internal/handler/product"
|
product "tyc-server/app/main/api/internal/handler/product"
|
||||||
query "tyc-server/app/main/api/internal/handler/query"
|
query "tyc-server/app/main/api/internal/handler/query"
|
||||||
|
tianyuan "tyc-server/app/main/api/internal/handler/tianyuan"
|
||||||
|
toolbox "tyc-server/app/main/api/internal/handler/toolbox"
|
||||||
|
upload "tyc-server/app/main/api/internal/handler/upload"
|
||||||
user "tyc-server/app/main/api/internal/handler/user"
|
user "tyc-server/app/main/api/internal/handler/user"
|
||||||
"tyc-server/app/main/api/internal/svc"
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
|
||||||
@@ -952,6 +956,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
rest.WithPrefix("/api/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// get encrypted scene id for aliyun captcha
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/captcha/encryptedSceneId",
|
||||||
|
Handler: captcha.GetEncryptedSceneIdHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
@@ -1148,6 +1164,54 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
rest.WithPrefix("/api/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 天远车辆类接口异步回调
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/tianyuan/vehicle/callback",
|
||||||
|
Handler: tianyuan.VehicleCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 获取工具列表
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/toolbox/list",
|
||||||
|
Handler: toolbox.ToolboxListHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 通用工具查询
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/toolbox/query",
|
||||||
|
Handler: toolbox.ToolboxQueryHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 访问已上传文件(供第三方或前端通过返回的 URL 拉取)
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/upload/file/:fileName",
|
||||||
|
Handler: upload.ServeUploadedFileHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 上传图片,返回可访问 URL(如行驶证);限制 3MB
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/upload/image",
|
||||||
|
Handler: upload.UploadImageHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package tianyuan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
tianyuanlogic "tyc-server/app/main/api/internal/logic/tianyuan"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VehicleCallbackHandler 天远车辆类接口异步回调入口
|
||||||
|
// 约定:第三方在回调 URL 上携带 order_no / api_id 等标识,例如:/api/v1/tianyuan/vehicle/callback?order_no=Q_xxx&api_id=QCXG1U4U
|
||||||
|
// 回调 Body 为该接口最终的 JSON 结果。
|
||||||
|
func VehicleCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := tianyuanlogic.NewVehicleCallbackLogic(r.Context(), svcCtx)
|
||||||
|
if err := l.Handle(r); err != nil {
|
||||||
|
// 对第三方尽量返回 200,避免无限重试,这里使用统一 result 封装错误
|
||||||
|
result.HttpResult(r, w, map[string]interface{}{
|
||||||
|
"code": 500,
|
||||||
|
"msg": "fail",
|
||||||
|
}, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpx.OkJson(w, map[string]interface{}{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/main/api/internal/handler/toolbox/toolboxlisthandler.go
Normal file
17
app/main/api/internal/handler/toolbox/toolboxlisthandler.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package toolbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/logic/toolbox"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToolboxListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := toolbox.NewToolboxListLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ToolboxList()
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/main/api/internal/handler/toolbox/toolboxqueryhandler.go
Normal file
29
app/main/api/internal/handler/toolbox/toolboxqueryhandler.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package toolbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
"tyc-server/app/main/api/internal/logic/toolbox"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
"tyc-server/pkg/lzkit/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToolboxQueryHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ToolboxQueryReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
result.ParamErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := validator.Validate(req); err != nil {
|
||||||
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := toolbox.NewToolboxQueryLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ToolboxQuery(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package upload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/logic/upload"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
"tyc-server/pkg/lzkit/validator"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServeUploadedFileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ServeUploadedFileReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
result.ParamErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := validator.Validate(req); err != nil {
|
||||||
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := upload.NewServeUploadedFileLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ServeUploadedFile(&req)
|
||||||
|
if err != nil {
|
||||||
|
result.HttpResult(r, w, nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp != nil && resp.FilePath != "" {
|
||||||
|
f, openErr := os.Open(resp.FilePath)
|
||||||
|
if openErr != nil {
|
||||||
|
result.HttpResult(r, w, nil, openErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
w.Header().Set("Content-Type", resp.ContentType)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = f.WriteTo(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpx.OkJson(w, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/main/api/internal/handler/upload/uploadimagehandler.go
Normal file
29
app/main/api/internal/handler/upload/uploadimagehandler.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package upload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
"tyc-server/app/main/api/internal/logic/upload"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
"tyc-server/pkg/lzkit/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UploadImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.UploadImageReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
result.ParamErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := validator.Validate(req); err != nil {
|
||||||
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := upload.NewUploadImageLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.UploadImage(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"tyc-server/app/main/api/internal/logic/user"
|
"tyc-server/app/main/api/internal/logic/user"
|
||||||
"tyc-server/app/main/api/internal/svc"
|
"tyc-server/app/main/api/internal/svc"
|
||||||
"tyc-server/app/main/api/internal/types"
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
jwtx "tyc-server/common/jwt"
|
||||||
"tyc-server/common/result"
|
"tyc-server/common/result"
|
||||||
"tyc-server/pkg/lzkit/validator"
|
"tyc-server/pkg/lzkit/validator"
|
||||||
|
|
||||||
@@ -23,7 +26,14 @@ func WxH5AuthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|||||||
result.ParamValidateErrorResult(r, w, err)
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l := user.NewWxH5AuthLogic(r.Context(), svcCtx)
|
ctx := r.Context()
|
||||||
|
// 本路由未挂 AuthInterceptor;若前端已带登录态(如手机号登录后再走静默授权),需解析 JWT 以便把 openid 写入当前正式用户 user_auth
|
||||||
|
if authHeader := strings.TrimSpace(r.Header.Get("Authorization")); authHeader != "" {
|
||||||
|
if claims, err := jwtx.ParseJwtToken(authHeader, svcCtx.Config.JwtAuth.AccessSecret); err == nil {
|
||||||
|
ctx = context.WithValue(ctx, jwtx.ExtraKey, claims)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := user.NewWxH5AuthLogic(ctx, svcCtx)
|
||||||
resp, err := l.WxH5Auth(&req)
|
resp, err := l.WxH5Auth(&req)
|
||||||
result.HttpResult(r, w, resp, err)
|
result.HttpResult(r, w, resp, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ func (l *AdminGetAgentWithdrawalListLogic) AdminGetAgentWithdrawalList(req *type
|
|||||||
item.PayeeName = v.PayeeName.String
|
item.PayeeName = v.PayeeName.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从扣税记录中取扣税比例
|
||||||
|
taxModel, taxErr := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(l.ctx, v.Id)
|
||||||
|
if taxErr == nil {
|
||||||
|
item.TaxRate = taxModel.TaxRate
|
||||||
|
}
|
||||||
|
|
||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
}
|
}
|
||||||
resp = &types.AdminGetAgentWithdrawalListResp{
|
resp = &types.AdminGetAgentWithdrawalListResp{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package admin_agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"math"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
"tyc-server/common/xerr"
|
"tyc-server/common/xerr"
|
||||||
@@ -85,9 +86,18 @@ func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *
|
|||||||
return errors.Wrapf(xerr.NewErrMsg("提现类型不正确"), "提现类型验证失败")
|
return errors.Wrapf(xerr.NewErrMsg("提现类型不正确"), "提现类型验证失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确认时使用的扣税比例:请求中传入则用传入值,否则默认 6%
|
||||||
|
taxRate := 0.06
|
||||||
|
if req.Action == ReviewActionApprove && req.TaxRate != nil {
|
||||||
|
taxRate = *req.TaxRate
|
||||||
|
if taxRate < 0 || taxRate > 1 {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("扣税比例必须在 0~100% 之间"), "扣税比例验证失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if req.Action == ReviewActionApprove {
|
if req.Action == ReviewActionApprove {
|
||||||
// 确认提现
|
// 确认提现(可带自定义扣税比例)
|
||||||
return l.approveWithdrawal(ctx, session, record)
|
return l.approveWithdrawal(ctx, session, record, taxRate)
|
||||||
} else {
|
} else {
|
||||||
// 拒绝提现
|
// 拒绝提现
|
||||||
return l.rejectWithdrawal(ctx, session, record, req.Remark)
|
return l.rejectWithdrawal(ctx, session, record, req.Remark)
|
||||||
@@ -102,18 +112,48 @@ func (l *AdminReviewBankCardWithdrawalLogic) AdminReviewBankCardWithdrawal(req *
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认提现
|
// 确认提现(taxRate 为扣税比例,如 0.06 表示 6%)
|
||||||
func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
func (l *AdminReviewBankCardWithdrawalLogic) approveWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, taxRate float64) error {
|
||||||
|
// 按审核时选择的扣税比例重新计算并更新提现记录与扣税记录
|
||||||
|
if err := l.applyReviewTaxRate(ctx, session, record, taxRate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// 根据提现类型执行不同的操作
|
// 根据提现类型执行不同的操作
|
||||||
if record.WithdrawType == WithdrawTypeAlipay {
|
if record.WithdrawType == WithdrawTypeAlipay {
|
||||||
// 支付宝提现:先调用支付宝转账接口
|
// 支付宝提现:按更新后的实际到账金额调用支付宝转账
|
||||||
return l.approveAlipayWithdrawal(ctx, session, record)
|
return l.approveAlipayWithdrawal(ctx, session, record)
|
||||||
} else {
|
} else {
|
||||||
// 银行卡提现:直接更新状态为成功(线下转账)
|
// 银行卡提现:直接更新状态为成功(线下转账)
|
||||||
return l.approveBankCardWithdrawal(ctx, session, record)
|
return l.approveBankCardWithdrawal(ctx, session, record, taxRate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyReviewTaxRate 按审核时选择的扣税比例更新提现记录与扣税记录
|
||||||
|
func (l *AdminReviewBankCardWithdrawalLogic) applyReviewTaxRate(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, taxRate float64) error {
|
||||||
|
// 金额保留两位小数,避免浮点误差并与前端/支付宝一致
|
||||||
|
newTaxAmount := math.Round(record.Amount*taxRate*100) / 100
|
||||||
|
newActualAmount := math.Round((record.Amount-newTaxAmount)*100) / 100
|
||||||
|
|
||||||
|
record.TaxAmount = newTaxAmount
|
||||||
|
record.ActualAmount = newActualAmount
|
||||||
|
if _, err := l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新提现记录扣税金额失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
||||||
|
}
|
||||||
|
taxModel.TaxRate = taxRate
|
||||||
|
taxModel.TaxAmount = newTaxAmount
|
||||||
|
taxModel.ActualAmount = newActualAmount
|
||||||
|
taxModel.TaxableAmount = record.Amount
|
||||||
|
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 确认支付宝提现
|
// 确认支付宝提现
|
||||||
func (l *AdminReviewBankCardWithdrawalLogic) approveAlipayWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
func (l *AdminReviewBankCardWithdrawalLogic) approveAlipayWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
||||||
// 同步调用支付宝转账
|
// 同步调用支付宝转账
|
||||||
@@ -146,7 +186,7 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveAlipayWithdrawal(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确认银行卡提现
|
// 确认银行卡提现
|
||||||
func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal) error {
|
func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx context.Context, session sqlx.Session, record *model.AgentWithdrawal, taxRate float64) error {
|
||||||
// 更新提现记录状态为成功
|
// 更新提现记录状态为成功
|
||||||
record.Status = StatusSuccess
|
record.Status = StatusSuccess
|
||||||
record.Remark = sql.NullString{String: "管理员确认提现", Valid: true}
|
record.Remark = sql.NullString{String: "管理员确认提现", Valid: true}
|
||||||
@@ -193,10 +233,13 @@ func (l *AdminReviewBankCardWithdrawalLogic) approveBankCardWithdrawal(ctx conte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
// 注意:由于在 applyReviewTaxRate 中已经更新过 tax 记录(version 已经递增)
|
||||||
|
// 这里只需要更新 tax_status 和 tax_time,不需要再次调用 UpdateWithVersion
|
||||||
|
// 使用普通 Update 方法避免乐观锁冲突
|
||||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||||
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
||||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Update(ctx, session, taxModel); err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,10 +307,12 @@ func (l *AdminReviewBankCardWithdrawalLogic) rejectWithdrawal(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
// 注意:由于在 applyReviewTaxRate 中已经更新过 tax 记录(version 已经递增)
|
||||||
|
// 这里只需要更新 tax_status 和 tax_time,使用普通 Update 方法避免乐观锁冲突
|
||||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||||
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
||||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Update(ctx, session, taxModel); err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,10 +369,12 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalSuccess(ctx conte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
// 注意:由于在 applyReviewTaxRate 中已经更新过 tax 记录(version 已经递增)
|
||||||
|
// 这里只需要更新 tax_status 和 tax_time,使用普通 Update 方法避免乐观锁冲突
|
||||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||||
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
|
||||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Update(ctx, session, taxModel); err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,10 +442,12 @@ func (l *AdminReviewBankCardWithdrawalLogic) completeWithdrawalFailure(ctx conte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
// 注意:由于在 applyReviewTaxRate 中已经更新过 tax 记录(version 已经递增)
|
||||||
|
// 这里只需要更新 tax_status 和 tax_time,使用普通 Update 方法避免乐观锁冲突
|
||||||
if taxModel.TaxStatus == model.TaxStatusPending {
|
if taxModel.TaxStatus == model.TaxStatusPending {
|
||||||
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
|
||||||
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||||
if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
|
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Update(ctx, session, taxModel); err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,16 +75,19 @@ func (l *AdminRefundOrderLogic) getAndValidateOrder(orderId int64, refundAmount
|
|||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleAlipayRefund 处理支付宝退款
|
// handleAlipayRefund 处理支付宝退款(仅 [2026-01-25 16:38:17, 2026-02-02 18:26) 区间内订单走 bak 商户号)
|
||||||
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
|
func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *types.AdminRefundOrderReq) (*types.AdminRefundOrderResp, error) {
|
||||||
// 调用支付宝退款接口
|
orderPayTime := &order.CreateTime
|
||||||
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.OrderNo, req.RefundAmount)
|
if order.PayTime.Valid {
|
||||||
|
orderPayTime = &order.PayTime.Time
|
||||||
|
}
|
||||||
|
refundNo := l.generateRefundNo(order.OrderNo)
|
||||||
|
// 按订单记录的商户号 payment_merchant 选择支付宝商户;老订单未写入时由 AliRefund 内部按时间区间兜底。
|
||||||
|
refundResp, err := l.svcCtx.AlipayService.AliRefund(l.ctx, order.PaymentMerchant, order.OrderNo, req.RefundAmount, orderPayTime, refundNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 支付宝退款失败 err: %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "AdminRefundOrder, 支付宝退款失败 err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
refundNo := l.generateRefundNo(order.OrderNo)
|
|
||||||
|
|
||||||
if refundResp.IsSuccess() {
|
if refundResp.IsSuccess() {
|
||||||
// 支付宝退款成功,创建成功记录
|
// 支付宝退款成功,创建成功记录
|
||||||
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
|
err = l.createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, model.OrderStatusRefunded, model.OrderRefundStatusSuccess)
|
||||||
@@ -144,6 +147,7 @@ func (l *AdminRefundOrderLogic) createRefundRecordAndUpdateOrder(order *model.Or
|
|||||||
OrderId: order.Id,
|
OrderId: order.Id,
|
||||||
UserId: order.UserId,
|
UserId: order.UserId,
|
||||||
ProductId: order.ProductId,
|
ProductId: order.ProductId,
|
||||||
|
PaymentMerchant: order.PaymentMerchant,
|
||||||
RefundAmount: req.RefundAmount,
|
RefundAmount: req.RefundAmount,
|
||||||
RefundReason: l.createNullString(req.RefundReason),
|
RefundReason: l.createNullString(req.RefundReason),
|
||||||
Status: refundStatus, // 使用传入的状态,不再硬编码
|
Status: refundStatus, // 使用传入的状态,不再硬编码
|
||||||
@@ -172,6 +176,7 @@ func (l *AdminRefundOrderLogic) createRefundRecordOnly(order *model.Order, req *
|
|||||||
OrderId: order.Id,
|
OrderId: order.Id,
|
||||||
UserId: order.UserId,
|
UserId: order.UserId,
|
||||||
ProductId: order.ProductId,
|
ProductId: order.ProductId,
|
||||||
|
PaymentMerchant: order.PaymentMerchant,
|
||||||
RefundAmount: req.RefundAmount,
|
RefundAmount: req.RefundAmount,
|
||||||
RefundReason: l.createNullString(req.RefundReason),
|
RefundReason: l.createNullString(req.RefundReason),
|
||||||
Status: refundStatus,
|
Status: refundStatus,
|
||||||
@@ -185,9 +190,9 @@ func (l *AdminRefundOrderLogic) createRefundRecordOnly(order *model.Order, req *
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateRefundNo 生成退款单号
|
// generateRefundNo 生成退款单号(同一订单多次退款需唯一,避免 unique_refund_no 冲突)
|
||||||
func (l *AdminRefundOrderLogic) generateRefundNo(orderNo string) string {
|
func (l *AdminRefundOrderLogic) generateRefundNo(orderNo string) string {
|
||||||
return fmt.Sprintf("%s%s", RefundNoPrefix, orderNo)
|
return fmt.Sprintf("%s%s-%d", RefundNoPrefix, orderNo, time.Now().UnixMilli())
|
||||||
}
|
}
|
||||||
|
|
||||||
// createNullString 创建 sql.NullString
|
// createNullString 创建 sql.NullString
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ func (l *AdminGetPlatformUserDetailLogic) AdminGetPlatformUserDetail(req *types.
|
|||||||
Nickname: "",
|
Nickname: "",
|
||||||
Info: user.Info,
|
Info: user.Info,
|
||||||
Inside: user.Inside,
|
Inside: user.Inside,
|
||||||
|
Disable: user.Disable,
|
||||||
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,12 @@ func NewAdminGetPlatformUserListLogic(ctx context.Context, svcCtx *svc.ServiceCo
|
|||||||
func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.AdminGetPlatformUserListReq) (resp *types.AdminGetPlatformUserListResp, err error) {
|
func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.AdminGetPlatformUserListReq) (resp *types.AdminGetPlatformUserListResp, err error) {
|
||||||
builder := l.svcCtx.UserModel.SelectBuilder()
|
builder := l.svcCtx.UserModel.SelectBuilder()
|
||||||
if req.Mobile != "" {
|
if req.Mobile != "" {
|
||||||
builder = builder.Where("mobile = ?", req.Mobile)
|
// 数据库手机号为密文,需加密明文手机号后再查询
|
||||||
|
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机号加密失败: %v", err)
|
||||||
|
}
|
||||||
|
builder = builder.Where("mobile = ?", encryptedMobile)
|
||||||
}
|
}
|
||||||
if req.Nickname != "" {
|
if req.Nickname != "" {
|
||||||
builder = builder.Where("nickname = ?", req.Nickname)
|
builder = builder.Where("nickname = ?", req.Nickname)
|
||||||
@@ -72,6 +77,7 @@ func (l *AdminGetPlatformUserListLogic) AdminGetPlatformUserList(req *types.Admi
|
|||||||
Nickname: "",
|
Nickname: "",
|
||||||
Info: user.Info,
|
Info: user.Info,
|
||||||
Inside: user.Inside,
|
Inside: user.Inside,
|
||||||
|
Disable: user.Disable,
|
||||||
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
CreateTime: user.CreateTime.Format("2006-01-02 15:04:05"),
|
||||||
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
UpdateTime: user.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ func (l *AdminUpdatePlatformUserLogic) AdminUpdatePlatformUser(req *types.AdminU
|
|||||||
}
|
}
|
||||||
user.Inside = *req.Inside
|
user.Inside = *req.Inside
|
||||||
}
|
}
|
||||||
|
if req.Disable != nil {
|
||||||
|
if *req.Disable != 1 && *req.Disable != 0 {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "封禁状态错误: %d", *req.Disable)
|
||||||
|
}
|
||||||
|
user.Disable = *req.Disable
|
||||||
|
}
|
||||||
if req.Password != nil {
|
if req.Password != nil {
|
||||||
user.Password = sql.NullString{String: *req.Password, Valid: *req.Password != ""}
|
user.Password = sql.NullString{String: *req.Password, Valid: *req.Password != ""}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ func (l *AdminGetQueryDetailByOrderIdLogic) AdminGetQueryDetailByOrderId(req *ty
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告查询, 获取商品信息失败, %v", err)
|
||||||
}
|
}
|
||||||
query.ProductName = product.ProductName
|
query.ProductName = product.ProductName
|
||||||
return &types.AdminGetQueryDetailByOrderIdResp{
|
|
||||||
|
resp = &types.AdminGetQueryDetailByOrderIdResp{
|
||||||
Id: query.Id,
|
Id: query.Id,
|
||||||
OrderId: query.OrderId,
|
OrderId: query.OrderId,
|
||||||
UserId: query.UserId,
|
UserId: query.UserId,
|
||||||
@@ -82,7 +83,23 @@ func (l *AdminGetQueryDetailByOrderIdLogic) AdminGetQueryDetailByOrderId(req *ty
|
|||||||
CreateTime: query.CreateTime,
|
CreateTime: query.CreateTime,
|
||||||
UpdateTime: query.UpdateTime,
|
UpdateTime: query.UpdateTime,
|
||||||
QueryState: query.QueryState,
|
QueryState: query.QueryState,
|
||||||
}, nil
|
}
|
||||||
|
// 从订单表补充:商户订单号、支付订单号、支付状态、支付时间、退款时间
|
||||||
|
orderModel, orderErr := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderId)
|
||||||
|
if orderErr == nil && orderModel != nil {
|
||||||
|
resp.OrderNo = orderModel.OrderNo
|
||||||
|
if orderModel.PlatformOrderId.Valid {
|
||||||
|
resp.PlatformOrderId = orderModel.PlatformOrderId.String
|
||||||
|
}
|
||||||
|
resp.PaymentStatus = orderModel.Status
|
||||||
|
if orderModel.PayTime.Valid {
|
||||||
|
resp.PayTime = orderModel.PayTime.Time.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
if orderModel.RefundTime.Valid {
|
||||||
|
resp.RefundTime = orderModel.RefundTime.Time.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessQueryData 解密和反序列化 QueryData
|
// ProcessQueryData 解密和反序列化 QueryData
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ func (l *GetAgentWithdrawalLogic) GetAgentWithdrawal(req *types.GetWithdrawalReq
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理提现列表, %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取代理提现列表, %v", err)
|
||||||
}
|
}
|
||||||
withdrawal.CreateTime = agentWithdrawalModel.CreateTime.Format("2006-01-02 15:04:05")
|
withdrawal.CreateTime = agentWithdrawalModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
|
// 从扣税记录中取扣税比例,供前端展示“扣了几个点”
|
||||||
|
taxModel, taxErr := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(l.ctx, agentWithdrawalModel.Id)
|
||||||
|
if taxErr == nil {
|
||||||
|
withdrawal.TaxRate = taxModel.TaxRate
|
||||||
|
}
|
||||||
list = append(list, withdrawal)
|
list = append(list, withdrawal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/common/xerr"
|
"tyc-server/common/xerr"
|
||||||
|
"tyc-server/pkg/captcha"
|
||||||
"tyc-server/pkg/lzkit/crypto"
|
"tyc-server/pkg/lzkit/crypto"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -34,7 +35,21 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserAgentContextKey 用于从 context 读取 User-Agent(如判断是否微信)
|
||||||
|
const UserAgentContextKey = "user_agent"
|
||||||
|
|
||||||
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error {
|
||||||
|
userAgent, _ := l.ctx.Value(UserAgentContextKey).(string)
|
||||||
|
cfg := l.svcCtx.Config.Captcha
|
||||||
|
if err := captcha.VerifyWithUserAgent(captcha.Config{
|
||||||
|
AccessKeyID: cfg.AccessKeyID,
|
||||||
|
AccessKeySecret: cfg.AccessKeySecret,
|
||||||
|
EndpointURL: cfg.EndpointURL,
|
||||||
|
SceneID: cfg.SceneID,
|
||||||
|
}, req.CaptchaVerifyParam, userAgent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/pkg/captcha"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetEncryptedSceneIdLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetEncryptedSceneIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetEncryptedSceneIdLogic {
|
||||||
|
return &GetEncryptedSceneIdLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetEncryptedSceneIdLogic) GetEncryptedSceneId() (resp *types.GetEncryptedSceneIdResp, err error) {
|
||||||
|
cfg := l.svcCtx.Config.Captcha
|
||||||
|
encrypted, genErr := captcha.GenerateEncryptedSceneID(cfg.SceneID, cfg.EKey, 3600)
|
||||||
|
if genErr != nil {
|
||||||
|
// 记录日志,返回通用错误
|
||||||
|
l.Errorf("generate encrypted scene id error: %+v", genErr)
|
||||||
|
return nil, genErr
|
||||||
|
}
|
||||||
|
return &types.GetEncryptedSceneIdResp{
|
||||||
|
EncryptedSceneId: encrypted,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -32,22 +32,58 @@ func NewAlipayCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Al
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Request) error {
|
func (l *AlipayCallbackLogic) AlipayCallback(w http.ResponseWriter, r *http.Request) error {
|
||||||
notification, err := l.svcCtx.AlipayService.HandleAliPaymentNotification(r)
|
// 先解析表单,拿到 out_trade_no 用于查找订单和对应商户号
|
||||||
if err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
logx.Errorf("支付宝支付回调,%v", err)
|
logx.Errorf("支付宝支付回调,解析请求表单失败: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据订单号前缀判断订单类型
|
orderNo := r.FormValue("out_trade_no")
|
||||||
orderNo := notification.OutTradeNo
|
if orderNo == "" {
|
||||||
|
logx.Errorf("支付宝支付回调,缺少 out_trade_no")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据订单号前缀判断订单类型,并查出对应商户标识
|
||||||
|
var merchant string
|
||||||
if strings.HasPrefix(orderNo, "Q_") {
|
if strings.HasPrefix(orderNo, "Q_") {
|
||||||
// 查询订单处理
|
// 查询订单
|
||||||
return l.handleQueryOrderPayment(w, notification)
|
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("支付宝支付回调,查询订单失败: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
merchant = order.PaymentMerchant
|
||||||
} else if strings.HasPrefix(orderNo, "A_") {
|
} else if strings.HasPrefix(orderNo, "A_") {
|
||||||
// 代理会员订单处理
|
// 代理会员订单
|
||||||
return l.handleAgentVipOrderPayment(w, notification)
|
agentOrder, err := l.svcCtx.AgentMembershipRechargeOrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("支付宝支付回调,查询代理会员订单失败: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
merchant = agentOrder.PaymentMerchant
|
||||||
} else {
|
} else {
|
||||||
// 兼容旧订单,假设没有前缀的是查询订单
|
// 兼容旧订单,假设没有前缀的是查询订单
|
||||||
|
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("支付宝支付回调(旧订单),查询订单失败: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
merchant = order.PaymentMerchant
|
||||||
|
}
|
||||||
|
|
||||||
|
notification, err := l.svcCtx.AlipayService.HandleAliPaymentNotification(merchant, r.Form)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("支付宝支付回调,验签失败: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再次根据订单号前缀分发到具体处理函数
|
||||||
|
if strings.HasPrefix(orderNo, "Q_") {
|
||||||
|
return l.handleQueryOrderPayment(w, notification)
|
||||||
|
} else if strings.HasPrefix(orderNo, "A_") {
|
||||||
|
return l.handleAgentVipOrderPayment(w, notification)
|
||||||
|
} else {
|
||||||
return l.handleQueryOrderPayment(w, notification)
|
return l.handleQueryOrderPayment(w, notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +254,10 @@ func (l *AlipayCallbackLogic) handleRefund(order *model.AgentMembershipRechargeO
|
|||||||
return refundErr
|
return refundErr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
// 支付宝退款按订单记录的商户号 payment_merchant 走对应商户;
|
||||||
|
// 老订单若未写入商户号,则在 AliRefund 内按时间区间兜底。
|
||||||
|
orderPayTime := order.CreateTime
|
||||||
|
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.PaymentMerchant, order.OrderNo, order.Amount, &orderPayTime, "")
|
||||||
if refundErr != nil {
|
if refundErr != nil {
|
||||||
return refundErr
|
return refundErr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/app/main/api/internal/svc"
|
"tyc-server/app/main/api/internal/svc"
|
||||||
"tyc-server/app/main/api/internal/types"
|
"tyc-server/app/main/api/internal/types"
|
||||||
@@ -32,6 +33,22 @@ type PaymentTypeResp struct {
|
|||||||
outTradeNo string
|
outTradeNo string
|
||||||
description string
|
description string
|
||||||
orderID int64
|
orderID int64
|
||||||
|
payMerchantID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepayDataMissing 判断第三方支付预创建是否未返回可用参数(含 interface 包了一层 nil 的情况)
|
||||||
|
func prepayDataMissing(v interface{}) bool {
|
||||||
|
if v == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch t := v.(type) {
|
||||||
|
case string:
|
||||||
|
return t == ""
|
||||||
|
case map[string]string:
|
||||||
|
return len(t) == 0
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLogic {
|
||||||
@@ -43,10 +60,9 @@ func NewPaymentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PaymentLo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
|
func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp, err error) {
|
||||||
// 暂时关闭微信支付
|
req.PayMethod = strings.TrimSpace(req.PayMethod)
|
||||||
if req.PayMethod == "wechat" {
|
req.PayType = strings.TrimSpace(req.PayType)
|
||||||
return nil, errors.Wrapf(xerr.NewErrMsg("微信支付暂时关闭"), "微信支付暂时关闭")
|
req.Id = strings.TrimSpace(req.Id)
|
||||||
}
|
|
||||||
|
|
||||||
var paymentTypeResp *PaymentTypeResp
|
var paymentTypeResp *PaymentTypeResp
|
||||||
var prepayData interface{}
|
var prepayData interface{}
|
||||||
@@ -79,19 +95,53 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
|||||||
if req.PayMethod == "wechat" {
|
if req.PayMethod == "wechat" {
|
||||||
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
prepayData, createOrderErr = l.svcCtx.WechatPayService.CreateWechatOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||||
} else if req.PayMethod == "alipay" {
|
} else if req.PayMethod == "alipay" {
|
||||||
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
// 支付宝:按订单写入的商户标识(one/two)创建支付订单
|
||||||
|
prepayData, createOrderErr = l.svcCtx.AlipayService.CreateAlipayOrder(l.ctx, paymentTypeResp.payMerchantID, paymentTypeResp.amount, paymentTypeResp.description, paymentTypeResp.outTradeNo)
|
||||||
} else if req.PayMethod == "appleiap" {
|
} else if req.PayMethod == "appleiap" {
|
||||||
prepayData = l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
|
iap := l.svcCtx.ApplePayService.GetIappayAppID(paymentTypeResp.outTradeNo)
|
||||||
|
prepayData = iap
|
||||||
|
if iap == "" {
|
||||||
|
createOrderErr = fmt.Errorf("获取 IAP 参数失败")
|
||||||
|
}
|
||||||
|
} else if req.PayMethod == "test" {
|
||||||
|
// 开发环境测试支付已在上方分支写入 prepayData;若走到此处说明非 development
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("测试支付仅在开发环境可用"), "ENV!=development 且 pay_method=test")
|
||||||
}
|
}
|
||||||
if createOrderErr != nil {
|
if createOrderErr != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
||||||
}
|
}
|
||||||
|
// 在事务内校验并失败则回滚,避免订单已落库却无 prepay(此前仅事务外校验会产生脏订单)
|
||||||
|
if req.PayMethod == "wechat" || req.PayMethod == "alipay" {
|
||||||
|
if prepayDataMissing(prepayData) {
|
||||||
|
platformVal := l.ctx.Value("platform")
|
||||||
|
logx.WithContext(l.ctx).Errorf(
|
||||||
|
"[Payment] 事务内 prepay 为空将回滚: pay_method=%q pay_type=%q order_no=%s platform=%v prepayData_type=%T",
|
||||||
|
req.PayMethod, req.PayType, paymentTypeResp.outTradeNo, platformVal, prepayData,
|
||||||
|
)
|
||||||
|
return errors.Wrapf(
|
||||||
|
xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, "获取支付参数失败,请确认在微信内打开且已完成网页授权后重试"),
|
||||||
|
"创建支付失败: prepay 为空 platform=%v",
|
||||||
|
platformVal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.PayMethod == "wechat" || req.PayMethod == "alipay" {
|
||||||
|
if prepayDataMissing(prepayData) {
|
||||||
|
logx.WithContext(l.ctx).Errorf("[Payment] 事务提交后 prepay 仍为空(不应出现): pay_method=%q id=%q platform=%v",
|
||||||
|
req.PayMethod, req.Id, l.ctx.Value("platform"))
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, "获取支付参数失败,请稍后重试"),
|
||||||
|
"创建支付失败: 未生成支付参数",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 开发环境测试支付模式:事务提交后处理订单状态更新和后续流程(仅 pay_method=test 且 query 类型 orderID>0)
|
// 开发环境测试支付模式:事务提交后处理订单状态更新和后续流程(仅 pay_method=test 且 query 类型 orderID>0)
|
||||||
isDevTestPayment := os.Getenv("ENV") == "development" && req.PayMethod == "test"
|
isDevTestPayment := os.Getenv("ENV") == "development" && req.PayMethod == "test"
|
||||||
if isDevTestPayment && paymentTypeResp != nil && paymentTypeResp.orderID != 0 {
|
if isDevTestPayment && paymentTypeResp != nil && paymentTypeResp.orderID != 0 {
|
||||||
@@ -161,6 +211,11 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败, %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 解析缓存内容失败, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 代理推广链接订单仅支持支付宝,拒绝微信支付(与个人查询页选项一致)
|
||||||
|
if req.PayMethod == "wechat" && data.AgentIdentifier != "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("代理推广订单暂不支持微信支付"), "代理推广订单暂不支持微信支付")
|
||||||
|
}
|
||||||
|
|
||||||
product, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, data.Product)
|
product, err := l.svcCtx.ProductModel.FindOneByProductEn(l.ctx, data.Product)
|
||||||
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)
|
||||||
@@ -186,12 +241,24 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
|
|||||||
amount = 0.01
|
amount = 0.01
|
||||||
}
|
}
|
||||||
var orderID int64
|
var orderID int64
|
||||||
|
|
||||||
|
// 默认支付宝商户号为 one;若为代理推广订单(存在 AgentIdentifier),则使用 two。
|
||||||
|
paymentMerchant := ""
|
||||||
|
if req.PayMethod == "alipay" {
|
||||||
|
if data.AgentIdentifier != "" {
|
||||||
|
paymentMerchant = "two"
|
||||||
|
} else {
|
||||||
|
paymentMerchant = "one"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
order := model.Order{
|
order := model.Order{
|
||||||
OrderNo: outTradeNo,
|
OrderNo: outTradeNo,
|
||||||
UserId: userID,
|
UserId: userID,
|
||||||
ProductId: product.Id,
|
ProductId: product.Id,
|
||||||
PaymentPlatform: req.PayMethod,
|
PaymentPlatform: req.PayMethod,
|
||||||
PaymentScene: "app",
|
PaymentScene: "app",
|
||||||
|
PaymentMerchant: paymentMerchant,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
Status: "pending",
|
Status: "pending",
|
||||||
}
|
}
|
||||||
@@ -228,7 +295,13 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理订单失败: %+v", agentOrderInsert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &PaymentTypeResp{amount: amount, outTradeNo: outTradeNo, description: product.ProductName, orderID: orderID}, nil
|
return &PaymentTypeResp{
|
||||||
|
amount: amount,
|
||||||
|
outTradeNo: outTradeNo,
|
||||||
|
description: product.ProductName,
|
||||||
|
orderID: orderID,
|
||||||
|
payMerchantID: paymentMerchant,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.Session) (resp *PaymentTypeResp, err error) {
|
||||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||||
@@ -274,12 +347,19 @@ func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.
|
|||||||
if user.Inside == 1 {
|
if user.Inside == 1 {
|
||||||
amount = 0.01
|
amount = 0.01
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paymentMerchant := ""
|
||||||
|
if req.PayMethod == "alipay" {
|
||||||
|
paymentMerchant = "one"
|
||||||
|
}
|
||||||
|
|
||||||
agentMembershipRechargeOrder := model.AgentMembershipRechargeOrder{
|
agentMembershipRechargeOrder := model.AgentMembershipRechargeOrder{
|
||||||
OrderNo: req.Id,
|
OrderNo: req.Id,
|
||||||
UserId: userID,
|
UserId: userID,
|
||||||
AgentId: agentModel.Id,
|
AgentId: agentModel.Id,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
PaymentMethod: req.PayMethod,
|
PaymentMethod: req.PayMethod,
|
||||||
|
PaymentMerchant: paymentMerchant,
|
||||||
LevelName: agentVipCache.Type,
|
LevelName: agentVipCache.Type,
|
||||||
Status: "pending",
|
Status: "pending",
|
||||||
}
|
}
|
||||||
@@ -287,7 +367,12 @@ func (l *PaymentLogic) AgentVipOrderPayment(req *types.PaymentReq, session sqlx.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理会员充值订单失败: %+v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 保存代理会员充值订单失败: %+v", err)
|
||||||
}
|
}
|
||||||
return &PaymentTypeResp{amount: amount, outTradeNo: req.Id, description: fmt.Sprintf("%s代理会员充值", agentMembershipConfig.LevelName)}, nil
|
return &PaymentTypeResp{
|
||||||
|
amount: amount,
|
||||||
|
outTradeNo: req.Id,
|
||||||
|
description: fmt.Sprintf("%s代理会员充值", agentMembershipConfig.LevelName),
|
||||||
|
payMerchantID: paymentMerchant,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
func (l *PaymentLogic) agentParsing(agentIdentifier string) (*types.AgentIdentifier, error) {
|
func (l *PaymentLogic) agentParsing(agentIdentifier string) (*types.AgentIdentifier, error) {
|
||||||
key, decodeErr := hex.DecodeString("8e3e7a2f60edb49221e953b9c029ed10")
|
key, decodeErr := hex.DecodeString("8e3e7a2f60edb49221e953b9c029ed10")
|
||||||
|
|||||||
@@ -221,7 +221,10 @@ func (l *WechatPayCallbackLogic) handleRefund(order *model.AgentMembershipRechar
|
|||||||
return refundErr
|
return refundErr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
// 支付宝退款按订单记录的商户号 payment_merchant 走对应商户;
|
||||||
|
// 老订单若未写入商户号,则在 AliRefund 内按时间区间兜底。
|
||||||
|
orderPayTime := order.CreateTime
|
||||||
|
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.PaymentMerchant, order.OrderNo, order.Amount, &orderPayTime, "")
|
||||||
if refundErr != nil {
|
if refundErr != nil {
|
||||||
return refundErr
|
return refundErr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,16 @@ func (l *QueryDetailByOrderIdLogic) QueryDetailByOrderId(req *types.QueryDetailB
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查订单状态
|
// 检查订单状态
|
||||||
if order.Status != "paid" {
|
// - pending:未支付,阻止查看
|
||||||
|
// - refunded:已退款,允许进入,但前端只展示“查询为空,订单已退款”提示(不再展示报告数据)
|
||||||
|
// - 其他异常状态:按未找到处理
|
||||||
|
if order.Status == "pending" {
|
||||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
|
return nil, errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
|
||||||
}
|
}
|
||||||
|
if order.Status != "paid" && order.Status != "refunded" {
|
||||||
|
// 使用逻辑层“未找到”错误码,前端按空结果处理
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "订单状态不支持查看报告, status=%s", order.Status)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取报告信息
|
// 获取报告信息
|
||||||
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderId)
|
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, req.OrderId)
|
||||||
@@ -105,6 +112,15 @@ func (l *QueryDetailByOrderIdLogic) QueryDetailByOrderId(req *types.QueryDetailB
|
|||||||
}
|
}
|
||||||
query.ProductName = product.ProductName
|
query.ProductName = product.ProductName
|
||||||
query.Product = product.ProductEn
|
query.Product = product.ProductEn
|
||||||
|
|
||||||
|
// 已退款订单:不再返回具体报告数据,仅告知前端“查询为空,订单已退款”
|
||||||
|
if order.Status == "refunded" {
|
||||||
|
query.QueryState = "failed" // 前端据此走空状态 + 退款提示
|
||||||
|
query.QueryData = []types.QueryItem{} // 不返回任何模块数据
|
||||||
|
if query.QueryParams == nil { // 参数可选保留,这里保险起见给个空 map
|
||||||
|
query.QueryParams = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &types.QueryDetailByOrderIdResp{
|
return &types.QueryDetailByOrderIdResp{
|
||||||
Query: query,
|
Query: query,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -53,9 +53,16 @@ func (l *QueryDetailByOrderNoLogic) QueryDetailByOrderNo(req *types.QueryDetailB
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查订单状态
|
// 检查订单状态
|
||||||
if order.Status != "paid" {
|
// - pending:未支付,阻止查看
|
||||||
|
// - refunded:已退款,允许进入,但前端只展示“查询为空,订单已退款”提示(不再展示报告数据)
|
||||||
|
// - 其他异常状态:按未找到处理
|
||||||
|
if order.Status == "pending" {
|
||||||
return nil, errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
|
return nil, errors.Wrapf(xerr.NewErrMsg("订单未支付,无法查看报告"), "")
|
||||||
}
|
}
|
||||||
|
if order.Status != "paid" && order.Status != "refunded" {
|
||||||
|
// 使用逻辑层“未找到”错误码,前端按空结果处理
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.LOGIC_QUERY_NOT_FOUND), "订单状态不支持查看报告, status=%s", order.Status)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取报告信息
|
// 获取报告信息
|
||||||
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
|
queryModel, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
|
||||||
@@ -96,6 +103,15 @@ func (l *QueryDetailByOrderNoLogic) QueryDetailByOrderNo(req *types.QueryDetailB
|
|||||||
}
|
}
|
||||||
query.Product = product.ProductEn
|
query.Product = product.ProductEn
|
||||||
query.ProductName = product.ProductName
|
query.ProductName = product.ProductName
|
||||||
|
|
||||||
|
// 已退款订单:不再返回具体报告数据,仅告知前端“查询为空,订单已退款”
|
||||||
|
if order.Status == "refunded" {
|
||||||
|
query.QueryState = "failed" // 前端据此走空状态 + 退款提示
|
||||||
|
query.QueryData = []types.QueryItem{} // 不返回任何模块数据
|
||||||
|
if query.QueryParams == nil { // 参数可选保留,这里保险起见给个空 map
|
||||||
|
query.QueryParams = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &types.QueryDetailByOrderNoResp{
|
return &types.QueryDetailByOrderNoResp{
|
||||||
Query: query,
|
Query: query,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package query
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"tyc-server/app/main/api/internal/svc"
|
"tyc-server/app/main/api/internal/svc"
|
||||||
"tyc-server/app/main/api/internal/types"
|
"tyc-server/app/main/api/internal/types"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
@@ -28,25 +29,45 @@ func NewQueryListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isVehicleProduct 判断 product_en 是否为车辆类产品
|
||||||
|
func isVehicleProduct(productEn string) bool {
|
||||||
|
vehiclePrefixes := []string{
|
||||||
|
"toc_Vehicle",
|
||||||
|
"toc_PersonVehicle",
|
||||||
|
}
|
||||||
|
for _, p := range vehiclePrefixes {
|
||||||
|
if strings.HasPrefix(productEn, p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryListResp, err error) {
|
func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryListResp, err error) {
|
||||||
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
userID, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||||
if getUidErr != nil {
|
if getUidErr != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 获取用户信息失败, %+v", getUidErr)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 获取用户信息失败, %+v", getUidErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 小程序端需要过滤非车辆产品,取更大分页后内存过滤
|
||||||
|
pageSize := req.PageSize
|
||||||
|
isMiniapp := req.Source == "miniapp"
|
||||||
|
if isMiniapp && pageSize < 200 {
|
||||||
|
pageSize = 200
|
||||||
|
}
|
||||||
|
|
||||||
// 直接构建查询query表的条件
|
// 直接构建查询query表的条件
|
||||||
build := l.svcCtx.QueryModel.SelectBuilder().Where(squirrel.Eq{
|
build := l.svcCtx.QueryModel.SelectBuilder().Where(squirrel.Eq{
|
||||||
"user_id": userID,
|
"user_id": userID,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 直接从query表分页查询
|
// 直接从query表分页查询
|
||||||
queryList, total, err := l.svcCtx.QueryModel.FindPageListByPageWithTotal(l.ctx, build, req.Page, req.PageSize, "create_time DESC")
|
queryList, total, err := l.svcCtx.QueryModel.FindPageListByPageWithTotal(l.ctx, build, req.Page, pageSize, "create_time DESC")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 查找报告列表错误, %+v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "报告列表查询, 查找报告列表错误, %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var list []types.Query
|
var list []types.Query
|
||||||
if len(queryList) > 0 {
|
|
||||||
for _, queryModel := range queryList {
|
for _, queryModel := range queryList {
|
||||||
var query types.Query
|
var query types.Query
|
||||||
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
|
query.CreateTime = queryModel.CreateTime.Format("2006-01-02 15:04:05")
|
||||||
@@ -60,6 +81,11 @@ func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryLi
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 获取商品信息失败, %+v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "报告列表查询, 获取商品信息失败, %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 小程序端:过滤非车辆类产品(H5 端 source 为空,不做过滤)
|
||||||
|
if isMiniapp && !isVehicleProduct(product.ProductEn) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 检查订单状态,如果订单已退款,则设置查询状态为已退款
|
// 检查订单状态,如果订单已退款,则设置查询状态为已退款
|
||||||
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, queryModel.OrderId)
|
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, queryModel.OrderId)
|
||||||
if findOrderErr == nil && order.Status == model.OrderStatusRefunded {
|
if findOrderErr == nil && order.Status == model.OrderStatusRefunded {
|
||||||
@@ -69,10 +95,15 @@ func (l *QueryListLogic) QueryList(req *types.QueryListReq) (resp *types.QueryLi
|
|||||||
query.Product = product.ProductEn
|
query.Product = product.ProductEn
|
||||||
list = append(list, query)
|
list = append(list, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// H5 端返回数据库原始 total;小程序端返回过滤后的实际数量
|
||||||
|
respTotal := total
|
||||||
|
if isMiniapp {
|
||||||
|
respTotal = int64(len(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.QueryListResp{
|
return &types.QueryListResp{
|
||||||
Total: total,
|
Total: respTotal,
|
||||||
List: list,
|
List: list,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
187
app/main/api/internal/logic/tianyuan/vehiclecallbacklogic.go
Normal file
187
app/main/api/internal/logic/tianyuan/vehiclecallbacklogic.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package tianyuan
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/service"
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/model"
|
||||||
|
"tyc-server/common/xerr"
|
||||||
|
"tyc-server/pkg/lzkit/crypto"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VehicleCallbackLogic 处理天远车辆类接口的异步回调
|
||||||
|
// 设计目标:
|
||||||
|
// - 按 order_no + api_id 找到对应的查询记录
|
||||||
|
// - 解密原有 query_data([]APIResponseData),更新或追加当前 api_id 的结果
|
||||||
|
// - 再次加密写回 query.query_data,必要时将 query_state 从 pending 更新为 success
|
||||||
|
type VehicleCallbackLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVehicleCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VehicleCallbackLogic {
|
||||||
|
return &VehicleCallbackLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 与 paySuccessNotify 中一致:仅通过异步回调回写结果的车辆接口
|
||||||
|
var asyncVehicleApiIDs = map[string]bool{
|
||||||
|
"QCXG1U4U": true, "QCXG3Y6B": true, "QCXG3Z3L": true, "QCXGP00W": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// allAsyncVehicleReceived 判断 apiList 中所有“异步车辆接口”是否均已收到回调(Success 为 true)
|
||||||
|
func allAsyncVehicleReceived(apiList []service.APIResponseData) bool {
|
||||||
|
for _, item := range apiList {
|
||||||
|
if asyncVehicleApiIDs[item.ApiID] && !item.Success {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 入口:直接接收原始 *http.Request,方便读取 query / body
|
||||||
|
func (l *VehicleCallbackLogic) Handle(r *http.Request) error {
|
||||||
|
apiID := r.URL.Query().Get("api_id")
|
||||||
|
orderNo := r.URL.Query().Get("order_no")
|
||||||
|
|
||||||
|
if apiID == "" || orderNo == "" {
|
||||||
|
return errors.Wrapf(
|
||||||
|
xerr.NewErrMsg("缺少 api_id 或 order_no"),
|
||||||
|
"tianyuan vehicle callback, api_id=%s, order_no=%s", apiID, orderNo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Infof("tianyuan vehicle callback start, api_id=%s, order_no=%s", apiID, orderNo)
|
||||||
|
|
||||||
|
bodyBytes, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取回调 Body 失败: %v", err)
|
||||||
|
}
|
||||||
|
l.Infof("tianyuan vehicle callback body received, api_id=%s, order_no=%s, body_len=%d", apiID, orderNo, len(bodyBytes))
|
||||||
|
|
||||||
|
// 回调包裹格式通常为 { code:0, msg:\"成功\", order_id:\"...\", data:{...} },
|
||||||
|
// 这里只把 data 字段提取出来存到 QueryData 里,便于前端直接渲染。
|
||||||
|
type callbackWrapper struct {
|
||||||
|
Code json.RawMessage `json:"code"`
|
||||||
|
Msg json.RawMessage `json:"msg"`
|
||||||
|
OrderID json.RawMessage `json:"order_id"`
|
||||||
|
Data json.RawMessage `json:"data"`
|
||||||
|
Extra map[string]any `json:"-"` // 预留
|
||||||
|
}
|
||||||
|
var wrapper callbackWrapper
|
||||||
|
payload := bodyBytes
|
||||||
|
if len(bodyBytes) > 0 {
|
||||||
|
if err := json.Unmarshal(bodyBytes, &wrapper); err != nil {
|
||||||
|
l.Errorf("tianyuan vehicle callback unmarshal body failed, api_id=%s, order_no=%s, err=%v", apiID, orderNo, err)
|
||||||
|
} else if len(wrapper.Data) > 0 {
|
||||||
|
payload = wrapper.Data
|
||||||
|
l.Infof("tianyuan vehicle callback extracted data field, api_id=%s, order_no=%s, data_len=%d", apiID, orderNo, len(payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 根据订单号找到订单
|
||||||
|
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||||
|
if err != nil {
|
||||||
|
if err == model.ErrNotFound {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("未找到订单"), "order_no=%s", orderNo)
|
||||||
|
}
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败, order_no=%s, err=%v", orderNo, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 根据订单ID找到对应查询记录
|
||||||
|
query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
|
||||||
|
if err != nil {
|
||||||
|
if err == model.ErrNotFound {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("未找到查询记录"), "order_no=%s, order_id=%d", orderNo, order.Id)
|
||||||
|
}
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 query 记录失败, order_id=%d, err=%v", order.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取加密密钥
|
||||||
|
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||||
|
key, decodeErr := hex.DecodeString(secretKey)
|
||||||
|
if decodeErr != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 AES 密钥失败: %v", decodeErr)
|
||||||
|
}
|
||||||
|
// 4. 解密 query_data,反序列化为 []APIResponseData
|
||||||
|
var apiList []service.APIResponseData
|
||||||
|
if query.QueryData.Valid && query.QueryData.String != "" {
|
||||||
|
decrypted, decErr := crypto.AesDecrypt(query.QueryData.String, key)
|
||||||
|
if decErr != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密 query_data 失败: %v", decErr)
|
||||||
|
}
|
||||||
|
if len(decrypted) > 0 {
|
||||||
|
if err := json.Unmarshal(decrypted, &apiList); err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 query_data 为 APIResponseData 列表失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 更新或追加当前 api_id 的结果
|
||||||
|
nowStr := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
updated := false
|
||||||
|
for i := range apiList {
|
||||||
|
if apiList[i].ApiID == apiID {
|
||||||
|
apiList[i].Data = json.RawMessage(payload)
|
||||||
|
apiList[i].Success = true
|
||||||
|
apiList[i].Timestamp = nowStr
|
||||||
|
apiList[i].Error = ""
|
||||||
|
updated = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !updated {
|
||||||
|
apiList = append(apiList, service.APIResponseData{
|
||||||
|
ApiID: apiID,
|
||||||
|
Data: json.RawMessage(payload),
|
||||||
|
Success: true,
|
||||||
|
Timestamp: nowStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 重新序列化并加密,写回查询记录
|
||||||
|
merged, err := json.Marshal(apiList)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化合并后的 query_data 失败: %v", err)
|
||||||
|
}
|
||||||
|
enc, encErr := crypto.AesEncrypt(merged, key)
|
||||||
|
if encErr != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密合并后的 query_data 失败: %v", encErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
query.QueryData.String = enc
|
||||||
|
query.QueryData.Valid = true
|
||||||
|
// 仅当所有异步车辆接口均已有回调结果时,才将 pending 置为 success,并触发代理结算
|
||||||
|
wasPending := query.QueryState == "pending"
|
||||||
|
didSetSuccess := wasPending && allAsyncVehicleReceived(apiList)
|
||||||
|
if didSetSuccess {
|
||||||
|
query.QueryState = "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, query); err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新查询记录失败, query_id=%d, err=%v", query.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if didSetSuccess {
|
||||||
|
if agentErr := l.svcCtx.AgentService.AgentProcess(l.ctx, order); agentErr != nil {
|
||||||
|
l.Errorf("tianyuan vehicle callback, AgentProcess failed, order_no=%s, err=%v", orderNo, agentErr)
|
||||||
|
// 不因代理处理失败而整体失败,回调已成功落库
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Infof("tianyuan vehicle callback handled, order_no=%s, api_id=%s, query_id=%d", orderNo, apiID, query.Id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
43
app/main/api/internal/logic/toolbox/toolboxlistlogic.go
Normal file
43
app/main/api/internal/logic/toolbox/toolboxlistlogic.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package toolbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ToolboxListLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewToolboxListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToolboxListLogic {
|
||||||
|
return &ToolboxListLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ToolboxListLogic) ToolboxList() (resp *types.ToolboxListResp, err error) {
|
||||||
|
// 调用toolboxService获取工具列表
|
||||||
|
tools := l.svcCtx.ToolboxService.ListTools()
|
||||||
|
|
||||||
|
// 转换为响应格式
|
||||||
|
var toolInfos []types.ToolInfo
|
||||||
|
for _, tool := range tools {
|
||||||
|
toolInfos = append(toolInfos, types.ToolInfo{
|
||||||
|
Key: tool.Key,
|
||||||
|
Name: tool.Name,
|
||||||
|
Desc: tool.Desc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.ToolboxListResp{
|
||||||
|
Tools: toolInfos,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
37
app/main/api/internal/logic/toolbox/toolboxquerylogic.go
Normal file
37
app/main/api/internal/logic/toolbox/toolboxquerylogic.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package toolbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ToolboxQueryLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewToolboxQueryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToolboxQueryLogic {
|
||||||
|
return &ToolboxQueryLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ToolboxQueryLogic) ToolboxQuery(req *types.ToolboxQueryReq) (resp *types.ToolboxQueryResp, err error) {
|
||||||
|
// 调用toolboxService执行工具查询
|
||||||
|
result, err := l.svcCtx.ToolboxService.Query(l.ctx, req.ToolKey, req.Params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.ToolboxQueryResp{
|
||||||
|
ToolKey: req.ToolKey,
|
||||||
|
Result: result,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
66
app/main/api/internal/logic/upload/serveuploadedfilelogic.go
Normal file
66
app/main/api/internal/logic/upload/serveuploadedfilelogic.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package upload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServeUploadedFileLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServeUploadedFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ServeUploadedFileLogic {
|
||||||
|
return &ServeUploadedFileLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ServeUploadedFileLogic) ServeUploadedFile(req *types.ServeUploadedFileReq) (resp *types.ServeUploadedFileResp, err error) {
|
||||||
|
fileName := strings.TrimSpace(req.FileName)
|
||||||
|
if fileName == "" {
|
||||||
|
return nil, errors.Wrap(xerr.NewErrMsg("缺少文件名"), "fileName empty")
|
||||||
|
}
|
||||||
|
// 只允许文件名,禁止路径穿越
|
||||||
|
if strings.Contains(fileName, "..") || filepath.Base(fileName) != fileName {
|
||||||
|
return nil, errors.Wrap(xerr.NewErrMsg("非法文件名"), fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := []string{
|
||||||
|
"data/uploads",
|
||||||
|
"../data/uploads",
|
||||||
|
"../../data/uploads",
|
||||||
|
"../../../data/uploads",
|
||||||
|
}
|
||||||
|
for _, c := range candidates {
|
||||||
|
abs, _ := filepath.Abs(c)
|
||||||
|
fullPath := filepath.Join(abs, fileName)
|
||||||
|
if info, err := os.Stat(fullPath); err == nil && !info.IsDir() {
|
||||||
|
contentType := "image/jpeg"
|
||||||
|
if strings.HasSuffix(strings.ToLower(fileName), ".png") {
|
||||||
|
contentType = "image/png"
|
||||||
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".gif") {
|
||||||
|
contentType = "image/gif"
|
||||||
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".webp") {
|
||||||
|
contentType = "image/webp"
|
||||||
|
}
|
||||||
|
return &types.ServeUploadedFileResp{
|
||||||
|
FilePath: fullPath,
|
||||||
|
ContentType: contentType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("文件不存在"), "fileName=%s", fileName)
|
||||||
|
}
|
||||||
137
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
137
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package upload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tyc-server/app/main/api/internal/svc"
|
||||||
|
"tyc-server/app/main/api/internal/types"
|
||||||
|
"tyc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxImageSize = 3 * 1024 * 1024 // 3MB
|
||||||
|
const defaultTempFileMaxAgeH = 24
|
||||||
|
|
||||||
|
type UploadImageLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUploadImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadImageLogic {
|
||||||
|
return &UploadImageLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UploadImageLogic) UploadImage(req *types.UploadImageReq) (resp *types.UploadImageResp, err error) {
|
||||||
|
decoded, decErr := base64.StdEncoding.DecodeString(req.ImageBase64)
|
||||||
|
if decErr != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("图片 base64 格式错误"), "%v", decErr)
|
||||||
|
}
|
||||||
|
if len(decoded) > maxImageSize {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("图片不能超过 3M"), "size=%d", len(decoded))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按文件内容 hash 命名,相同文件复用同一 URL,避免重复传输与刷流量
|
||||||
|
hashSum := sha256.Sum256(decoded)
|
||||||
|
hashHex := hex.EncodeToString(hashSum[:])
|
||||||
|
fileName := hashHex + ".jpg"
|
||||||
|
|
||||||
|
dir := l.uploadStoragePath()
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建上传目录失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Join(dir, fileName)
|
||||||
|
// 若已存在同 hash 文件,直接返回 URL,不重复写入
|
||||||
|
if _, statErr := os.Stat(filePath); statErr == nil {
|
||||||
|
url := l.buildURL(fileName)
|
||||||
|
logx.Infof("upload image dedup by hash, file=%s", fileName)
|
||||||
|
return &types.UploadImageResp{Url: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filePath, decoded, 0644); err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存图片失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步清理过期临时文件,不阻塞响应
|
||||||
|
go l.deleteOldUploads(dir)
|
||||||
|
|
||||||
|
url := l.buildURL(fileName)
|
||||||
|
logx.Infof("upload image ok, file=%s", fileName)
|
||||||
|
return &types.UploadImageResp{Url: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UploadImageLogic) buildURL(fileName string) string {
|
||||||
|
baseURL := l.svcCtx.Config.Upload.FileBaseURL
|
||||||
|
if baseURL == "" && l.svcCtx.Config.PublicBaseURL != "" {
|
||||||
|
// 兜底:如果未单独配置 Upload.FileBaseURL,则使用公共域名拼接默认上传路径
|
||||||
|
baseURL = strings.TrimRight(l.svcCtx.Config.PublicBaseURL, "/") + "/api/v1/upload/file"
|
||||||
|
}
|
||||||
|
if baseURL == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", baseURL, fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UploadImageLogic) uploadStoragePath() string {
|
||||||
|
candidates := []string{
|
||||||
|
"data/uploads",
|
||||||
|
"../data/uploads",
|
||||||
|
"../../data/uploads",
|
||||||
|
"../../../data/uploads",
|
||||||
|
}
|
||||||
|
for _, c := range candidates {
|
||||||
|
abs, _ := filepath.Abs(c)
|
||||||
|
if err := os.MkdirAll(abs, 0755); err == nil {
|
||||||
|
return abs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abs, _ := filepath.Abs(candidates[0])
|
||||||
|
return abs
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteOldUploads 删除目录下超过保留时长的临时文件
|
||||||
|
func (l *UploadImageLogic) deleteOldUploads(dir string) {
|
||||||
|
maxAgeH := l.svcCtx.Config.Upload.TempFileMaxAgeH
|
||||||
|
if maxAgeH <= 0 {
|
||||||
|
maxAgeH = defaultTempFileMaxAgeH
|
||||||
|
}
|
||||||
|
cutoff := time.Now().Add(-time.Duration(maxAgeH) * time.Hour)
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("deleteOldUploads ReadDir: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
path := filepath.Join(dir, e.Name())
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.ModTime().Before(cutoff) {
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
l.Errorf("deleteOldUploads Remove %s: %v", path, err)
|
||||||
|
} else {
|
||||||
|
l.Infof("deleteOldUploads removed %s", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 加密手机号失败: %+v", err)
|
||||||
}
|
}
|
||||||
// 检查验证码(开发环境可跳过)
|
// 短信验证码校验(开发环境可跳过)
|
||||||
if os.Getenv("ENV") != "development" {
|
if os.Getenv("ENV") != "development" {
|
||||||
redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile)
|
redisKey := fmt.Sprintf("%s:%s", "login", encryptedMobile)
|
||||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
|
"tyc-server/common/ctxdata"
|
||||||
"tyc-server/common/xerr"
|
"tyc-server/common/xerr"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -39,6 +41,11 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe
|
|||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已登录正式用户 + 静默授权 code:把当前公众号 openid 写入 user_auth(wxh5_openid),供微信内 JSAPI 支付使用
|
||||||
|
if claims, claimsErr := ctxdata.GetClaimsFromCtx(l.ctx); claimsErr == nil && claims != nil && claims.UserType == model.UserTypeNormal {
|
||||||
|
return l.bindWxh5OpenidToNormalUser(claims.UserId, accessTokenResp.Openid)
|
||||||
|
}
|
||||||
|
|
||||||
// Step 2: 查找用户授权信息
|
// Step 2: 查找用户授权信息
|
||||||
userAuth, findErr := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5OpenID, accessTokenResp.Openid)
|
userAuth, findErr := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5OpenID, accessTokenResp.Openid)
|
||||||
if findErr != nil && !errors.Is(findErr, model.ErrNotFound) {
|
if findErr != nil && !errors.Is(findErr, model.ErrNotFound) {
|
||||||
@@ -94,6 +101,53 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindWxh5OpenidToNormalUser 将 snsapi_base 换得的 openid 绑定到当前正式用户(插入或更新 user_auth)。
|
||||||
|
// 临时用户不能走此分支:需在绑定手机号时由 TempUserBindUser 写入 user_auth,避免与临时转正逻辑冲突。
|
||||||
|
func (l *WxH5AuthLogic) bindWxh5OpenidToNormalUser(normalUserID int64, openid string) (*types.WXH5AuthResp, error) {
|
||||||
|
openid = strings.TrimSpace(openid)
|
||||||
|
if openid == "" {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "微信 openid 为空")
|
||||||
|
}
|
||||||
|
existingByOpenid, err := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxh5OpenID, openid)
|
||||||
|
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 wxh5_openid 失败: %v", err)
|
||||||
|
}
|
||||||
|
if existingByOpenid != nil && existingByOpenid.UserId != normalUserID {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrMsg("该微信已绑定其他账号"), "wxh5_openid 已被占用 user_id=%d", existingByOpenid.UserId)
|
||||||
|
}
|
||||||
|
rowByUser, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, normalUserID, model.UserAuthTypeWxh5OpenID)
|
||||||
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
|
_, insErr := l.svcCtx.UserAuthModel.Insert(l.ctx, nil, &model.UserAuth{
|
||||||
|
UserId: normalUserID,
|
||||||
|
AuthType: model.UserAuthTypeWxh5OpenID,
|
||||||
|
AuthKey: openid,
|
||||||
|
})
|
||||||
|
if insErr != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "写入 wxh5_openid 失败: %v", insErr)
|
||||||
|
}
|
||||||
|
logx.WithContext(l.ctx).Infof("[WxH5Auth] 已写入 user_auth wxh5_openid user_id=%d", normalUserID)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户 wxh5_openid 失败: %v", err)
|
||||||
|
} else if rowByUser.AuthKey != openid {
|
||||||
|
rowByUser.AuthKey = openid
|
||||||
|
if _, updErr := l.svcCtx.UserAuthModel.Update(l.ctx, nil, rowByUser); updErr != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新 wxh5_openid 失败: %v", updErr)
|
||||||
|
}
|
||||||
|
logx.WithContext(l.ctx).Infof("[WxH5Auth] 已更新 user_auth wxh5_openid user_id=%d", normalUserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, normalUserID, model.UserTypeNormal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成JWT token失败: %v", err)
|
||||||
|
}
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return &types.WXH5AuthResp{
|
||||||
|
AccessToken: token,
|
||||||
|
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||||
|
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type AccessTokenResp struct {
|
type AccessTokenResp struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
Openid string `json:"openid"`
|
Openid string `json:"openid"`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -12,7 +13,7 @@ const (
|
|||||||
func GlobalSourceInterceptor(next http.HandlerFunc) http.HandlerFunc {
|
func GlobalSourceInterceptor(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 获取请求头 X-Platform 的值
|
// 获取请求头 X-Platform 的值
|
||||||
platform := r.Header.Get(PlatformKey)
|
platform := strings.TrimSpace(r.Header.Get(PlatformKey))
|
||||||
|
|
||||||
// 将值放入新的 context 中
|
// 将值放入新的 context 中
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"tyc-server/app/main/model"
|
||||||
|
"tyc-server/common/ctxdata"
|
||||||
|
"tyc-server/common/result"
|
||||||
|
"tyc-server/common/xerr"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserDisableInterceptorMiddleware 检查用户是否被封禁,封禁用户直接拦截并返回错误
|
||||||
|
type UserDisableInterceptorMiddleware struct {
|
||||||
|
UserModel model.UserModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserDisableInterceptorMiddleware 创建用户封禁检查中间件
|
||||||
|
func NewUserDisableInterceptorMiddleware(userModel model.UserModel) *UserDisableInterceptorMiddleware {
|
||||||
|
return &UserDisableInterceptorMiddleware{
|
||||||
|
UserModel: userModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 处理请求:当用户已登录且被封禁时,直接返回错误
|
||||||
|
func (m *UserDisableInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, err := ctxdata.GetClaimsFromCtx(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
// 无登录信息,放行(由后续认证中间件处理)
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 管理员不检查 user 表的 disable 字段
|
||||||
|
if claims.UserType == model.UserTypeAdmin {
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户是否被封禁
|
||||||
|
user, err := m.UserModel.FindOne(r.Context(), claims.UserId)
|
||||||
|
if err != nil {
|
||||||
|
// 用户不存在(如管理员账号)或查询失败,放行
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 表示已禁用
|
||||||
|
if user.Disable == 1 {
|
||||||
|
errcode := xerr.USER_DISABLED
|
||||||
|
errmsg := xerr.MapErrMsg(errcode)
|
||||||
|
httpx.WriteJson(w, http.StatusOK, result.Error(errcode, errmsg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,26 @@ var payload struct {
|
|||||||
OrderID int64 `json:"order_id"`
|
OrderID int64 `json:"order_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 仅通过异步回调回写结果的车辆接口 ApiID
|
||||||
|
var asyncVehicleApiIDs = map[string]bool{
|
||||||
|
"QCXG1U4U": true, // 车辆里程混合
|
||||||
|
"QCXG3Y6B": true, // 车辆维保简版
|
||||||
|
"QCXG3Z3L": true, // 车辆维保详细版
|
||||||
|
"QCXGP00W": true, // 车辆出险详版
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAllAsyncVehicleQuery(responseData []service.APIResponseData) bool {
|
||||||
|
if len(responseData) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, r := range responseData {
|
||||||
|
if !asyncVehicleApiIDs[r.ApiID] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
|
func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
|
||||||
// 从任务的负载中解码数据
|
// 从任务的负载中解码数据
|
||||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||||
@@ -76,11 +96,31 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
return fmt.Errorf("解密参数失败: %+v", aesdecryptErr)
|
return fmt.Errorf("解密参数失败: %+v", aesdecryptErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 为避免 base64 图片等大字段撑爆 query.query_params 列,这里对参数做一次瘦身:
|
||||||
|
// 删除纯 base64 图片字段(如 vlphoto_data / photo_data / image_base64),仅保留必要的文本信息。
|
||||||
|
var originalParams map[string]interface{}
|
||||||
|
if err := json.Unmarshal(decryptData, &originalParams); err != nil {
|
||||||
|
return fmt.Errorf("解析原始参数失败: %+v", err)
|
||||||
|
}
|
||||||
|
delete(originalParams, "vlphoto_data")
|
||||||
|
delete(originalParams, "photo_data")
|
||||||
|
delete(originalParams, "image_base64")
|
||||||
|
|
||||||
|
slimBytes, err := json.Marshal(originalParams)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("序列化精简后的参数失败: %+v", err)
|
||||||
|
}
|
||||||
|
slimEncrypted, err := crypto.AesEncrypt(slimBytes, key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("加密精简后的参数失败: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
query := &model.Query{
|
query := &model.Query{
|
||||||
OrderId: order.Id,
|
OrderId: order.Id,
|
||||||
UserId: order.UserId,
|
UserId: order.UserId,
|
||||||
ProductId: product.Id,
|
ProductId: product.Id,
|
||||||
QueryParams: data.Params,
|
// 仅保存精简后的参数,避免 base64 图片占满列
|
||||||
|
QueryParams: slimEncrypted,
|
||||||
QueryState: "pending",
|
QueryState: "pending",
|
||||||
}
|
}
|
||||||
result, insertQueryErr := l.svcCtx.QueryModel.Insert(ctx, nil, query)
|
result, insertQueryErr := l.svcCtx.QueryModel.Insert(ctx, nil, query)
|
||||||
@@ -100,7 +140,7 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
return fmt.Errorf("获取插入后的查询记录失败: %+v", err)
|
return fmt.Errorf("获取插入后的查询记录失败: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析解密后的参数获取用户信息
|
// 解析解密后的参数获取用户信息(这里依然使用完整参数,后续会在写回时再次瘦身)
|
||||||
var userInfo map[string]interface{}
|
var userInfo map[string]interface{}
|
||||||
if err := json.Unmarshal(decryptData, &userInfo); err != nil {
|
if err := json.Unmarshal(decryptData, &userInfo); err != nil {
|
||||||
return fmt.Errorf("解析用户信息失败: %+v", err)
|
return fmt.Errorf("解析用户信息失败: %+v", err)
|
||||||
@@ -120,17 +160,31 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
downloadURL := l.buildAuthorizationDownloadURL(authDoc.FileName)
|
downloadURL := l.buildAuthorizationDownloadURL(authDoc.FileName)
|
||||||
userInfo["authorization_url"] = downloadURL
|
userInfo["authorization_url"] = downloadURL
|
||||||
|
|
||||||
// 重新序列化用户信息
|
// 1)完整参数(包含图片和授权书 URL)用于后续调用天远接口
|
||||||
updatedDecryptData, marshalErr := json.Marshal(userInfo)
|
updatedDecryptData, marshalErr := json.Marshal(userInfo)
|
||||||
if marshalErr != nil {
|
if marshalErr != nil {
|
||||||
logx.Errorf("序列化用户信息失败: %v", marshalErr)
|
logx.Errorf("序列化用户信息失败: %v", marshalErr)
|
||||||
} else {
|
} else {
|
||||||
// 重新加密更新后的数据
|
decryptData = updatedDecryptData
|
||||||
encryptedUpdatedData, encryptErr := crypto.AesEncrypt(updatedDecryptData, key)
|
|
||||||
if encryptErr != nil {
|
// 2)精简版本(去掉图片类大字段)仅用于持久化到 query.QueryParams
|
||||||
logx.Errorf("重新加密用户信息失败: %v", encryptErr)
|
slimUserInfo := make(map[string]interface{}, len(userInfo))
|
||||||
|
for k, v := range userInfo {
|
||||||
|
slimUserInfo[k] = v
|
||||||
|
}
|
||||||
|
delete(slimUserInfo, "vlphoto_data")
|
||||||
|
delete(slimUserInfo, "photo_data")
|
||||||
|
delete(slimUserInfo, "image_base64")
|
||||||
|
|
||||||
|
slimUpdatedBytes, marshalSlimErr := json.Marshal(slimUserInfo)
|
||||||
|
if marshalSlimErr != nil {
|
||||||
|
logx.Errorf("序列化精简用户信息失败: %v", marshalSlimErr)
|
||||||
|
} else {
|
||||||
|
// 重新加密精简后的数据写回数据库
|
||||||
|
encryptedUpdatedData, encryptErr := crypto.AesEncrypt(slimUpdatedBytes, key)
|
||||||
|
if encryptErr != nil {
|
||||||
|
logx.Errorf("重新加密精简用户信息失败: %v", encryptErr)
|
||||||
} else {
|
} else {
|
||||||
// 更新查询记录中的参数
|
|
||||||
query.QueryParams = string(encryptedUpdatedData)
|
query.QueryParams = string(encryptedUpdatedData)
|
||||||
updateParamsErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
updateParamsErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
||||||
if updateParamsErr != nil {
|
if updateParamsErr != nil {
|
||||||
@@ -139,7 +193,7 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
logx.Infof("成功更新查询参数,包含授权书URL: %s", downloadURL)
|
logx.Infof("成功更新查询参数,包含授权书URL: %s", downloadURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decryptData = updatedDecryptData
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +206,8 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
responseData = []service.APIResponseData{}
|
responseData = []service.APIResponseData{}
|
||||||
} else {
|
} else {
|
||||||
var processErr error
|
var processErr error
|
||||||
responseData, processErr = l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
|
// 传入订单号,用于在 ApiRequestService 中为异步车辆接口生成回调地址(return_url)
|
||||||
|
responseData, processErr = l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id, order.OrderNo)
|
||||||
if processErr != nil {
|
if processErr != nil {
|
||||||
return l.handleError(ctx, processErr, order, query)
|
return l.handleError(ctx, processErr, order, query)
|
||||||
}
|
}
|
||||||
@@ -160,7 +215,6 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
|
|
||||||
// 计算成功模块的总成本价
|
// 计算成功模块的总成本价
|
||||||
totalCostPrice := 0.0
|
totalCostPrice := 0.0
|
||||||
if responseData != nil {
|
|
||||||
for _, item := range responseData {
|
for _, item := range responseData {
|
||||||
if item.Success {
|
if item.Success {
|
||||||
// 根据API ID查找功能模块
|
// 根据API ID查找功能模块
|
||||||
@@ -173,7 +227,6 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
totalCostPrice += feature.CostPrice
|
totalCostPrice += feature.CostPrice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 更新订单的销售成本
|
// 更新订单的销售成本
|
||||||
order.SalesCost = totalCostPrice
|
order.SalesCost = totalCostPrice
|
||||||
@@ -204,12 +257,15 @@ func (l *PaySuccessNotifyUserHandler) ProcessTask(ctx context.Context, t *asynq.
|
|||||||
return l.handleError(ctx, err, order, query)
|
return l.handleError(ctx, err, order, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 若当前产品全部为异步车辆接口(结果通过回调回写),则保持 pending,由回调再置为 success
|
||||||
|
if !isAllAsyncVehicleQuery(responseData) {
|
||||||
query.QueryState = "success"
|
query.QueryState = "success"
|
||||||
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
updateQueryErr := l.svcCtx.QueryModel.UpdateWithVersion(ctx, nil, query)
|
||||||
if updateQueryErr != nil {
|
if updateQueryErr != nil {
|
||||||
updateQueryErr = fmt.Errorf("修改查询状态失败: %v", updateQueryErr)
|
updateQueryErr = fmt.Errorf("修改查询状态失败: %v", updateQueryErr)
|
||||||
return l.handleError(ctx, updateQueryErr, order, query)
|
return l.handleError(ctx, updateQueryErr, order, query)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = l.svcCtx.AgentService.AgentProcess(ctx, order)
|
err = l.svcCtx.AgentService.AgentProcess(ctx, order)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -266,8 +322,13 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
|
|||||||
logx.Infof("已发起微信退款申请, orderID: %d, amount: %f", order.Id, order.Amount)
|
logx.Infof("已发起微信退款申请, orderID: %d, amount: %f", order.Id, order.Amount)
|
||||||
return asynq.SkipRetry
|
return asynq.SkipRetry
|
||||||
} else {
|
} else {
|
||||||
// 支付宝退款为同步结果,这里直接根据返回结果更新订单和佣金/钱包
|
// 支付宝退款为同步结果,优先按订单记录的 payment_merchant 选择商户;
|
||||||
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.OrderNo, order.Amount)
|
// 老订单若未写入商户号,则在 AliRefund 内按时间区间兜底。
|
||||||
|
orderPayTime := &order.CreateTime
|
||||||
|
if order.PayTime.Valid {
|
||||||
|
orderPayTime = &order.PayTime.Time
|
||||||
|
}
|
||||||
|
refund, refundErr := l.svcCtx.AlipayService.AliRefund(ctx, order.PaymentMerchant, order.OrderNo, order.Amount, orderPayTime, "")
|
||||||
if refundErr != nil {
|
if refundErr != nil {
|
||||||
logx.Error(refundErr)
|
logx.Error(refundErr)
|
||||||
return asynq.SkipRetry
|
return asynq.SkipRetry
|
||||||
|
|||||||
113
app/main/api/internal/service/1.md
Normal file
113
app/main/api/internal/service/1.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
首页
|
||||||
|
生活服务
|
||||||
|
垃圾分类问答
|
||||||
|
垃圾分类问答接口
|
||||||
|
158 13540
|
||||||
|
垃圾分类问答正常服务支持MCP
|
||||||
|
提供垃圾分类知识判断题,帮助用户掌握干湿垃圾、可回收物等分类标准。
|
||||||
|
会员免费・升级会员畅享160+免费接口,立即升级>>
|
||||||
|
收藏
|
||||||
|
普通会员
|
||||||
|
100次/天
|
||||||
|
高级会员
|
||||||
|
1W次/天
|
||||||
|
黄金会员
|
||||||
|
50W次/天
|
||||||
|
钻石会员
|
||||||
|
不限次数
|
||||||
|
|
||||||
|
▼ 接口文档
|
||||||
|
|
||||||
|
价格
|
||||||
|
|
||||||
|
返回示例
|
||||||
|
|
||||||
|
参考代码
|
||||||
|
|
||||||
|
MCP服务
|
||||||
|
|
||||||
|
帮助
|
||||||
|
|
||||||
|
生成小程序
|
||||||
|
垃圾分类问答判断题,每次调用接口返回一个废弃物名称及正确分类。
|
||||||
|
接入点列表:
|
||||||
|
|
||||||
|
垃圾分类问答
|
||||||
|
相关资源:
|
||||||
|
|
||||||
|
垃圾分类问答功能演示
|
||||||
|
▼ 接口信息
|
||||||
|
随机返回带结果的垃圾分类知识库
|
||||||
|
|
||||||
|
接口地址:https://apis.tianapi.com/anslajifenlei/index?key={apiKey}
|
||||||
|
支持协议:http/https
|
||||||
|
请求方法:get/post
|
||||||
|
返回格式:utf-8 json
|
||||||
|
▼ 请求参数
|
||||||
|
上传文件时请使用标准表单格式 multipart/form-data
|
||||||
|
|
||||||
|
普通参数请使用默认表单格式 application/x-www-form-urlencoded
|
||||||
|
|
||||||
|
当参数值(如url、base64)包含特殊字符时,建议urlencode编码后传递
|
||||||
|
|
||||||
|
名称 类型 必须 示例值/默认值 说明
|
||||||
|
key string 是 您自己的ApiKey(注册账号后获得) API密钥
|
||||||
|
▼ 返回示例
|
||||||
|
接口数据示例仅作为预览参考,请以实际测试结果为准
|
||||||
|
|
||||||
|
旧接口域名返回的数据结构和现在略有不同,请查看说明
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
成功调用,返回内容并产生计费:
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"msg": "success",
|
||||||
|
"code": 200,
|
||||||
|
"result": {
|
||||||
|
"name": "西红柿",
|
||||||
|
"type": 2,
|
||||||
|
"explain": "湿垃圾"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
失败调用,查看接口错误码释义:
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"code": 150,
|
||||||
|
"msg": "API可用次数不足"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
▼ 返回参数
|
||||||
|
公共参数指所有接口都会返回的参数,应用参数每个接口都不同
|
||||||
|
|
||||||
|
建议对返回的字段进行存在性与类型校验,可参考API安全接入指南
|
||||||
|
|
||||||
|
名称 类型 示例值 说明
|
||||||
|
公共参数
|
||||||
|
code int 200 状态码
|
||||||
|
msg string success 错误信息
|
||||||
|
result object {} 返回结果集
|
||||||
|
应用参数
|
||||||
|
name string 西红柿 垃圾物品
|
||||||
|
type int 2 垃圾分类,0为可回收、1为有害、2为厨余(湿)、3为其他(干)
|
||||||
|
explain string 湿垃圾 中文释义
|
||||||
|
▼ 接口价格
|
||||||
|
本接口为会员免费类接口,可根据业务需求选择升级会员方案>>
|
||||||
|
|
||||||
|
|
||||||
|
不同会员方案仅每日调用量等配额上限不同,数据本身无区别
|
||||||
|
|
||||||
|
|
||||||
|
会员方案 免费接口数 每日调用量 QPS 价格
|
||||||
|
普通会员 10个 100次 3 免费
|
||||||
|
高级会员 不限 1万次 20 29元/月、348元/年169元/年惠
|
||||||
|
黄金会员 不限 50万次 30 89元/月、1068元/年529元/年惠
|
||||||
|
钻石会员 不限 不限次 60 3380元/年1699元/年惠
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -16,9 +16,38 @@ import (
|
|||||||
"github.com/smartwalle/alipay/v3"
|
"github.com/smartwalle/alipay/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// bak 支付宝仅用于 [AlipayBakRefundStart, AlipayBakRefundEnd) 区间内支付订单的退款,区间外使用正式 client(CST)
|
||||||
|
var (
|
||||||
|
AlipayBakRefundStart = time.Date(2026, 1, 25, 16, 38, 17, 0, time.FixedZone("CST", 8*3600)) // Sun Jan 25 2026 16:38:17 GMT+0800 之前用正式
|
||||||
|
AlipayBakRefundEnd = time.Date(2026, 2, 2, 18, 26, 0, 0, time.FixedZone("CST", 8*3600)) // 2026-02-02 18:26 之后用正式
|
||||||
|
)
|
||||||
|
|
||||||
type AliPayService struct {
|
type AliPayService struct {
|
||||||
config config.AlipayConfig
|
config config.AlipayConfig
|
||||||
AlipayClient *alipay.Client
|
AlipayClient *alipay.Client
|
||||||
|
AlipayClientBak *alipay.Client // 仅用于 [2026-01-25 16:38:17, 2026-02-02 18:26) 区间内订单的退款
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientForMerchant 根据商户标识与订单时间选择对应的支付宝 client。
|
||||||
|
// - merchant == "two" 且 Bak 存在:优先返回 Bak;
|
||||||
|
// - merchant == "one" 或空:默认返回主商户;
|
||||||
|
// - merchant 为空且 orderPayTime 落在备份时间区间:兼容老订单,走 Bak。
|
||||||
|
func (a *AliPayService) clientForMerchant(merchant string, orderPayTime *time.Time) *alipay.Client {
|
||||||
|
// 显式指定 two,则优先走 Bak
|
||||||
|
if merchant == "two" && a.AlipayClientBak != nil {
|
||||||
|
return a.AlipayClientBak
|
||||||
|
}
|
||||||
|
// 显式指定 one 或其他未知标识,一律走主商户
|
||||||
|
if merchant == "one" || merchant == "" {
|
||||||
|
// 对于老订单未写入 merchant 的情况,继续保留时间区间兜底逻辑
|
||||||
|
if merchant == "" && orderPayTime != nil && a.AlipayClientBak != nil &&
|
||||||
|
!orderPayTime.Before(AlipayBakRefundStart) && orderPayTime.Before(AlipayBakRefundEnd) {
|
||||||
|
return a.AlipayClientBak
|
||||||
|
}
|
||||||
|
return a.AlipayClient
|
||||||
|
}
|
||||||
|
// 兜底:未知标识时仍走主商户,避免因为配置问题导致整体不可用
|
||||||
|
return a.AlipayClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAliPayService 是一个构造函数,用于初始化 AliPayService
|
// NewAliPayService 是一个构造函数,用于初始化 AliPayService
|
||||||
@@ -44,14 +73,34 @@ func NewAliPayService(c config.Config) *AliPayService {
|
|||||||
panic(fmt.Sprintf("加载根证书失败: %v", err))
|
panic(fmt.Sprintf("加载根证书失败: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AliPayService{
|
svc := &AliPayService{
|
||||||
config: c.Alipay,
|
config: c.Alipay,
|
||||||
AlipayClient: client,
|
AlipayClient: client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化 bak 支付宝客户端(仅用于 [2026-01-25 16:38:17, 2026-02-02 18:26) 区间内订单的退款)
|
||||||
|
if c.Alipay.AppIDBak != "" && c.Alipay.PrivateKeyBak != "" {
|
||||||
|
bakClient, err := alipay.New(c.Alipay.AppIDBak, c.Alipay.PrivateKeyBak, c.Alipay.IsProduction)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("创建支付宝 bak 客户端失败: %v", err))
|
||||||
|
}
|
||||||
|
if err = bakClient.LoadAppCertPublicKeyFromFile(c.Alipay.AppCertPathBak); err != nil {
|
||||||
|
panic(fmt.Sprintf("加载 bak 应用公钥证书失败: %v", err))
|
||||||
|
}
|
||||||
|
if err = bakClient.LoadAlipayCertPublicKeyFromFile(c.Alipay.AlipayCertPathBak); err != nil {
|
||||||
|
panic(fmt.Sprintf("加载 bak 支付宝公钥证书失败: %v", err))
|
||||||
|
}
|
||||||
|
if err = bakClient.LoadAliPayRootCertFromFile(c.Alipay.AlipayRootCertPathBak); err != nil {
|
||||||
|
panic(fmt.Sprintf("加载 bak 根证书失败: %v", err))
|
||||||
|
}
|
||||||
|
svc.AlipayClientBak = bakClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, outTradeNo string) (string, error) {
|
return svc
|
||||||
client := a.AlipayClient
|
}
|
||||||
|
|
||||||
|
func (a *AliPayService) CreateAlipayAppOrder(merchant string, amount float64, subject string, outTradeNo string) (string, error) {
|
||||||
|
client := a.clientForMerchant(merchant, nil)
|
||||||
totalAmount := lzUtils.ToAlipayAmount(amount)
|
totalAmount := lzUtils.ToAlipayAmount(amount)
|
||||||
// 构造移动支付请求
|
// 构造移动支付请求
|
||||||
p := alipay.TradeAppPay{
|
p := alipay.TradeAppPay{
|
||||||
@@ -74,8 +123,8 @@ func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, out
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateAlipayH5Order 创建支付宝H5支付订单
|
// CreateAlipayH5Order 创建支付宝H5支付订单
|
||||||
func (a *AliPayService) CreateAlipayH5Order(amount float64, subject string, outTradeNo string) (string, error) {
|
func (a *AliPayService) CreateAlipayH5Order(merchant string, amount float64, subject string, outTradeNo string) (string, error) {
|
||||||
client := a.AlipayClient
|
client := a.clientForMerchant(merchant, nil)
|
||||||
totalAmount := lzUtils.ToAlipayAmount(amount)
|
totalAmount := lzUtils.ToAlipayAmount(amount)
|
||||||
// 构造H5支付请求
|
// 构造H5支付请求
|
||||||
p := alipay.TradeWapPay{
|
p := alipay.TradeWapPay{
|
||||||
@@ -97,8 +146,9 @@ func (a *AliPayService) CreateAlipayH5Order(amount float64, subject string, outT
|
|||||||
return payUrl.String(), nil
|
return payUrl.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAlipayOrder 根据平台类型创建支付宝支付订单
|
// CreateAlipayOrder 根据平台类型和商户标识创建支付宝支付订单
|
||||||
func (a *AliPayService) CreateAlipayOrder(ctx context.Context, amount float64, subject string, outTradeNo string) (string, error) {
|
// merchant: 商户标识,目前约定 "one"=主商户, "two"=备商户
|
||||||
|
func (a *AliPayService) CreateAlipayOrder(ctx context.Context, merchant string, amount float64, subject string, outTradeNo string) (string, error) {
|
||||||
// 根据 ctx 中的 platform 判断平台
|
// 根据 ctx 中的 platform 判断平台
|
||||||
platform, platformOk := ctx.Value("platform").(string)
|
platform, platformOk := ctx.Value("platform").(string)
|
||||||
if !platformOk {
|
if !platformOk {
|
||||||
@@ -107,52 +157,58 @@ func (a *AliPayService) CreateAlipayOrder(ctx context.Context, amount float64, s
|
|||||||
switch platform {
|
switch platform {
|
||||||
case model.PlatformApp:
|
case model.PlatformApp:
|
||||||
// 调用App支付的创建方法
|
// 调用App支付的创建方法
|
||||||
return a.CreateAlipayAppOrder(amount, subject, outTradeNo)
|
return a.CreateAlipayAppOrder(merchant, amount, subject, outTradeNo)
|
||||||
case model.PlatformH5:
|
case model.PlatformH5:
|
||||||
// 调用H5支付的创建方法,并传入 returnUrl
|
// 调用H5支付的创建方法,并传入 returnUrl
|
||||||
return a.CreateAlipayH5Order(amount, subject, outTradeNo)
|
return a.CreateAlipayH5Order(merchant, amount, subject, outTradeNo)
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("不支持的支付平台: %s", platform)
|
return "", fmt.Errorf("不支持的支付平台: %s", platform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AliRefund 发起支付宝退款
|
// AliRefund 发起支付宝退款。
|
||||||
func (a *AliPayService) AliRefund(ctx context.Context, outTradeNo string, refundAmount float64) (*alipay.TradeRefundRsp, error) {
|
// merchant: 支付商户标识(one/two)。为空时按老逻辑仅在备份时间区间内使用 Bak。
|
||||||
|
// orderPayTime 为订单支付时间(或创建时间);用于老订单按时间区间选择商户;传 nil 则忽略时间区间。
|
||||||
|
// outRequestNo 为商户退款请求号,同一笔退款需唯一;传空则使用 "refund-"+outTradeNo,重试时建议传入唯一号避免支付宝报重复。
|
||||||
|
func (a *AliPayService) AliRefund(ctx context.Context, merchant string, outTradeNo string, refundAmount float64, orderPayTime *time.Time, outRequestNo string) (*alipay.TradeRefundRsp, error) {
|
||||||
|
client := a.clientForMerchant(merchant, orderPayTime)
|
||||||
|
|
||||||
|
if outRequestNo == "" {
|
||||||
|
outRequestNo = fmt.Sprintf("refund-%s", outTradeNo)
|
||||||
|
}
|
||||||
refund := alipay.TradeRefund{
|
refund := alipay.TradeRefund{
|
||||||
OutTradeNo: outTradeNo,
|
OutTradeNo: outTradeNo,
|
||||||
RefundAmount: lzUtils.ToAlipayAmount(refundAmount),
|
RefundAmount: lzUtils.ToAlipayAmount(refundAmount),
|
||||||
OutRequestNo: fmt.Sprintf("refund-%s", outTradeNo),
|
OutRequestNo: outRequestNo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发起退款请求
|
refundResp, err := client.TradeRefund(ctx, refund)
|
||||||
refundResp, err := a.AlipayClient.TradeRefund(ctx, refund)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("支付宝退款请求错误:%v", err)
|
return nil, fmt.Errorf("支付宝退款请求错误:%v", err)
|
||||||
}
|
}
|
||||||
return refundResp, nil
|
return refundResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleAliPaymentNotification 支付宝支付回调
|
// HandleAliPaymentNotification 支付宝支付回调验签。
|
||||||
func (a *AliPayService) HandleAliPaymentNotification(r *http.Request) (*alipay.Notification, error) {
|
// 由上层根据 out_trade_no 查出订单并传入对应商户标识 merchant。
|
||||||
// 解析表单
|
func (a *AliPayService) HandleAliPaymentNotification(merchant string, form url.Values) (*alipay.Notification, error) {
|
||||||
err := r.ParseForm()
|
client := a.clientForMerchant(merchant, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("解析请求表单失败:%v", err)
|
|
||||||
}
|
|
||||||
// 解析并验证通知,DecodeNotification 会自动验证签名
|
// 解析并验证通知,DecodeNotification 会自动验证签名
|
||||||
notification, err := a.AlipayClient.DecodeNotification(r.Form)
|
notification, err := client.DecodeNotification(form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("验证签名失败: %v", err)
|
return nil, fmt.Errorf("验证签名失败: %v", err)
|
||||||
}
|
}
|
||||||
return notification, nil
|
return notification, nil
|
||||||
}
|
}
|
||||||
func (a *AliPayService) QueryOrderStatus(ctx context.Context, outTradeNo string) (*alipay.TradeQueryRsp, error) {
|
// QueryOrderStatus 按商户标识查询支付宝订单状态
|
||||||
|
func (a *AliPayService) QueryOrderStatus(ctx context.Context, merchant string, outTradeNo string) (*alipay.TradeQueryRsp, error) {
|
||||||
|
client := a.clientForMerchant(merchant, nil)
|
||||||
queryRequest := alipay.TradeQuery{
|
queryRequest := alipay.TradeQuery{
|
||||||
OutTradeNo: outTradeNo,
|
OutTradeNo: outTradeNo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发起查询请求
|
// 发起查询请求
|
||||||
resp, err := a.AlipayClient.TradeQuery(ctx, queryRequest)
|
resp, err := client.TradeQuery(ctx, queryRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("查询支付宝订单失败: %v", err)
|
return nil, fmt.Errorf("查询支付宝订单失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"tyc-server/app/main/api/internal/config"
|
"tyc-server/app/main/api/internal/config"
|
||||||
tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
|
"tyc-server/pkg/lzkit/crypto"
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
@@ -37,18 +39,30 @@ type ApiRequestService struct {
|
|||||||
featureModel model.FeatureModel
|
featureModel model.FeatureModel
|
||||||
productFeatureModel model.ProductFeatureModel
|
productFeatureModel model.ProductFeatureModel
|
||||||
tianyuanapi *tianyuanapi.Client
|
tianyuanapi *tianyuanapi.Client
|
||||||
|
authService *AuthorizationService
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApiRequestService 是一个构造函数,用于初始化 ApiRequestService
|
// NewApiRequestService 是一个构造函数,用于初始化 ApiRequestService
|
||||||
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, tianyuanapi *tianyuanapi.Client) *ApiRequestService {
|
func NewApiRequestService(c config.Config, featureModel model.FeatureModel, productFeatureModel model.ProductFeatureModel, tianyuanapi *tianyuanapi.Client, authService *AuthorizationService) *ApiRequestService {
|
||||||
return &ApiRequestService{
|
return &ApiRequestService{
|
||||||
config: c,
|
config: c,
|
||||||
featureModel: featureModel,
|
featureModel: featureModel,
|
||||||
productFeatureModel: productFeatureModel,
|
productFeatureModel: productFeatureModel,
|
||||||
tianyuanapi: tianyuanapi,
|
tianyuanapi: tianyuanapi,
|
||||||
|
authService: authService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keysOfMap 返回 map 的 key 列表,便于 debug 日志展示参数结构而不是完整明文
|
||||||
|
func keysOfMap(m map[string]interface{}) []string {
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
type APIResponseData struct {
|
type APIResponseData struct {
|
||||||
ApiID string `json:"apiID"`
|
ApiID string `json:"apiID"`
|
||||||
Data json.RawMessage `json:"data"` // 这里用 RawMessage 来存储原始的 data
|
Data json.RawMessage `json:"data"` // 这里用 RawMessage 来存储原始的 data
|
||||||
@@ -58,7 +72,8 @@ type APIResponseData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProcessRequests 处理请求
|
// ProcessRequests 处理请求
|
||||||
func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]APIResponseData, error) {
|
// orderNo: 当前查询对应的订单号,用于为异步车辆类接口生成回调地址(return_url)
|
||||||
|
func (a *ApiRequestService) ProcessRequests(params []byte, productID int64, orderNo string) ([]APIResponseData, error) {
|
||||||
var ctx, cancel = context.WithCancel(context.Background())
|
var ctx, cancel = context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{
|
build := a.productFeatureModel.SelectBuilder().Where(squirrel.Eq{
|
||||||
@@ -85,6 +100,27 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]A
|
|||||||
if len(featureList) == 0 {
|
if len(featureList) == 0 {
|
||||||
return nil, errors.New("处理请求错误,产品无对应接口功能")
|
return nil, errors.New("处理请求错误,产品无对应接口功能")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在原始 params 上附加 order_no,供异步车辆类接口自动生成回调地址使用
|
||||||
|
var baseParams map[string]interface{}
|
||||||
|
if err := json.Unmarshal(params, &baseParams); err != nil {
|
||||||
|
logx.Errorf("解析查询参数失败, Params: %s, Error: %v", string(params), err)
|
||||||
|
return nil, fmt.Errorf("解析查询参数失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 参数转换:将 mobile 转换为 mobile_no
|
||||||
|
if mobile, exists := baseParams["mobile"]; exists {
|
||||||
|
baseParams["mobile_no"] = mobile
|
||||||
|
}
|
||||||
|
|
||||||
|
if orderNo != "" {
|
||||||
|
baseParams["order_no"] = orderNo
|
||||||
|
}
|
||||||
|
paramsWithOrder, err := json.Marshal(baseParams)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("序列化查询参数失败, Params: %s, Error: %v", string(params), err)
|
||||||
|
return nil, fmt.Errorf("序列化查询参数失败: %w", err)
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
resultsCh = make(chan APIResponseData, len(featureList))
|
resultsCh = make(chan APIResponseData, len(featureList))
|
||||||
@@ -118,7 +154,7 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]A
|
|||||||
tryCount := 0
|
tryCount := 0
|
||||||
for {
|
for {
|
||||||
tryCount++
|
tryCount++
|
||||||
resp, preprocessErr = a.PreprocessRequestApi(params, feature.ApiId)
|
resp, preprocessErr = a.PreprocessRequestApi(paramsWithOrder, feature.ApiId)
|
||||||
if preprocessErr == nil {
|
if preprocessErr == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -129,6 +165,9 @@ func (a *ApiRequestService) ProcessRequests(params []byte, productID int64) ([]A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if preprocessErr != nil {
|
if preprocessErr != nil {
|
||||||
|
// 在这里添加日志记录入参
|
||||||
|
logx.Errorf("API请求处理器失败, ApiID: %s, Params: %s, Error: %v", feature.ApiId, string(paramsWithOrder), preprocessErr)
|
||||||
|
|
||||||
result.Timestamp = timestamp
|
result.Timestamp = timestamp
|
||||||
result.Error = preprocessErr.Error()
|
result.Error = preprocessErr.Error()
|
||||||
result.Data = resp
|
result.Data = resp
|
||||||
@@ -175,9 +214,12 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
|
|||||||
"BehaviorRiskScan": (*ApiRequestService).ProcessBehaviorRiskScanRequest,
|
"BehaviorRiskScan": (*ApiRequestService).ProcessBehaviorRiskScanRequest,
|
||||||
"YYSYBE08": (*ApiRequestService).ProcessYYSYBE08Request,
|
"YYSYBE08": (*ApiRequestService).ProcessYYSYBE08Request,
|
||||||
"YYSY09CD": (*ApiRequestService).ProcessYYSY09CDRequest,
|
"YYSY09CD": (*ApiRequestService).ProcessYYSY09CDRequest,
|
||||||
|
"QCXGGB2Q": (*ApiRequestService).ProcessQCXGGB2QRequest,
|
||||||
|
"QCXGYTS2": (*ApiRequestService).ProcessQCXGYTS2Request,
|
||||||
|
"QCXG5F3A": (*ApiRequestService).ProcessQCXG5F3ARequest, //内部替换名下
|
||||||
"FLXG0687": (*ApiRequestService).ProcessFLXG0687Request,
|
"FLXG0687": (*ApiRequestService).ProcessFLXG0687Request,
|
||||||
"FLXG3D56": (*ApiRequestService).ProcessFLXG3D56Request,
|
"FLXG3D56": (*ApiRequestService).ProcessFLXG3D56Request,
|
||||||
"FLXG0V4B": (*ApiRequestService).ProcesFLXG0V4BRequest,
|
"FLXG0V4B": (*ApiRequestService).ProcessFLXG0V4BRequest,
|
||||||
"QYGL8271": (*ApiRequestService).ProcessQYGL8271Request,
|
"QYGL8271": (*ApiRequestService).ProcessQYGL8271Request,
|
||||||
"IVYZ5733": (*ApiRequestService).ProcessIVYZ5733Request,
|
"IVYZ5733": (*ApiRequestService).ProcessIVYZ5733Request,
|
||||||
"IVYZ9A2B": (*ApiRequestService).ProcessIVYZ9A2BRequest,
|
"IVYZ9A2B": (*ApiRequestService).ProcessIVYZ9A2BRequest,
|
||||||
@@ -197,6 +239,41 @@ var requestProcessors = map[string]func(*ApiRequestService, []byte) ([]byte, err
|
|||||||
"IVYZ3P9M": (*ApiRequestService).ProcessIVYZ3P9MRequest,
|
"IVYZ3P9M": (*ApiRequestService).ProcessIVYZ3P9MRequest,
|
||||||
"FLXG7E8F": (*ApiRequestService).ProcessFLXG7E8FRequest,
|
"FLXG7E8F": (*ApiRequestService).ProcessFLXG7E8FRequest,
|
||||||
"QCXG9P1C": (*ApiRequestService).ProcessQCXG9P1CFRequest,
|
"QCXG9P1C": (*ApiRequestService).ProcessQCXG9P1CFRequest,
|
||||||
|
// 车辆类接口(传参后续配置,先透传缓存 params)
|
||||||
|
"QCXG4D2E": (*ApiRequestService).ProcessQCXG4D2ERequest,
|
||||||
|
"QCXG5U0Z": (*ApiRequestService).ProcessQCXG5U0ZRequest,
|
||||||
|
"QCXG1U4U": (*ApiRequestService).ProcessQCXG1U4URequest,
|
||||||
|
"QCXGY7F2": (*ApiRequestService).ProcessQCXGY7F2Request,
|
||||||
|
"QCXG1H7Y": (*ApiRequestService).ProcessQCXG1H7YRequest,
|
||||||
|
"QCXG4I1Z": (*ApiRequestService).ProcessQCXG4I1ZRequest,
|
||||||
|
"QCXG3Y6B": (*ApiRequestService).ProcessQCXG3Y6BRequest,
|
||||||
|
"QCXG3Z3L": (*ApiRequestService).ProcessQCXG3Z3LRequest,
|
||||||
|
"QCXGP00W": (*ApiRequestService).ProcessQCXGP00WRequest,
|
||||||
|
"QCXG6B4E": (*ApiRequestService).ProcessQCXG6B4ERequest,
|
||||||
|
// 核验工具(verify feature.md)
|
||||||
|
"IVYZ9K7F": (*ApiRequestService).ProcessIVYZ9K7FRequest,
|
||||||
|
"IVYZA1B3": (*ApiRequestService).ProcessIVYZA1B3Request,
|
||||||
|
"IVYZ6M8P": (*ApiRequestService).ProcessIVYZ6M8PRequest,
|
||||||
|
"JRZQ8B3C": (*ApiRequestService).ProcessJRZQ8B3CRequest,
|
||||||
|
"YYSY3M8S": (*ApiRequestService).ProcessYYSY3M8SRequest,
|
||||||
|
"YYSYK9R4": (*ApiRequestService).ProcessYYSYK9R4Request,
|
||||||
|
"YYSYF2T7": (*ApiRequestService).ProcessYYSYF2T7Request,
|
||||||
|
"YYSYK8R3": (*ApiRequestService).ProcessYYSYK8R3Request,
|
||||||
|
"YYSYS9W1": (*ApiRequestService).ProcessYYSYS9W1Request,
|
||||||
|
"YYSYE7V5": (*ApiRequestService).ProcessYYSYE7V5Request,
|
||||||
|
"YYSYP0T4": (*ApiRequestService).ProcessYYSYP0T4Request,
|
||||||
|
"YYSY6F2B": (*ApiRequestService).ProcessYYSY6F2BRequest,
|
||||||
|
"YYSY9E4A": (*ApiRequestService).ProcessYYSY9E4ARequest,
|
||||||
|
"QYGL5F6A": (*ApiRequestService).ProcessQYGL5F6ARequest,
|
||||||
|
"JRZQACAB": (*ApiRequestService).ProcessJRZQACABRequest,
|
||||||
|
"JRZQ0B6Y": (*ApiRequestService).ProcessJRZQ0B6YRequest,
|
||||||
|
// 新增司法涉诉类接口
|
||||||
|
"QYGL66SL": (*ApiRequestService).ProcessQYGL66SLRequest,
|
||||||
|
"FLXG3A9B": (*ApiRequestService).ProcessFLXG3A9BRequest,
|
||||||
|
"QYGL2S0W": (*ApiRequestService).ProcessQYGL2S0WRequest,
|
||||||
|
"FLXGDEA9": (*ApiRequestService).ProcessFLXGDEA9Request,
|
||||||
|
"IVYZ4Y27": (*ApiRequestService).ProcessIVYZ4Y27Request,
|
||||||
|
"IVYZ0S0D": (*ApiRequestService).ProcessIVYZ0S0DRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreprocessRequestApi 调用指定的请求处理函数
|
// PreprocessRequestApi 调用指定的请求处理函数
|
||||||
@@ -1104,6 +1181,382 @@ func (a *ApiRequestService) ProcessQYGL6F2DRequest(params []byte) ([]byte, error
|
|||||||
return nil, fmt.Errorf("响应code错误%s", code.String())
|
return nil, fmt.Errorf("响应code错误%s", code.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessQCXGGB2QRequest 人车核验简版
|
||||||
|
func (a *ApiRequestService) ProcessQCXGGB2QRequest(params []byte) ([]byte, error) {
|
||||||
|
plateNo := gjson.GetBytes(params, "plate_no")
|
||||||
|
carplateType := gjson.GetBytes(params, "carplate_type")
|
||||||
|
name := gjson.GetBytes(params, "name")
|
||||||
|
|
||||||
|
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
|
||||||
|
return nil, errors.New("api请求, QCXGGB2Q, 获取相关参数失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXGGB2Q", map[string]interface{}{
|
||||||
|
"plate_no": plateNo.String(),
|
||||||
|
"carplate_type": carplateType.String(),
|
||||||
|
"name": name.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessQCXGYTS2Request 人车核验详版
|
||||||
|
func (a *ApiRequestService) ProcessQCXGYTS2Request(params []byte) ([]byte, error) {
|
||||||
|
plateNo := gjson.GetBytes(params, "plate_no")
|
||||||
|
carplateType := gjson.GetBytes(params, "carplate_type")
|
||||||
|
name := gjson.GetBytes(params, "name")
|
||||||
|
|
||||||
|
if !plateNo.Exists() || !carplateType.Exists() || !name.Exists() {
|
||||||
|
return nil, errors.New("api请求, QCXGYTS2, 获取相关参数失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXGYTS2", map[string]interface{}{
|
||||||
|
"plate_no": plateNo.String(),
|
||||||
|
"carplate_type": carplateType.String(),
|
||||||
|
"name": name.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessQCXG5F3ARequest 名下车辆(车牌)
|
||||||
|
func (a *ApiRequestService) ProcessQCXG5F3ARequest(params []byte) ([]byte, error) {
|
||||||
|
idCard := gjson.GetBytes(params, "id_card")
|
||||||
|
name := gjson.GetBytes(params, "name")
|
||||||
|
if !idCard.Exists() || !name.Exists() {
|
||||||
|
return nil, errors.New("api请求, QCXG5F3A, 获取相关参数失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG5F3A", map[string]interface{}{
|
||||||
|
"id_card": idCard.String(),
|
||||||
|
"name": name.String(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// processVehicleApiPassThrough 车辆类接口通用透传:将缓存 params 原样传给天远(传参后续按接口文档再细化)
|
||||||
|
func (a *ApiRequestService) processVehicleApiPassThrough(params []byte, apiID string) ([]byte, error) {
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal(params, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, %s, 解析参数失败: %w", apiID, err)
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api passthrough, api_id=%s, params_keys=%v", apiID, keysOfMap(m))
|
||||||
|
resp, err := a.tianyuanapi.CallInterface(apiID, m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆 API 按 md 从缓存 params 提取字段并调用天远
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG4D2ERequest(params []byte) ([]byte, error) {
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal(params, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, QCXG4D2E, 解析参数失败: %w", err)
|
||||||
|
}
|
||||||
|
body := map[string]interface{}{}
|
||||||
|
if v, ok := m["user_type"].(string); ok && v != "" {
|
||||||
|
body["user_type"] = v
|
||||||
|
} else {
|
||||||
|
body["user_type"] = "1"
|
||||||
|
}
|
||||||
|
if v, ok := m["id_card"].(string); ok {
|
||||||
|
body["id_card"] = v
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("api请求, QCXG4D2E, 缺少 id_card")
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG4D2E", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG5U0ZRequest(params []byte) ([]byte, error) {
|
||||||
|
vin := gjson.GetBytes(params, "vin_code")
|
||||||
|
if !vin.Exists() || vin.String() == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG5U0Z, 缺少 vin_code")
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG5U0Z", map[string]interface{}{"vin_code": vin.String()})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG1U4URequest(params []byte) ([]byte, error) {
|
||||||
|
body := buildVehicleBody(params, []string{"vin_code", "image_url"}, nil)
|
||||||
|
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||||
|
if body["vin_code"] == nil || body["image_url"] == nil || orderNo == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG1U4U, 缺少必填参数 vin_code/image_url/order_no")
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXG1U4U, order_no=%s, vin_code=%v, image_url=%v", orderNo, body["vin_code"], body["image_url"])
|
||||||
|
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG1U4U")
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG1U4U", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXGY7F2Request(params []byte) ([]byte, error) {
|
||||||
|
body := buildVehicleBody(params, []string{"vin_code", "vehicle_location", "first_registrationdate"}, nil)
|
||||||
|
if body["vin_code"] == nil || body["vehicle_location"] == nil || body["first_registrationdate"] == nil {
|
||||||
|
return nil, errors.New("api请求, QCXGY7F2, 缺少必填参数 vin_code/vehicle_location/first_registrationdate")
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXGY7F2, vin_code=%v, vehicle_location=%v, first_registrationdate=%v", body["vin_code"], body["vehicle_location"], body["first_registrationdate"])
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXGY7F2", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG1H7YRequest(params []byte) ([]byte, error) {
|
||||||
|
vin := gjson.GetBytes(params, "vin_code")
|
||||||
|
plate := gjson.GetBytes(params, "car_license")
|
||||||
|
if !vin.Exists() || vin.String() == "" || !plate.Exists() || plate.String() == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG1H7Y, 缺少 vin_code 或 car_license")
|
||||||
|
}
|
||||||
|
// 天远侧字段为 vin_code + plate_no,这里将前端 car_license 映射为 plate_no
|
||||||
|
body := map[string]interface{}{
|
||||||
|
"vin_code": vin.String(),
|
||||||
|
"plate_no": plate.String(),
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXG1H7Y, vin_code=%s, plate_no=%s", vin.String(), plate.String())
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG1H7Y", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG4I1ZRequest(params []byte) ([]byte, error) {
|
||||||
|
vin := gjson.GetBytes(params, "vin_code")
|
||||||
|
if !vin.Exists() || vin.String() == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG4I1Z, 缺少 vin_code")
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG4I1Z", map[string]interface{}{"vin_code": vin.String()})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG3Y6BRequest(params []byte) ([]byte, error) {
|
||||||
|
body := buildVehicleBody(params, []string{"vin_code"}, nil)
|
||||||
|
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||||
|
if body["vin_code"] == nil || orderNo == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG3Y6B, 缺少必填参数 vin_code/order_no")
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXG3Y6B, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
|
||||||
|
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Y6B")
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG3Y6B", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG3Z3LRequest(params []byte) ([]byte, error) {
|
||||||
|
body := buildVehicleBody(params, []string{"vin_code"}, nil)
|
||||||
|
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||||
|
if body["vin_code"] == nil || orderNo == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG3Z3L, 缺少必填参数 vin_code/order_no")
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXG3Z3L, order_no=%s, vin_code=%v", orderNo, body["vin_code"])
|
||||||
|
body["return_url"] = a.buildVehicleCallbackURL(orderNo, "QCXG3Z3L")
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG3Z3L", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXGP00WRequest(params []byte) ([]byte, error) {
|
||||||
|
vin := gjson.GetBytes(params, "vin_code")
|
||||||
|
orderNo := gjson.GetBytes(params, "order_no").String()
|
||||||
|
vlphoto := gjson.GetBytes(params, "vlphoto_data")
|
||||||
|
if !vin.Exists() || vin.String() == "" || orderNo == "" || !vlphoto.Exists() || vlphoto.String() == "" {
|
||||||
|
return nil, errors.New("api请求, QCXGP00W, 缺少必填参数 vin_code/order_no/vlphoto_data")
|
||||||
|
}
|
||||||
|
logx.Infof("vehicle api request QCXGP00W, order_no=%s, vin_code=%s, vlphoto_data_len=%d", orderNo, vin.String(), len(vlphoto.String()))
|
||||||
|
key, err := hex.DecodeString(a.config.Encrypt.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, QCXGP00W, 密钥解析失败: %w", err)
|
||||||
|
}
|
||||||
|
encData, err := crypto.AesEncrypt([]byte(vlphoto.String()), key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, QCXGP00W, 加密行驶证数据失败: %w", err)
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXGP00W", map[string]interface{}{
|
||||||
|
"vin_code": vin.String(),
|
||||||
|
"return_url": a.buildVehicleCallbackURL(orderNo, "QCXGP00W"),
|
||||||
|
"data": encData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessQCXG6B4ERequest(params []byte) ([]byte, error) {
|
||||||
|
vin := gjson.GetBytes(params, "vin_code")
|
||||||
|
if !vin.Exists() || vin.String() == "" {
|
||||||
|
return nil, errors.New("api请求, QCXG6B4E, 缺少 vin_code")
|
||||||
|
}
|
||||||
|
auth := gjson.GetBytes(params, "authorized").String()
|
||||||
|
if auth == "" {
|
||||||
|
auth = "1"
|
||||||
|
}
|
||||||
|
// 天远文档字段名为 VINCode、Authorized
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("QCXG6B4E", map[string]interface{}{
|
||||||
|
"vin_code": vin.String(),
|
||||||
|
"authorized": auth,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// processVerifyPassThrough 核验类接口:缓存 params 已含 mobile_no/id_card/name 等,原样传天远
|
||||||
|
func (a *ApiRequestService) processVerifyPassThrough(params []byte, apiID string) ([]byte, error) {
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal(params, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, %s, 解析参数失败: %w", apiID, err)
|
||||||
|
}
|
||||||
|
logx.Infof("verify api passthrough, api_id=%s, params_keys=%v", apiID, keysOfMap(m))
|
||||||
|
resp, err := a.tianyuanapi.CallInterface(apiID, m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApiRequestService) ProcessIVYZ9K7FRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "IVYZ9K7F")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessIVYZA1B3Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "IVYZA1B3")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessIVYZ6M8PRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "IVYZ6M8P")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessJRZQ8B3CRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "JRZQ8B3C")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSY3M8SRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSY3M8S")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYK9R4Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYK9R4")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYF2T7Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYF2T7")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYK8R3Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYK8R3")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYS9W1Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYS9W1")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYE7V5Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYE7V5")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSYP0T4Request(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSYP0T4")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSY6F2BRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSY6F2B")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessYYSY9E4ARequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "YYSY9E4A")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessQYGL5F6ARequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "QYGL5F6A")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessJRZQACABRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "JRZQACAB")
|
||||||
|
}
|
||||||
|
func (a *ApiRequestService) ProcessJRZQ0B6YRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "JRZQ0B6Y")
|
||||||
|
}
|
||||||
|
|
||||||
|
// QYGL66SL 企业司法涉诉(简版)
|
||||||
|
func (a *ApiRequestService) ProcessQYGL66SLRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "QYGL66SL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLXG3A9B 限高被执行人
|
||||||
|
func (a *ApiRequestService) ProcessFLXG3A9BRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "FLXG3A9B")
|
||||||
|
}
|
||||||
|
|
||||||
|
// QYGL2S0W 失信被执行人
|
||||||
|
func (a *ApiRequestService) ProcessQYGL2S0WRequest(params []byte) ([]byte, error) {
|
||||||
|
return a.processVerifyPassThrough(params, "QYGL2S0W")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLXGDEA9 本人不良
|
||||||
|
func (a *ApiRequestService) ProcessFLXGDEA9Request(params []byte) ([]byte, error) {
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal(params, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("api请求, FLXGDEA9, 解析参数失败: %w", err)
|
||||||
|
}
|
||||||
|
// 授权由后端默认传 1,前端与查询服务不再感知 authorized
|
||||||
|
if v, ok := m["authorized"]; !ok || v == "" {
|
||||||
|
m["authorized"] = "1"
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("FLXGDEA9", m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildVehicleBody 从 params 中取 required 与 optional 键,仅非空才写入 body
|
||||||
|
func buildVehicleBody(params []byte, required, optional []string) map[string]interface{} {
|
||||||
|
body := make(map[string]interface{})
|
||||||
|
for _, k := range required {
|
||||||
|
v := gjson.GetBytes(params, k)
|
||||||
|
if v.Exists() {
|
||||||
|
body[k] = v.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, k := range optional {
|
||||||
|
v := gjson.GetBytes(params, k)
|
||||||
|
if v.Exists() && v.String() != "" {
|
||||||
|
body[k] = v.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildVehicleCallbackURL 生成车辆类接口的异步回调地址
|
||||||
|
// 使用 PublicBaseURL 作为对外域名配置,路径固定为 /api/v1/tianyuan/vehicle/callback
|
||||||
|
// 并通过查询参数携带 order_no 与 api_id 以便后端识别具体查询与模块。
|
||||||
|
func (a *ApiRequestService) buildVehicleCallbackURL(orderNo, apiID string) string {
|
||||||
|
base := strings.TrimRight(a.config.PublicBaseURL, "/")
|
||||||
|
if base == "" {
|
||||||
|
// 兜底:如果未配置 URLDomain,则使用相对路径,交给网关/部署层补全域名
|
||||||
|
return fmt.Sprintf("/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", orderNo, apiID)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/api/v1/tianyuan/vehicle/callback?order_no=%s&api_id=%s", base, orderNo, apiID)
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessQCXG7A2BRequest 名下车辆
|
// ProcessQCXG7A2BRequest 名下车辆
|
||||||
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
|
func (a *ApiRequestService) ProcessQCXG7A2BRequest(params []byte) ([]byte, error) {
|
||||||
idCard := gjson.GetBytes(params, "id_card")
|
idCard := gjson.GetBytes(params, "id_card")
|
||||||
@@ -1478,3 +1931,54 @@ func (a *ApiRequestService) ProcessQCXG9P1CFRequest(params []byte) ([]byte, erro
|
|||||||
|
|
||||||
return convertTianyuanResponse(resp)
|
return convertTianyuanResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessIVYZ4Y27Request 学历信息查询(需生成专用授权书PDF并Base64编码传入)
|
||||||
|
func (a *ApiRequestService) ProcessIVYZ4Y27Request(params []byte) ([]byte, error) {
|
||||||
|
name := gjson.GetBytes(params, "name")
|
||||||
|
idCard := gjson.GetBytes(params, "id_card")
|
||||||
|
mobile := gjson.GetBytes(params, "mobile")
|
||||||
|
if !name.Exists() || !idCard.Exists() {
|
||||||
|
return nil, errors.New("api请求, IVYZ4Y27, 获取相关参数失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成专用授权书PDF并Base64编码
|
||||||
|
userInfo := map[string]interface{}{
|
||||||
|
"name": name.String(),
|
||||||
|
"id_card": idCard.String(),
|
||||||
|
"mobile": mobile.String(),
|
||||||
|
}
|
||||||
|
authFileBase64, err := a.authService.GenerateIVYZ4Y27AuthorizationBase64(userInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("生成IVYZ4Y27授权书失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("IVYZ4Y27", map[string]interface{}{
|
||||||
|
"name": name.String(),
|
||||||
|
"id_card": idCard.String(),
|
||||||
|
"auth_authorize_file_base64": authFileBase64,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessIVYZ0S0DRequest 劳动仲裁信息查询(个人版)
|
||||||
|
func (a *ApiRequestService) ProcessIVYZ0S0DRequest(params []byte) ([]byte, error) {
|
||||||
|
idCard := gjson.GetBytes(params, "id_card")
|
||||||
|
name := gjson.GetBytes(params, "name")
|
||||||
|
if !idCard.Exists() || !name.Exists() {
|
||||||
|
return nil, errors.New("api请求, IVYZ0S0D, 获取相关参数失败")
|
||||||
|
}
|
||||||
|
resp, err := a.tianyuanapi.CallInterface("IVYZ0S0D", map[string]interface{}{
|
||||||
|
"id_card": idCard.String(),
|
||||||
|
"name": name.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertTianyuanResponse(resp)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/app/main/api/internal/config"
|
"tyc-server/app/main/api/internal/config"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
@@ -329,3 +331,116 @@ func getUserInfoString(userInfo map[string]interface{}, key string) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateIVYZ4Y27AuthorizationBase64 生成IVYZ4Y27专用授权书PDF并返回Base64编码字符串
|
||||||
|
func (s *AuthorizationService) GenerateIVYZ4Y27AuthorizationBase64(userInfo map[string]interface{}) (string, error) {
|
||||||
|
name := getUserInfoString(userInfo, "name")
|
||||||
|
idCard := getUserInfoString(userInfo, "id_card")
|
||||||
|
if name == "" || idCard == "" {
|
||||||
|
return "", fmt.Errorf("缺少必要的用户信息(name或id_card)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建模板变量
|
||||||
|
data := map[string]string{
|
||||||
|
"CompanyName": "广西福铭网络科技有限公司",
|
||||||
|
"Name": name,
|
||||||
|
"IdCard": idCard,
|
||||||
|
"Mobile": getUserInfoString(userInfo, "mobile"),
|
||||||
|
"Date": time.Now().Format("2006年1月2日"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从模板生成PDF
|
||||||
|
pdfBytes, err := s.generatePDFFromTemplate("static/authorization_ivyz4y27.tmpl", data)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "生成IVYZ4Y27授权书PDF失败")
|
||||||
|
}
|
||||||
|
// Base64编码
|
||||||
|
return base64.StdEncoding.EncodeToString(pdfBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatePDFFromTemplate 从模板生成PDF
|
||||||
|
func (s *AuthorizationService) generatePDFFromTemplate(templatePath string, data interface{}) ([]byte, error) {
|
||||||
|
// 1. 读取模板文件
|
||||||
|
tmplContent, err := os.ReadFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
// 尝试从项目根目录读取
|
||||||
|
absPath, _ := filepath.Abs(templatePath)
|
||||||
|
if _, err := os.Stat(absPath); err == nil {
|
||||||
|
tmplContent, err = os.ReadFile(absPath)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "读取模板文件失败: %s", templatePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 解析并执行模板
|
||||||
|
tmpl, err := template.New("auth").Parse(string(tmplContent))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "解析模板失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&buf, data); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "执行模板失败")
|
||||||
|
}
|
||||||
|
content := buf.String()
|
||||||
|
|
||||||
|
// 3. 创建PDF
|
||||||
|
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||||
|
pdf.AddPage()
|
||||||
|
|
||||||
|
// 4. 加载中文字体
|
||||||
|
fontPaths := []string{
|
||||||
|
"static/SIMHEI.TTF",
|
||||||
|
"/app/static/SIMHEI.TTF",
|
||||||
|
"app/main/api/static/SIMHEI.TTF",
|
||||||
|
"../static/SIMHEI.TTF",
|
||||||
|
}
|
||||||
|
|
||||||
|
fontAdded := false
|
||||||
|
for _, fontPath := range fontPaths {
|
||||||
|
if _, err := os.Stat(fontPath); err == nil {
|
||||||
|
pdf.AddUTF8Font("ChineseFont", "", fontPath)
|
||||||
|
fontAdded = true
|
||||||
|
logx.Infof("generatePDFFromTemplate 成功加载字体: %s", fontPath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fontAdded {
|
||||||
|
pdf.SetFont("ChineseFont", "", 12)
|
||||||
|
} else {
|
||||||
|
pdf.SetFont("Arial", "", 12)
|
||||||
|
logx.Errorf("generatePDFFromTemplate 未找到中文字体文件,使用默认Arial字体")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 写入内容
|
||||||
|
// 简单处理:按行分割并使用 MultiCell
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
pdf.Ln(5)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// MultiCell 支持自动换行
|
||||||
|
pdf.MultiCell(0, 6, line, "", "L", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 添加水印 (简单实现:在页面中间添加浅灰色文字)
|
||||||
|
if fontAdded {
|
||||||
|
pdf.SetFont("ChineseFont", "", 40)
|
||||||
|
pdf.SetTextColor(220, 220, 220) // 非常浅的灰色
|
||||||
|
// 将坐标移动到页面中间附近,并旋转或倾斜(gofpdf 旋转比较复杂,这里简单放几个位置)
|
||||||
|
pdf.Text(40, 100, "仅供背景调查使用")
|
||||||
|
pdf.Text(40, 180, "仅供背景调查使用")
|
||||||
|
pdf.Text(40, 260, "仅供背景调查使用")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 输出PDF
|
||||||
|
var pdfBuf bytes.Buffer
|
||||||
|
if err := pdf.Output(&pdfBuf); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "输出PDF字节数组失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdfBuf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|||||||
150
app/main/api/internal/service/tianxingjuhe_sdk/client.go
Normal file
150
app/main/api/internal/service/tianxingjuhe_sdk/client.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package tianxingjuhe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client 天行聚合API客户端
|
||||||
|
type Client struct {
|
||||||
|
baseURL string
|
||||||
|
key string
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config 客户端配置
|
||||||
|
type Config struct {
|
||||||
|
BaseURL string // API基础URL
|
||||||
|
Key string // API密钥
|
||||||
|
Timeout int // 超时时间(秒)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response 通用API响应结构
|
||||||
|
type Response struct {
|
||||||
|
Code int `json:"code"` // 状态码,200表示成功
|
||||||
|
Msg string `json:"msg"` // 返回说明
|
||||||
|
Result interface{} `json:"result"` // 返回结果集,具体内容根据接口而定
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient 创建新的客户端实例
|
||||||
|
func NewClient(config Config) (*Client, error) {
|
||||||
|
if config.BaseURL == "" {
|
||||||
|
return nil, fmt.Errorf("baseURL不能为空")
|
||||||
|
}
|
||||||
|
if config.Key == "" {
|
||||||
|
return nil, fmt.Errorf("key不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
baseURL: config.BaseURL,
|
||||||
|
key: config.Key,
|
||||||
|
client: &http.Client{
|
||||||
|
Timeout: time.Duration(config.Timeout) * time.Second,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 发送GET请求
|
||||||
|
func (c *Client) Get(endpoint string, params map[string]interface{}) (*Response, error) {
|
||||||
|
// 构建完整URL
|
||||||
|
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
|
||||||
|
|
||||||
|
// 添加请求参数
|
||||||
|
queryParams := url.Values{}
|
||||||
|
for key, value := range params {
|
||||||
|
queryParams.Set(key, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加key参数
|
||||||
|
queryParams.Set("key", c.key)
|
||||||
|
|
||||||
|
// 拼接查询参数
|
||||||
|
if len(queryParams) > 0 {
|
||||||
|
fullURL = fmt.Sprintf("%s?%s", fullURL, queryParams.Encode())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建HTTP请求
|
||||||
|
req, err := http.NewRequest("GET", fullURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 读取响应
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
var apiResp Response
|
||||||
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post 发送POST请求
|
||||||
|
func (c *Client) Post(endpoint string, params map[string]interface{}) (*Response, error) {
|
||||||
|
// 构建完整URL
|
||||||
|
fullURL := fmt.Sprintf("%s/%s", c.baseURL, endpoint)
|
||||||
|
|
||||||
|
// 添加key参数
|
||||||
|
if params == nil {
|
||||||
|
params = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
params["key"] = c.key
|
||||||
|
|
||||||
|
// 序列化请求体
|
||||||
|
requestBody, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("序列化请求体失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建HTTP请求
|
||||||
|
req, err := http.NewRequest("POST", fullURL, bytes.NewBuffer(requestBody))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", "Tianxingjuhe-Go-SDK/1.0.0")
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 读取响应
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
var apiResp Response
|
||||||
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiResp, nil
|
||||||
|
}
|
||||||
8276
app/main/api/internal/service/toolboxService.go
Normal file
8276
app/main/api/internal/service/toolboxService.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"tyc-server/app/main/api/internal/config"
|
"tyc-server/app/main/api/internal/config"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
@@ -24,6 +26,14 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// wxpayAPIHTTPStatus 安全读取微信支付 API 的 HTTP 状态码;本地签名/随机串失败时 result 可能为 nil。
|
||||||
|
func wxpayAPIHTTPStatus(result *core.APIResult) int {
|
||||||
|
if result == nil || result.Response == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result.Response.StatusCode
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TradeStateSuccess = "SUCCESS" // 支付成功
|
TradeStateSuccess = "SUCCESS" // 支付成功
|
||||||
TradeStateRefund = "REFUND" // 转入退款
|
TradeStateRefund = "REFUND" // 转入退款
|
||||||
@@ -172,14 +182,54 @@ func (w *WechatPayService) CreateWechatAppOrder(ctx context.Context, amount floa
|
|||||||
|
|
||||||
// 发起预支付请求
|
// 发起预支付请求
|
||||||
resp, result, err := svc.Prepay(ctx, payRequest)
|
resp, result, err := svc.Prepay(ctx, payRequest)
|
||||||
|
logx.Infof("微信app支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回预支付交易会话标识
|
// 返回预支付交易会话标识
|
||||||
return *resp.PrepayId, nil
|
return *resp.PrepayId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jsapiRequestPaymentToMap 将 JSAPI 调起参数转为 map[string]string,便于 JSON 序列化给前端 WeixinJSBridge
|
||||||
|
func jsapiRequestPaymentToMap(resp *jsapi.PrepayWithRequestPaymentResponse) (map[string]string, error) {
|
||||||
|
if resp == nil {
|
||||||
|
return nil, fmt.Errorf("微信下单返回为空")
|
||||||
|
}
|
||||||
|
m := map[string]string{}
|
||||||
|
if resp.Appid != nil {
|
||||||
|
m["appId"] = *resp.Appid
|
||||||
|
}
|
||||||
|
if resp.TimeStamp != nil {
|
||||||
|
m["timeStamp"] = *resp.TimeStamp
|
||||||
|
}
|
||||||
|
if resp.NonceStr != nil {
|
||||||
|
m["nonceStr"] = *resp.NonceStr
|
||||||
|
}
|
||||||
|
if resp.Package != nil {
|
||||||
|
m["package"] = *resp.Package
|
||||||
|
}
|
||||||
|
if resp.SignType != nil && *resp.SignType != "" {
|
||||||
|
m["signType"] = *resp.SignType
|
||||||
|
} else {
|
||||||
|
m["signType"] = "RSA"
|
||||||
|
}
|
||||||
|
if resp.PaySign != nil {
|
||||||
|
m["paySign"] = *resp.PaySign
|
||||||
|
}
|
||||||
|
var missing []string
|
||||||
|
for _, key := range []string{"appId", "timeStamp", "nonceStr", "package", "paySign"} {
|
||||||
|
if m[key] == "" {
|
||||||
|
missing = append(missing, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(missing) > 0 {
|
||||||
|
logx.Errorf("[WechatPay] JSAPI 调起参数缺项: missing=%v resp=%s", missing, resp.String())
|
||||||
|
return nil, fmt.Errorf("微信 JSAPI 调起参数不完整: 缺少或为空 %v", missing)
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateWechatMiniProgramOrder 创建微信小程序支付订单
|
// CreateWechatMiniProgramOrder 创建微信小程序支付订单
|
||||||
func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amount float64, description string, outTradeNo string, openid string) (interface{}, error) {
|
func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amount float64, description string, outTradeNo string, openid string) (interface{}, error) {
|
||||||
totalAmount := lzUtils.ToWechatAmount(amount)
|
totalAmount := lzUtils.ToWechatAmount(amount)
|
||||||
@@ -203,17 +253,18 @@ func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amo
|
|||||||
|
|
||||||
// 发起预支付请求
|
// 发起预支付请求
|
||||||
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
|
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
|
||||||
|
logx.Infof("微信小程序支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
|
||||||
}
|
}
|
||||||
// 返回预支付交易会话标识
|
return jsapiRequestPaymentToMap(resp)
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateWechatH5Order 创建微信H5支付订单
|
// CreateWechatH5Order 创建微信H5支付订单
|
||||||
func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float64, description string, outTradeNo string, openid string) (interface{}, error) {
|
func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float64, description string, outTradeNo string, openid string) (interface{}, error) {
|
||||||
totalAmount := lzUtils.ToWechatAmount(amount)
|
totalAmount := lzUtils.ToWechatAmount(amount)
|
||||||
|
|
||||||
|
logx.Infof("微信h5支付订单:amount: %+v, description: %+v, outTradeNo: %+v, openid: %+v", amount, description, outTradeNo, openid)
|
||||||
// 构建支付请求参数
|
// 构建支付请求参数
|
||||||
payRequest := jsapi.PrepayRequest{
|
payRequest := jsapi.PrepayRequest{
|
||||||
Appid: core.String(w.config.WechatH5.AppID),
|
Appid: core.String(w.config.WechatH5.AppID),
|
||||||
@@ -231,20 +282,31 @@ func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float
|
|||||||
// 初始化 AppApiService
|
// 初始化 AppApiService
|
||||||
svc := jsapi.JsapiApiService{Client: w.wechatClient}
|
svc := jsapi.JsapiApiService{Client: w.wechatClient}
|
||||||
|
|
||||||
|
logx.Infof("微信h5支付订单:payRequest: %+v", payRequest)
|
||||||
// 发起预支付请求
|
// 发起预支付请求
|
||||||
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
|
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
|
||||||
logx.Infof("微信h5支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
logx.Infof("微信h5支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
logx.Infof("微信h5支付订单:resp: %+v, result: %+v, err: %+v", resp, result, err)
|
||||||
|
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
|
||||||
}
|
}
|
||||||
// 返回预支付交易会话标识
|
return jsapiRequestPaymentToMap(resp)
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
||||||
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string) (interface{}, error) {
|
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string) (interface{}, error) {
|
||||||
// 根据 ctx 中的 platform 判断平台
|
platformVal := ctx.Value("platform")
|
||||||
platform := ctx.Value("platform").(string)
|
platform, ok := platformVal.(string)
|
||||||
|
rawPlatform := platform
|
||||||
|
platform = strings.TrimSpace(platform)
|
||||||
|
logx.WithContext(ctx).Infof(
|
||||||
|
"[WechatPay] CreateWechatOrder platform ctx: value_type=%T assert_ok=%v raw=%q trimmed=%q ref_wxh5=%q ref_wxmini=%q ref_app=%q out_trade_no=%s",
|
||||||
|
platformVal, ok, rawPlatform, platform, model.PlatformWxH5, model.PlatformWxMini, model.PlatformApp, outTradeNo,
|
||||||
|
)
|
||||||
|
if !ok || platform == "" {
|
||||||
|
logx.WithContext(ctx).Errorf("[WechatPay] CreateWechatOrder 缺少 X-Platform")
|
||||||
|
return "", fmt.Errorf("缺少 X-Platform 请求头(微信内请传 wxh5)")
|
||||||
|
}
|
||||||
|
|
||||||
var prepayData interface{}
|
var prepayData interface{}
|
||||||
var err error
|
var err error
|
||||||
@@ -264,15 +326,33 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
case model.PlatformWxH5:
|
case model.PlatformWxH5:
|
||||||
|
logx.WithContext(ctx).Infof(
|
||||||
|
"[WechatPay] CreateWechatOrder branch=wxh5 out_trade_no=%s amount=%.2f desc_len=%d",
|
||||||
|
outTradeNo, amount, len(description),
|
||||||
|
)
|
||||||
userID, getUidErr := ctxdata.GetUidFromCtx(ctx)
|
userID, getUidErr := ctxdata.GetUidFromCtx(ctx)
|
||||||
if getUidErr != nil {
|
if getUidErr != nil {
|
||||||
return "", getUidErr
|
return "", getUidErr
|
||||||
}
|
}
|
||||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
// 微信内置浏览器 JSAPI 必须使用与商户公众号一致的 openid(snsapi_base / snsapi_userinfo 授权后写入 wxh5_openid)。
|
||||||
if findAuthModelErr != nil {
|
// 不可使用小程序 openid 兜底:AppID 与 openid 主体不一致会导致下单失败或调起异常。
|
||||||
return "", findAuthModelErr
|
h5Auth, h5Err := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||||
|
if h5Err != nil {
|
||||||
|
if errors.Is(h5Err, model.ErrNotFound) {
|
||||||
|
logx.WithContext(ctx).Infof(
|
||||||
|
"[WechatPay] wxh5 缺少 user_auth(wxh5_openid) user_id=%d out_trade_no=%s,需先走公众号网页授权(建议 scope=snsapi_base)",
|
||||||
|
userID, outTradeNo,
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("微信内支付需先完成公众号网页授权以获取 openid(建议使用 snsapi_base 静默授权)")
|
||||||
}
|
}
|
||||||
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
return "", h5Err
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(h5Auth.AuthKey) == "" {
|
||||||
|
logx.WithContext(ctx).Errorf("[WechatPay] wxh5_openid 记录存在但 auth_key 为空 user_id=%d", userID)
|
||||||
|
return "", fmt.Errorf("微信内支付 openid 未就绪,请重新完成公众号网页授权")
|
||||||
|
}
|
||||||
|
logx.Infof("微信h5支付订单:userAuthModel(wxh5): %+v", h5Auth)
|
||||||
|
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, strings.TrimSpace(h5Auth.AuthKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -288,6 +368,21 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
|||||||
return "", fmt.Errorf("支付订单创建失败: %v", err)
|
return "", fmt.Errorf("支付订单创建失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prepayData == nil {
|
||||||
|
logx.WithContext(ctx).Errorf("[WechatPay] CreateWechatOrder 返回 prepayData 为 nil platform=%q", platform)
|
||||||
|
return nil, fmt.Errorf("微信支付返回数据为空 platform=%s", platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, isMap := prepayData.(map[string]string); isMap {
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
logx.WithContext(ctx).Infof("[WechatPay] CreateWechatOrder return prepay: platform=%q type=map[string]string len=%d keys=%v", platform, len(m), keys)
|
||||||
|
} else {
|
||||||
|
logx.WithContext(ctx).Infof("[WechatPay] CreateWechatOrder return prepay: platform=%q type=%T", platform, prepayData)
|
||||||
|
}
|
||||||
|
|
||||||
// 返回预支付ID
|
// 返回预支付ID
|
||||||
return prepayData, nil
|
return prepayData, nil
|
||||||
}
|
}
|
||||||
@@ -323,7 +418,7 @@ func (w *WechatPayService) QueryOrderStatus(ctx context.Context, transactionID s
|
|||||||
Mchid: core.String(w.config.Wxpay.MchID),
|
Mchid: core.String(w.config.Wxpay.MchID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, result.Response.StatusCode)
|
return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"tyc-server/app/main/api/internal/config"
|
"tyc-server/app/main/api/internal/config"
|
||||||
"tyc-server/app/main/api/internal/middleware"
|
"tyc-server/app/main/api/internal/middleware"
|
||||||
"tyc-server/app/main/api/internal/service"
|
"tyc-server/app/main/api/internal/service"
|
||||||
|
tianxingjuhe "tyc-server/app/main/api/internal/service/tianxingjuhe_sdk"
|
||||||
tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
tianyuanapi "tyc-server/app/main/api/internal/service/tianyuanapi_sdk"
|
||||||
"tyc-server/app/main/model"
|
"tyc-server/app/main/model"
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ type ServiceContext struct {
|
|||||||
// 中间件
|
// 中间件
|
||||||
AuthInterceptor rest.Middleware
|
AuthInterceptor rest.Middleware
|
||||||
UserAuthInterceptor rest.Middleware
|
UserAuthInterceptor rest.Middleware
|
||||||
|
UserDisableInterceptor rest.Middleware
|
||||||
AdminAuthInterceptor rest.Middleware
|
AdminAuthInterceptor rest.Middleware
|
||||||
|
|
||||||
// 用户相关模型
|
// 用户相关模型
|
||||||
@@ -100,6 +102,8 @@ type ServiceContext struct {
|
|||||||
AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService
|
AdminPromotionLinkStatsService *service.AdminPromotionLinkStatsService
|
||||||
ImageService *service.ImageService
|
ImageService *service.ImageService
|
||||||
AuthorizationService *service.AuthorizationService
|
AuthorizationService *service.AuthorizationService
|
||||||
|
ToolboxService *service.ToolboxService
|
||||||
|
TianxingjuheService *tianxingjuhe.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceContext 创建服务上下文
|
// NewServiceContext 创建服务上下文
|
||||||
@@ -187,13 +191,22 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
logx.Errorf("初始化天远API失败: %+v", err)
|
logx.Errorf("初始化天远API失败: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化天行聚合API客户端
|
||||||
|
tianxingjuhe, err := tianxingjuhe.NewClient(tianxingjuhe.Config{
|
||||||
|
BaseURL: c.Tianxingjuhe.URL,
|
||||||
|
Key: c.Tianxingjuhe.Key,
|
||||||
|
Timeout: c.Tianxingjuhe.Timeout,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("初始化天行聚合API失败: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// ============================== 业务服务初始化 ==============================
|
// ============================== 业务服务初始化 ==============================
|
||||||
alipayService := service.NewAliPayService(c)
|
alipayService := service.NewAliPayService(c)
|
||||||
// wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey)
|
wechatPayService := service.NewWechatPayService(c, userAuthModel, service.InitTypeWxPayPubKey)
|
||||||
// 为暂时关闭微信支付,将 WechatPayService 置为 nil,避免在项目启动时初始化微信支付相关配置
|
|
||||||
var wechatPayService *service.WechatPayService
|
|
||||||
applePayService := service.NewApplePayService(c)
|
applePayService := service.NewApplePayService(c)
|
||||||
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, tianyuanapi)
|
authorizationService := service.NewAuthorizationService(c, authorizationDocumentModel)
|
||||||
|
apiRequestService := service.NewApiRequestService(c, featureModel, productFeatureModel, tianyuanapi, authorizationService)
|
||||||
verificationService := service.NewVerificationService(c, tianyuanapi, apiRequestService)
|
verificationService := service.NewVerificationService(c, tianyuanapi, apiRequestService)
|
||||||
asynqService := service.NewAsynqService(c)
|
asynqService := service.NewAsynqService(c)
|
||||||
agentService := service.NewAgentService(c, orderModel, agentModel, agentAuditModel, agentClosureModel,
|
agentService := service.NewAgentService(c, orderModel, agentModel, agentAuditModel, agentClosureModel,
|
||||||
@@ -206,7 +219,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel,
|
adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel,
|
||||||
adminPromotionLinkStatsTotalModel, adminPromotionLinkStatsHistoryModel)
|
adminPromotionLinkStatsTotalModel, adminPromotionLinkStatsHistoryModel)
|
||||||
imageService := service.NewImageService()
|
imageService := service.NewImageService()
|
||||||
authorizationService := service.NewAuthorizationService(c, authorizationDocumentModel)
|
toolboxService := service.NewToolboxService(tianxingjuhe)
|
||||||
|
tianxingjuheService := tianxingjuhe
|
||||||
|
|
||||||
// ============================== 异步任务服务 ==============================
|
// ============================== 异步任务服务 ==============================
|
||||||
asynqServer := asynq.NewServer(
|
asynqServer := asynq.NewServer(
|
||||||
@@ -226,6 +240,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
Redis: redisClient,
|
Redis: redisClient,
|
||||||
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
|
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
|
||||||
UserAuthInterceptor: middleware.NewUserAuthInterceptorMiddleware().Handle,
|
UserAuthInterceptor: middleware.NewUserAuthInterceptorMiddleware().Handle,
|
||||||
|
UserDisableInterceptor: middleware.NewUserDisableInterceptorMiddleware(userModel).Handle,
|
||||||
AdminAuthInterceptor: middleware.NewAdminAuthInterceptorMiddleware(c,
|
AdminAuthInterceptor: middleware.NewAdminAuthInterceptorMiddleware(c,
|
||||||
adminUserModel, adminUserRoleModel, adminRoleModel, adminApiModel, adminRoleApiModel).Handle,
|
adminUserModel, adminUserRoleModel, adminRoleModel, adminApiModel, adminRoleApiModel).Handle,
|
||||||
|
|
||||||
@@ -304,6 +319,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
AdminPromotionLinkStatsService: adminPromotionLinkStatsService,
|
AdminPromotionLinkStatsService: adminPromotionLinkStatsService,
|
||||||
ImageService: imageService,
|
ImageService: imageService,
|
||||||
AuthorizationService: authorizationService,
|
AuthorizationService: authorizationService,
|
||||||
|
ToolboxService: toolboxService,
|
||||||
|
TianxingjuheService: tianxingjuheService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,12 +45,10 @@ type PreLoanBackgroundCheckReq struct {
|
|||||||
Code string `json:"code" validate:"required"`
|
Code string `json:"code" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackgroundCheck 查询请求结构
|
|
||||||
type BackgroundCheckReq struct {
|
type BackgroundCheckReq struct {
|
||||||
Name string `json:"name" validate:"required,name"`
|
Name string `json:"name" validate:"required,name"`
|
||||||
IDCard string `json:"id_card" validate:"required,idCard"`
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
Code string `json:"code" validate:"required"`
|
|
||||||
}
|
}
|
||||||
type PersonalDataReq struct {
|
type PersonalDataReq struct {
|
||||||
Name string `json:"name" validate:"required,name"`
|
Name string `json:"name" validate:"required,name"`
|
||||||
@@ -64,6 +62,38 @@ type EntLawsuitReq struct {
|
|||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
Code string `json:"code" validate:"required"`
|
Code string `json:"code" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnterpriseLawsuitSimple 企业司法涉诉简版(QYGL66SL):仅企业名称,授权信息由后端补齐
|
||||||
|
type EnterpriseLawsuitSimpleReq struct {
|
||||||
|
EntName string `json:"ent_name" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LimitHighExecuted 限高被执行人(FLXG3A9B)
|
||||||
|
type LimitHighExecutedReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Authorized string `json:"authorized"` // 可选,后端默认 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// DishonestExecuted 失信被执行人(QYGL2S0W)
|
||||||
|
type DishonestExecutedReq struct {
|
||||||
|
Type string `json:"type"` // 可选,后端默认 per
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PersonalBadRecord 本人不良(FLXGDEA9):姓名 + 身份证(授权由 ApiRequest 默认传 1)
|
||||||
|
type PersonalBadRecordReq struct {
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TocPersonEnterprisePro 人企关系加强版预查询:仅身份证号
|
||||||
|
type TocPersonEnterpriseProReq struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
}
|
||||||
|
|
||||||
type TocPhoneThreeElements struct {
|
type TocPhoneThreeElements struct {
|
||||||
Name string `json:"name" validate:"required,name"`
|
Name string `json:"name" validate:"required,name"`
|
||||||
IDCard string `json:"id_card" validate:"required,idCard"`
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
@@ -83,6 +113,16 @@ type TocDualMarriage struct {
|
|||||||
NameWoman string `json:"name_woman" validate:"required,name"`
|
NameWoman string `json:"name_woman" validate:"required,name"`
|
||||||
IDCardWoman string `json:"id_card_woman" validate:"required,idCard"`
|
IDCardWoman string `json:"id_card_woman" validate:"required,idCard"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TocDualMarriageReq 双人婚姻状态查询(含手机号与验证码)
|
||||||
|
type TocDualMarriageReq struct {
|
||||||
|
NameMan string `json:"name_man" validate:"required,name"`
|
||||||
|
IDCardMan string `json:"id_card_man" validate:"required,idCard"`
|
||||||
|
NameWoman string `json:"name_woman" validate:"required,name"`
|
||||||
|
IDCardWoman string `json:"id_card_woman" validate:"required,idCard"`
|
||||||
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Code string `json:"code" validate:"required"`
|
||||||
|
}
|
||||||
type TocPersonVehicleVerification struct {
|
type TocPersonVehicleVerification struct {
|
||||||
Name string `json:"name" validate:"required,name"`
|
Name string `json:"name" validate:"required,name"`
|
||||||
CarType string `json:"car_type" validate:"required"`
|
CarType string `json:"car_type" validate:"required"`
|
||||||
@@ -112,8 +152,170 @@ type AgentQueryData struct {
|
|||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 名下车辆(数量) QCXG4D2E:仅 user_type(1-ETC开户人 2-车辆所有人 3-ETC经办人)+id_card,无验证码
|
||||||
|
type TocVehiclesUnderNameCountReq struct {
|
||||||
|
UserType string `json:"user_type"` // 必填,默认 1
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆静态信息/过户详版等 仅 vin_code(车辆类不要求手机号与验证码)
|
||||||
|
type TocVehicleVinCodeReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆里程记录(混合) QCXG1U4U
|
||||||
|
type TocVehicleMileageMixedReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
PlateNo string `json:"plate_no"`
|
||||||
|
// 回调地址由后端在调用第三方接口时自动生成,不再由前端透传
|
||||||
|
ReturnURL string `json:"return_url"`
|
||||||
|
ImageURL string `json:"image_url" validate:"required"`
|
||||||
|
RegURL string `json:"reg_url"`
|
||||||
|
EngineNumber string `json:"engine_number"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 二手车VIN估值 QCXGY7F2
|
||||||
|
type TocVehicleVinValuationReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
VehicleName string `json:"vehicle_name"`
|
||||||
|
VehicleLocation string `json:"vehicle_location" validate:"required"`
|
||||||
|
FirstRegistrationDate string `json:"first_registrationdate" validate:"required"` // yyyy-MM
|
||||||
|
Color string `json:"color"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆过户简版 QCXG1H7Y
|
||||||
|
type TocVehicleTransferSimpleReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
PlateNo string `json:"plate_no"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆维保简版 QCXG3Y6B
|
||||||
|
type TocVehicleMaintenanceSimpleReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
PlateNo string `json:"plate_no"`
|
||||||
|
// 回调地址由后端在调用第三方接口时自动生成,不再由前端透传
|
||||||
|
ReturnURL string `json:"return_url"`
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
|
RegURL string `json:"reg_url"`
|
||||||
|
EngineNumber string `json:"engine_number"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆维保详细版 QCXG3Z3L
|
||||||
|
type TocVehicleMaintenanceDetailReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
PlateNo string `json:"plate_no"`
|
||||||
|
// 回调地址由后端在调用第三方接口时自动生成,不再由前端透传
|
||||||
|
ReturnURL string `json:"return_url"`
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
|
EngineNumber string `json:"engine_number"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆出险详版 QCXGP00W,vlphoto_data 加密后以 data 字段提交
|
||||||
|
type TocVehicleClaimDetailReq struct {
|
||||||
|
VinCode string `json:"vin_code" validate:"required"`
|
||||||
|
PlateNo string `json:"plate_no"`
|
||||||
|
// 回调地址由后端在调用第三方接口时自动生成,不再由前端透传
|
||||||
|
ReturnURL string `json:"return_url"`
|
||||||
|
VlphotoData string `json:"vlphoto_data" validate:"required"` // 行驶证图片 base64,加密后传 API 的 data
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车辆出险记录核验 QCXG6B4E,VINCode;Authorized 由后端默认传 1
|
||||||
|
type TocVehicleClaimVerifyReq struct {
|
||||||
|
VINCode string `json:"vin_code" validate:"required"`
|
||||||
|
Authorized string `json:"authorized"` // 可选,后端默认 1
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
type AgentIdentifier struct {
|
type AgentIdentifier struct {
|
||||||
Product string `json:"product"`
|
Product string `json:"product"`
|
||||||
AgentID int64 `json:"agent_id"`
|
AgentID int64 `json:"agent_id"`
|
||||||
Price string `json:"price"`
|
Price string `json:"price"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------- 核验工具(verify feature.md)---------------
|
||||||
|
// 公安二要素 IVYZ9K7F:请求用 mobile,缓存/API 用 mobile_no
|
||||||
|
type TocVerifyAuthTwoReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"` // 前端传 mobile
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公安三要素 IVYZA1B3:photo_data, id_card, name
|
||||||
|
type TocVerifyAuthThreeReq struct {
|
||||||
|
PhotoData string `json:"photo_data" validate:"required"` // 人像 base64
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 职业资格证书 IVYZ6M8P:id_card, name
|
||||||
|
type TocVerifyCertReq struct {
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 个人消费能力 JRZQ8B3C
|
||||||
|
type TocVerifyConsumptionReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运营商二要素 YYSY3M8S
|
||||||
|
type TocVerifyYysTwoReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全网手机三要素 YYSYK9R4
|
||||||
|
type TocVerifyYysThreeReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅手机号:YYSYF2T7/YYSYK8R3/YYSYS9W1/YYSYE7V5/YYSYP0T4/YYSY9E4A
|
||||||
|
type TocVerifyMobileOnlyReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手机消费区间 YYSY6F2B
|
||||||
|
type TocVerifyYysConsumptionReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
Authorized string `json:"authorized" validate:"required"` // 0/1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 名下企业关联 QYGL5F6A:id_card 选填
|
||||||
|
type TocVerifyEntRelationReq struct {
|
||||||
|
IDCard string `json:"id_card"` // 可选
|
||||||
|
}
|
||||||
|
|
||||||
|
// 银行卡四要素 JRZQACAB
|
||||||
|
type TocVerifyBankFourReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
BankCard string `json:"bank_card" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 银行卡黑名单 JRZQ0B6Y
|
||||||
|
type TocVerifyBankBlackReq struct {
|
||||||
|
MobileNo string `json:"mobile" validate:"required,mobile"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||||
|
Name string `json:"name" validate:"required,name"`
|
||||||
|
BankCard string `json:"bank_card" validate:"required"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ type AdminGetOrderListReq struct {
|
|||||||
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
|
RefundTimeStart string `form:"refund_time_start,optional"` // 退款时间开始
|
||||||
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
|
RefundTimeEnd string `form:"refund_time_end,optional"` // 退款时间结束
|
||||||
SalesCost float64 `form:"sales_cost,optional"` // 成本价
|
SalesCost float64 `form:"sales_cost,optional"` // 成本价
|
||||||
QueriedName string `form:"queried_name,optional"` // 被查询人姓名(明文)
|
QueriedName string `form:"queried_name,optional"` // 被查询人姓名(明文,后端加密后匹配 query_user_record)
|
||||||
QueriedIdCard string `form:"queried_id_card,optional"` // 被查询人身份证(明文)
|
QueriedIdCard string `form:"queried_id_card,optional"` // 被查询人身份证(明文)
|
||||||
QueriedMobile string `form:"queried_mobile,optional"` // 被查询人手机号(明文)
|
QueriedMobile string `form:"queried_mobile,optional"` // 被查询人手机号(明文)
|
||||||
}
|
}
|
||||||
@@ -568,6 +568,7 @@ type AdminGetPlatformUserDetailResp struct {
|
|||||||
Nickname string `json:"nickname"` // 昵称
|
Nickname string `json:"nickname"` // 昵称
|
||||||
Info string `json:"info"` // 备注信息
|
Info string `json:"info"` // 备注信息
|
||||||
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable int64 `json:"disable"` // 是否封禁 0-可用 1-禁用
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
UpdateTime string `json:"update_time"` // 更新时间
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
}
|
}
|
||||||
@@ -674,7 +675,12 @@ type AdminGetQueryDetailByOrderIdResp struct {
|
|||||||
Id int64 `json:"id"` // 主键ID
|
Id int64 `json:"id"` // 主键ID
|
||||||
OrderId int64 `json:"order_id"` // 订单ID
|
OrderId int64 `json:"order_id"` // 订单ID
|
||||||
UserId int64 `json:"user_id"` // 用户ID
|
UserId int64 `json:"user_id"` // 用户ID
|
||||||
ProductName string `json:"product_name"` // 产品ID
|
ProductName string `json:"product_name"` // 产品名称
|
||||||
|
OrderNo string `json:"order_no"` // 商户订单号
|
||||||
|
PlatformOrderId string `json:"platform_order_id"` // 支付订单号
|
||||||
|
PaymentStatus string `json:"payment_status"` // 支付状态
|
||||||
|
PayTime string `json:"pay_time"` // 支付时间
|
||||||
|
RefundTime string `json:"refund_time"` // 退款时间(有退款时)
|
||||||
QueryParams map[string]interface{} `json:"query_params"`
|
QueryParams map[string]interface{} `json:"query_params"`
|
||||||
QueryData []AdminQueryItem `json:"query_data"`
|
QueryData []AdminQueryItem `json:"query_data"`
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
@@ -811,6 +817,7 @@ type AdminReviewBankCardWithdrawalReq struct {
|
|||||||
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
WithdrawalId int64 `json:"withdrawal_id"` // 提现记录ID
|
||||||
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
Action int64 `json:"action"` // 操作:1-确认,2-拒绝
|
||||||
Remark string `json:"remark"` // 备注(拒绝时必填)
|
Remark string `json:"remark"` // 备注(拒绝时必填)
|
||||||
|
TaxRate *float64 `json:"tax_rate,optional"` // 扣税比例,如 0.06 表示 6%,不传则默认 6%
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminReviewBankCardWithdrawalResp struct {
|
type AdminReviewBankCardWithdrawalResp struct {
|
||||||
@@ -949,6 +956,7 @@ type AdminUpdatePlatformUserReq struct {
|
|||||||
Nickname *string `json:"nickname,optional"` // 昵称
|
Nickname *string `json:"nickname,optional"` // 昵称
|
||||||
Info *string `json:"info,optional"` // 备注信息
|
Info *string `json:"info,optional"` // 备注信息
|
||||||
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
|
Inside *int64 `json:"inside,optional"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable *int64 `json:"disable,optional"` // 是否封禁 0-可用 1-禁用
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminUpdatePlatformUserResp struct {
|
type AdminUpdatePlatformUserResp struct {
|
||||||
@@ -1300,6 +1308,7 @@ type AgentWithdrawalListItem struct {
|
|||||||
Amount float64 `json:"amount"` // 金额
|
Amount float64 `json:"amount"` // 金额
|
||||||
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
ActualAmount float64 `json:"actual_amount"` // 实际到账金额(扣税后)
|
||||||
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
TaxAmount float64 `json:"tax_amount"` // 扣税金额
|
||||||
|
TaxRate float64 `json:"tax_rate"` // 扣税比例,如 0.06 表示 6%
|
||||||
Status int64 `json:"status"` // 状态
|
Status int64 `json:"status"` // 状态
|
||||||
PayeeAccount string `json:"payee_account"` // 收款账户
|
PayeeAccount string `json:"payee_account"` // 收款账户
|
||||||
Remark string `json:"remark"` // 备注
|
Remark string `json:"remark"` // 备注
|
||||||
@@ -1538,6 +1547,10 @@ type GetCommissionResp struct {
|
|||||||
List []Commission `json:"list"` // 查询列表
|
List []Commission `json:"list"` // 查询列表
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetEncryptedSceneIdResp struct {
|
||||||
|
EncryptedSceneId string `json:"encryptedSceneId"`
|
||||||
|
}
|
||||||
|
|
||||||
type GetLinkDataReq struct {
|
type GetLinkDataReq struct {
|
||||||
LinkIdentifier string `form:"link_identifier"`
|
LinkIdentifier string `form:"link_identifier"`
|
||||||
}
|
}
|
||||||
@@ -1838,7 +1851,7 @@ type PaymentCheckResp struct {
|
|||||||
|
|
||||||
type PaymentReq struct {
|
type PaymentReq struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
PayMethod string `json:"pay_method"`
|
PayMethod string `json:"pay_method" validate:"required,oneof=wechat alipay appleiap test"`
|
||||||
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"`
|
PayType string `json:"pay_type" validate:"required,oneof=query agent_vip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1854,6 +1867,7 @@ type PlatformUserListItem struct {
|
|||||||
Nickname string `json:"nickname"` // 昵称
|
Nickname string `json:"nickname"` // 昵称
|
||||||
Info string `json:"info"` // 备注信息
|
Info string `json:"info"` // 备注信息
|
||||||
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
Inside int64 `json:"inside"` // 是否内部用户 1-是 0-否
|
||||||
|
Disable int64 `json:"disable"` // 是否封禁 0-可用 1-禁用
|
||||||
CreateTime string `json:"create_time"` // 创建时间
|
CreateTime string `json:"create_time"` // 创建时间
|
||||||
UpdateTime string `json:"update_time"` // 更新时间
|
UpdateTime string `json:"update_time"` // 更新时间
|
||||||
}
|
}
|
||||||
@@ -2005,6 +2019,7 @@ type QueryItem struct {
|
|||||||
type QueryListReq struct {
|
type QueryListReq struct {
|
||||||
Page int64 `form:"page"` // 页码
|
Page int64 `form:"page"` // 页码
|
||||||
PageSize int64 `form:"page_size"` // 每页数据量
|
PageSize int64 `form:"page_size"` // 每页数据量
|
||||||
|
Source string `form:"source,optional"` // 来源: miniapp 小程序过滤非车辆产品
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryListResp struct {
|
type QueryListResp struct {
|
||||||
@@ -2044,6 +2059,7 @@ type QueryServiceReq struct {
|
|||||||
Data string `json:"data" validate:"required"`
|
Data string `json:"data" validate:"required"`
|
||||||
AgentIdentifier string `json:"agent_identifier,optional"`
|
AgentIdentifier string `json:"agent_identifier,optional"`
|
||||||
App bool `json:"app,optional"`
|
App bool `json:"app,optional"`
|
||||||
|
CaptchaVerifyParam string `json:"captchaVerifyParam,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryServiceResp struct {
|
type QueryServiceResp struct {
|
||||||
@@ -2105,11 +2121,40 @@ type SaveAgentMembershipUserConfigReq struct {
|
|||||||
PriceRatio float64 `json:"price_ratio"`
|
PriceRatio float64 `json:"price_ratio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ServeUploadedFileReq struct {
|
||||||
|
FileName string `path:"fileName"` // 文件名,如 uuid.jpg
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServeUploadedFileResp struct {
|
||||||
|
FilePath string `json:"-"` // 内部:本地文件路径
|
||||||
|
ContentType string `json:"-"` // 内部:Content-Type
|
||||||
|
}
|
||||||
|
|
||||||
type TimeRangeReport struct {
|
type TimeRangeReport struct {
|
||||||
Commission float64 `json:"commission"` // 佣金
|
Commission float64 `json:"commission"` // 佣金
|
||||||
Report int `json:"report"` // 报告量
|
Report int `json:"report"` // 报告量
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ToolboxQueryReq struct {
|
||||||
|
ToolKey string `json:"tool_key" validate:"required"`
|
||||||
|
Params map[string]interface{} `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolboxQueryResp struct {
|
||||||
|
ToolKey string `json:"tool_key"`
|
||||||
|
Result map[string]interface{} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolInfo struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolboxListResp struct {
|
||||||
|
Tools []ToolInfo `json:"tools"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateMenuReq struct {
|
type UpdateMenuReq struct {
|
||||||
Id int64 `path:"id"` // 菜单ID
|
Id int64 `path:"id"` // 菜单ID
|
||||||
Pid int64 `json:"pid,optional"` // 父菜单ID
|
Pid int64 `json:"pid,optional"` // 父菜单ID
|
||||||
@@ -2160,6 +2205,14 @@ type UpdateRoleResp struct {
|
|||||||
Success bool `json:"success"` // 是否成功
|
Success bool `json:"success"` // 是否成功
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UploadImageReq struct {
|
||||||
|
ImageBase64 string `json:"image_base64" validate:"required"` // 图片 base64(不含 data URL 前缀)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadImageResp struct {
|
||||||
|
Url string `json:"url"` // 可公网访问的图片 URL
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
@@ -2198,6 +2251,7 @@ type Withdrawal struct {
|
|||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
PayeeAccount string `json:"payee_account"`
|
PayeeAccount string `json:"payee_account"`
|
||||||
CreateTime string `json:"create_time"`
|
CreateTime string `json:"create_time"`
|
||||||
|
TaxRate float64 `json:"tax_rate"` // 扣税比例,如 0.06 表示 6%
|
||||||
}
|
}
|
||||||
|
|
||||||
type WithdrawalReq struct {
|
type WithdrawalReq struct {
|
||||||
@@ -2219,4 +2273,5 @@ type GetAppVersionResp struct {
|
|||||||
type SendSmsReq struct {
|
type SendSmsReq struct {
|
||||||
Mobile string `json:"mobile" validate:"required,mobile"`
|
Mobile string `json:"mobile" validate:"required,mobile"`
|
||||||
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
ActionType string `json:"actionType" validate:"required,oneof=login register query agentApply realName bindMobile"`
|
||||||
|
CaptchaVerifyParam string `json:"captchaVerifyParam"`
|
||||||
}
|
}
|
||||||
|
|||||||
85
app/main/api/static/authorization_ivyz4y27.tmpl
Normal file
85
app/main/api/static/authorization_ivyz4y27.tmpl
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
【重要提示】为了保障您的合法权益,您在签署本授权书前,应当确保您为具有完全民事权利能力和民事行为能力的自然人,并审慎阅读、充分理解本授权书所有条款(特别是加粗字体的条款)。您在操作页面上的确认、勾选等行为或以其他方式接受即表示您已阅读并同意本授权书,本授权书随即在法律上生效并在您和{{.CompanyName}}之间产生法律约束力。
|
||||||
|
|
||||||
|
个人信息处理授权书
|
||||||
|
|
||||||
|
致:{{.CompanyName}}
|
||||||
|
|
||||||
|
本人("授权人")在此确认、同意并授权{{.CompanyName}}("贵司")及贵司授权的第三方,为本文件所列之目的,处理(指收集、整理、存储、使用、加工、分析和比对、核验、传输、提供等)本人的个人信息。
|
||||||
|
|
||||||
|
一、 授权处理的个人信息范围
|
||||||
|
|
||||||
|
1.本人同意并授权贵司处理以下类型的个人信息(无论以电子或非电子形式存在):
|
||||||
|
|
||||||
|
身份信息:包括但不限于姓名、身份证号码。
|
||||||
|
|
||||||
|
教育背景信息:包括但不限于各教育阶段的入学与毕业时间、毕业院校、专业、学位类型、学历学位证书信息、学信网验证信息等。
|
||||||
|
|
||||||
|
婚姻家庭信息:包括但不限于婚姻状况(未婚、已婚、离异等)。
|
||||||
|
|
||||||
|
工作履历信息:包括但不限于过往及当前工作单位、任职时间、职位、职责、证明人及其联系方式等。
|
||||||
|
|
||||||
|
其他与背景调查相关的信息:贵司为完成背景调查认为必要的其他信息。
|
||||||
|
|
||||||
|
2.本人知悉且同意,贵司拟处理的数据信息可能包含身份证件信息、生物识别信息等敏感信息,本人明确知悉对本人权益的影响且同意贵司处理该等信息。
|
||||||
|
|
||||||
|
二、 信息来源与查询方式
|
||||||
|
|
||||||
|
本人知晓并同意,贵司为完成背景调查,将通过以下方式查询并核实本授权书第一条所列的个人信息:
|
||||||
|
|
||||||
|
1. 通过本人或委托贵司开展背景调查的拟入职机构提供的联系方式和证明材料进行直接核实。
|
||||||
|
|
||||||
|
2. 通过贵司链接的权威数据源进行查询或验证、核实,包括但不限于政府部门或事业单位、社会团体、公用事业服务单位、第三方数据库或数据平台(如从中国高等教育学生信息网-学信网)等。本人授权贵司通过地方信用服务平台向前述数据源单位收集、处理、加工和分析本人的婚姻家庭信息、教育背景信息。
|
||||||
|
|
||||||
|
3. 通过向本人前任及现任雇主、同事、证明人等进行访谈核实。
|
||||||
|
|
||||||
|
4. 在法律允许的范围内,通过其他公开、合法的渠道进行核实。
|
||||||
|
|
||||||
|
三、 信息处理的目的与使用
|
||||||
|
|
||||||
|
本人同意贵司将本授权书项下收集的个人信息用于以下目的:
|
||||||
|
|
||||||
|
1. 为{{.CompanyName}}("委托方")对本人进行的员工招聘/录用前背景调查/持续的尽职调查提供信息核实与报告服务。
|
||||||
|
|
||||||
|
2. 基于核实的信息,生成关于本人的《背景调查报告》或《尽职调查报告》("报告"),并提供给委托方,以供其评估本人的任职资格与工作能力。
|
||||||
|
|
||||||
|
3. 贵司为内部质量控制和合规目的而对相关信息进行留存、处理与管理。
|
||||||
|
|
||||||
|
四、 信息存储与保护
|
||||||
|
|
||||||
|
贵司承诺将采取必要的技术和管理措施,保护本人个人信息的安全性与机密性,防止信息丢失、泄露、篡改或毁损。
|
||||||
|
|
||||||
|
除非法律法规另有规定或为履行本授权书目的之必要,贵司处理及存储本人个人信息的时间将在完成本次背景调查目的后12个月内,或直至委托方与本人的劳动关系确立或明确终止之时。具体留存期限届满后,贵司将依法对相关信息进行删除或匿名化处理。删除个人信息从技术上难以实现的,贵司将停止除存储和采取必要的安全保护措施之外的处理。
|
||||||
|
|
||||||
|
五、 授权转移与共享
|
||||||
|
|
||||||
|
(一)本人知晓并同意,为实现本授权书之目的,贵司可能将本人的个人信息提供给以下接收方:
|
||||||
|
|
||||||
|
1. 委托方(即本次背景调查的发起企业/拟入职机构)。
|
||||||
|
|
||||||
|
2. 为完成特定核实工作而必需的第三方合作伙伴,如数据源提供方(包括但不限于地方融资信用服务平台、政府部门或事业单位)等,但贵司应确保该等第三方受到与本授权书同等严格的保密义务约束。
|
||||||
|
|
||||||
|
六、 权利告知与行使
|
||||||
|
|
||||||
|
1.本人知悉并理解,根据相关法律法规,本人有权:
|
||||||
|
|
||||||
|
(1)查阅、复制及要求更正本人的个人信息;
|
||||||
|
|
||||||
|
(2)在满足法定条件时,要求删除个人信息或撤回本授权同意。
|
||||||
|
|
||||||
|
本人确认,撤回本授权同意将不影响撤回前基于本授权已进行的个人信息处理活动的效力。但若撤回授权,可能导致委托方无法完整评估本人的任职资格,从而可能影响本次招聘/录用结果,本人将自行承担相应后果。
|
||||||
|
|
||||||
|
2. 本人知悉:如本人对贵司或数据源单位个人信息处理活动有任何疑问、意见建议或需要依法行使权利,可通过以下联系方式进行咨询、反映或行使法定权利:
|
||||||
|
|
||||||
|
贵司:【联系方式:{{.Mobile}}】
|
||||||
|
|
||||||
|
七、 全部协议与授权效力
|
||||||
|
|
||||||
|
本授权书自本人同意之日起生效,并在上述业务办理及存续期间持续有效,至本授权书所述的所有业务终结之日止。
|
||||||
|
|
||||||
|
本人已仔细阅读并完全理解本授权书的全部内容,特别是加粗字体部分。本人的签署是基于本人的真实意愿,本人知悉且理解由此产生的法律效力及相应信息披露产生的不利后果(包括不限于第三方通过非法手段或方式获取、使用该信息,可能会给本人造成人身财产的损害,或是造成本人预期利益减少、损失扩大,或是其他不良或不利影响等),自愿作出上述授权。
|
||||||
|
|
||||||
|
|
||||||
|
授权人签署:{{.Name}}
|
||||||
|
身份证号码:{{.IdCard}}
|
||||||
|
|
||||||
|
签署日期:{{.Date}}
|
||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tyc-server/common/globalkey"
|
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
@@ -19,6 +17,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
"tyc-server/common/globalkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -65,6 +64,7 @@ type (
|
|||||||
LevelName string `db:"level_name"` // 会员级别,如 VIP,SVIP,normal
|
LevelName string `db:"level_name"` // 会员级别,如 VIP,SVIP,normal
|
||||||
Amount float64 `db:"amount"` // 充值金额
|
Amount float64 `db:"amount"` // 充值金额
|
||||||
PaymentMethod string `db:"payment_method"` // 支付方式:支付宝,微信,苹果支付,其他
|
PaymentMethod string `db:"payment_method"` // 支付方式:支付宝,微信,苹果支付,其他
|
||||||
|
PaymentMerchant string `db:"payment_merchant"` // 支付商户标识,例如 one/two
|
||||||
OrderNo string `db:"order_no"` // 交易号
|
OrderNo string `db:"order_no"` // 交易号
|
||||||
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号
|
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号
|
||||||
Status string `db:"status"`
|
Status string `db:"status"`
|
||||||
@@ -89,11 +89,11 @@ func (m *defaultAgentMembershipRechargeOrderModel) Insert(ctx context.Context, s
|
|||||||
tycAgentMembershipRechargeOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTycAgentMembershipRechargeOrderOrderNoPrefix, data.OrderNo)
|
tycAgentMembershipRechargeOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTycAgentMembershipRechargeOrderOrderNoPrefix, data.OrderNo)
|
||||||
tycAgentMembershipRechargeOrderPlatformOrderIdKey := fmt.Sprintf("%s%v", cacheTycAgentMembershipRechargeOrderPlatformOrderIdPrefix, data.PlatformOrderId)
|
tycAgentMembershipRechargeOrderPlatformOrderIdKey := fmt.Sprintf("%s%v", cacheTycAgentMembershipRechargeOrderPlatformOrderIdPrefix, data.PlatformOrderId)
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentMembershipRechargeOrderRowsExpectAutoSet)
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, agentMembershipRechargeOrderRowsExpectAutoSet)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, data.UserId, data.AgentId, data.LevelName, data.Amount, data.PaymentMethod, data.OrderNo, data.PlatformOrderId, data.Status, data.DeleteTime, data.DelState, data.Version)
|
return session.ExecCtx(ctx, query, data.UserId, data.AgentId, data.LevelName, data.Amount, data.PaymentMethod, data.PaymentMerchant, data.OrderNo, data.PlatformOrderId, data.Status, data.DeleteTime, data.DelState, data.Version)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, data.UserId, data.AgentId, data.LevelName, data.Amount, data.PaymentMethod, data.OrderNo, data.PlatformOrderId, data.Status, data.DeleteTime, data.DelState, data.Version)
|
return conn.ExecCtx(ctx, query, data.UserId, data.AgentId, data.LevelName, data.Amount, data.PaymentMethod, data.PaymentMerchant, data.OrderNo, data.PlatformOrderId, data.Status, data.DeleteTime, data.DelState, data.Version)
|
||||||
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +165,9 @@ func (m *defaultAgentMembershipRechargeOrderModel) Update(ctx context.Context, s
|
|||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentMembershipRechargeOrderRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, agentMembershipRechargeOrderRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
return session.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.PaymentMerchant, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
return conn.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.PaymentMerchant, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id)
|
||||||
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +189,9 @@ func (m *defaultAgentMembershipRechargeOrderModel) UpdateWithVersion(ctx context
|
|||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentMembershipRechargeOrderRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, agentMembershipRechargeOrderRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
return session.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.PaymentMerchant, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
return conn.ExecCtx(ctx, query, newData.UserId, newData.AgentId, newData.LevelName, newData.Amount, newData.PaymentMethod, newData.PaymentMerchant, newData.OrderNo, newData.PlatformOrderId, newData.Status, newData.DeleteTime, newData.DelState, newData.Version, newData.Id, oldVersion)
|
||||||
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
}, tycAgentMembershipRechargeOrderIdKey, tycAgentMembershipRechargeOrderOrderNoKey, tycAgentMembershipRechargeOrderPlatformOrderIdKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tyc-server/common/globalkey"
|
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
@@ -19,6 +17,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
"tyc-server/common/globalkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -63,6 +62,7 @@ type (
|
|||||||
ProductId int64 `db:"product_id"` // 产品ID(软关联到产品表)
|
ProductId int64 `db:"product_id"` // 产品ID(软关联到产品表)
|
||||||
PaymentPlatform string `db:"payment_platform"` // 支付平台(支付宝、微信、苹果内购、其他)
|
PaymentPlatform string `db:"payment_platform"` // 支付平台(支付宝、微信、苹果内购、其他)
|
||||||
PaymentScene string `db:"payment_scene"` // 支付场景(App、H5、微信小程序、公众号)
|
PaymentScene string `db:"payment_scene"` // 支付场景(App、H5、微信小程序、公众号)
|
||||||
|
PaymentMerchant string `db:"payment_merchant"` // 支付商户标识,例如 one/two
|
||||||
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号
|
PlatformOrderId sql.NullString `db:"platform_order_id"` // 支付平台订单号
|
||||||
Amount float64 `db:"amount"` // 支付金额
|
Amount float64 `db:"amount"` // 支付金额
|
||||||
Status string `db:"status"` // 支付状态
|
Status string `db:"status"` // 支付状态
|
||||||
@@ -90,11 +90,11 @@ func (m *defaultOrderModel) Insert(ctx context.Context, session sqlx.Session, da
|
|||||||
tycOrderIdKey := fmt.Sprintf("%s%v", cacheTycOrderIdPrefix, data.Id)
|
tycOrderIdKey := fmt.Sprintf("%s%v", cacheTycOrderIdPrefix, data.Id)
|
||||||
tycOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTycOrderOrderNoPrefix, data.OrderNo)
|
tycOrderOrderNoKey := fmt.Sprintf("%s%v", cacheTycOrderOrderNoPrefix, data.OrderNo)
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, orderRowsExpectAutoSet)
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, orderRowsExpectAutoSet)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
|
return session.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PaymentMerchant, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
|
return conn.ExecCtx(ctx, query, data.OrderNo, data.UserId, data.ProductId, data.PaymentPlatform, data.PaymentScene, data.PaymentMerchant, data.PlatformOrderId, data.Amount, data.Status, data.DelState, data.Version, data.PayTime, data.RefundTime, data.CloseTime, data.DeleteTime, data.SalesCost)
|
||||||
}, tycOrderIdKey, tycOrderOrderNoKey)
|
}, tycOrderIdKey, tycOrderOrderNoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,9 +145,9 @@ func (m *defaultOrderModel) Update(ctx context.Context, session sqlx.Session, ne
|
|||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
|
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PaymentMerchant, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
|
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PaymentMerchant, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id)
|
||||||
}, tycOrderIdKey, tycOrderOrderNoKey)
|
}, tycOrderIdKey, tycOrderOrderNoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +168,9 @@ func (m *defaultOrderModel) UpdateWithVersion(ctx context.Context, session sqlx.
|
|||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, orderRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, orderRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
|
return session.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PaymentMerchant, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
|
return conn.ExecCtx(ctx, query, newData.OrderNo, newData.UserId, newData.ProductId, newData.PaymentPlatform, newData.PaymentScene, newData.PaymentMerchant, newData.PlatformOrderId, newData.Amount, newData.Status, newData.DelState, newData.Version, newData.PayTime, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.SalesCost, newData.Id, oldVersion)
|
||||||
}, tycOrderIdKey, tycOrderOrderNoKey)
|
}, tycOrderIdKey, tycOrderOrderNoKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tyc-server/common/globalkey"
|
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||||
@@ -19,6 +17,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
"tyc-server/common/globalkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -64,6 +63,7 @@ type (
|
|||||||
OrderId int64 `db:"order_id"` // 关联的订单ID
|
OrderId int64 `db:"order_id"` // 关联的订单ID
|
||||||
UserId int64 `db:"user_id"` // 用户ID
|
UserId int64 `db:"user_id"` // 用户ID
|
||||||
ProductId int64 `db:"product_id"` // 产品ID
|
ProductId int64 `db:"product_id"` // 产品ID
|
||||||
|
PaymentMerchant string `db:"payment_merchant"` // 退款对应的支付商户标识,例如 one/two
|
||||||
PlatformRefundId sql.NullString `db:"platform_refund_id"` // 支付平台退款单号
|
PlatformRefundId sql.NullString `db:"platform_refund_id"` // 支付平台退款单号
|
||||||
RefundAmount float64 `db:"refund_amount"` // 退款金额
|
RefundAmount float64 `db:"refund_amount"` // 退款金额
|
||||||
RefundReason sql.NullString `db:"refund_reason"` // 退款原因
|
RefundReason sql.NullString `db:"refund_reason"` // 退款原因
|
||||||
@@ -91,11 +91,11 @@ func (m *defaultOrderRefundModel) Insert(ctx context.Context, session sqlx.Sessi
|
|||||||
tycOrderRefundPlatformRefundIdKey := fmt.Sprintf("%s%v", cacheTycOrderRefundPlatformRefundIdPrefix, data.PlatformRefundId)
|
tycOrderRefundPlatformRefundIdKey := fmt.Sprintf("%s%v", cacheTycOrderRefundPlatformRefundIdPrefix, data.PlatformRefundId)
|
||||||
tycOrderRefundRefundNoKey := fmt.Sprintf("%s%v", cacheTycOrderRefundRefundNoPrefix, data.RefundNo)
|
tycOrderRefundRefundNoKey := fmt.Sprintf("%s%v", cacheTycOrderRefundRefundNoPrefix, data.RefundNo)
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, orderRefundRowsExpectAutoSet)
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, orderRefundRowsExpectAutoSet)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, data.RefundNo, data.OrderId, data.UserId, data.ProductId, data.PlatformRefundId, data.RefundAmount, data.RefundReason, data.Status, data.DelState, data.Version, data.RefundTime, data.CloseTime, data.DeleteTime)
|
return session.ExecCtx(ctx, query, data.RefundNo, data.OrderId, data.UserId, data.ProductId, data.PaymentMerchant, data.PlatformRefundId, data.RefundAmount, data.RefundReason, data.Status, data.DelState, data.Version, data.RefundTime, data.CloseTime, data.DeleteTime)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, data.RefundNo, data.OrderId, data.UserId, data.ProductId, data.PlatformRefundId, data.RefundAmount, data.RefundReason, data.Status, data.DelState, data.Version, data.RefundTime, data.CloseTime, data.DeleteTime)
|
return conn.ExecCtx(ctx, query, data.RefundNo, data.OrderId, data.UserId, data.ProductId, data.PaymentMerchant, data.PlatformRefundId, data.RefundAmount, data.RefundReason, data.Status, data.DelState, data.Version, data.RefundTime, data.CloseTime, data.DeleteTime)
|
||||||
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,9 +167,9 @@ func (m *defaultOrderRefundModel) Update(ctx context.Context, session sqlx.Sessi
|
|||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRefundRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, orderRefundRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id)
|
return session.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PaymentMerchant, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id)
|
return conn.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PaymentMerchant, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id)
|
||||||
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,9 +191,9 @@ func (m *defaultOrderRefundModel) UpdateWithVersion(ctx context.Context, session
|
|||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, orderRefundRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, orderRefundRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion)
|
return session.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PaymentMerchant, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion)
|
return conn.ExecCtx(ctx, query, newData.RefundNo, newData.OrderId, newData.UserId, newData.ProductId, newData.PaymentMerchant, newData.PlatformRefundId, newData.RefundAmount, newData.RefundReason, newData.Status, newData.DelState, newData.Version, newData.RefundTime, newData.CloseTime, newData.DeleteTime, newData.Id, oldVersion)
|
||||||
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
}, tycOrderRefundIdKey, tycOrderRefundPlatformRefundIdKey, tycOrderRefundRefundNoKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type (
|
|||||||
Nickname sql.NullString `db:"nickname"`
|
Nickname sql.NullString `db:"nickname"`
|
||||||
Info string `db:"info"`
|
Info string `db:"info"`
|
||||||
Inside int64 `db:"inside"`
|
Inside int64 `db:"inside"`
|
||||||
|
Disable int64 `db:"disable"` // 0可用 1禁用
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,11 +84,11 @@ func (m *defaultUserModel) Insert(ctx context.Context, session sqlx.Session, dat
|
|||||||
tycUserIdKey := fmt.Sprintf("%s%v", cacheTycUserIdPrefix, data.Id)
|
tycUserIdKey := fmt.Sprintf("%s%v", cacheTycUserIdPrefix, data.Id)
|
||||||
tycUserMobileKey := fmt.Sprintf("%s%v", cacheTycUserMobilePrefix, data.Mobile)
|
tycUserMobileKey := fmt.Sprintf("%s%v", cacheTycUserMobilePrefix, data.Mobile)
|
||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
|
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside)
|
return session.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside, data.Disable)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside)
|
return conn.ExecCtx(ctx, query, data.DeleteTime, data.DelState, data.Version, data.Mobile, data.Password, data.Nickname, data.Info, data.Inside, data.Disable)
|
||||||
}, tycUserIdKey, tycUserMobileKey)
|
}, tycUserIdKey, tycUserMobileKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +139,9 @@ func (m *defaultUserModel) Update(ctx context.Context, session sqlx.Session, new
|
|||||||
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
return m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id)
|
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id)
|
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id)
|
||||||
}, tycUserIdKey, tycUserMobileKey)
|
}, tycUserIdKey, tycUserMobileKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +162,9 @@ func (m *defaultUserModel) UpdateWithVersion(ctx context.Context, session sqlx.S
|
|||||||
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
sqlResult, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userRowsWithPlaceHolder)
|
query := fmt.Sprintf("update %s set %s where `id` = ? and version = ? ", m.table, userRowsWithPlaceHolder)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id, oldVersion)
|
return session.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id, oldVersion)
|
||||||
}
|
}
|
||||||
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Id, oldVersion)
|
return conn.ExecCtx(ctx, query, newData.DeleteTime, newData.DelState, newData.Version, newData.Mobile, newData.Password, newData.Nickname, newData.Info, newData.Inside, newData.Disable, newData.Id, oldVersion)
|
||||||
}, tycUserIdKey, tycUserMobileKey)
|
}, tycUserIdKey, tycUserMobileKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func GetPlatformFromCtx(ctx context.Context) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch platform {
|
switch platform {
|
||||||
case model.PlatformWxMini:
|
case model.PlatformWxMini, "mp-weixin": // 兼容旧客户端误传的 uni 平台名
|
||||||
return model.PlatformWxMini, nil
|
return model.PlatformWxMini, nil
|
||||||
case model.PlatformWxH5:
|
case model.PlatformWxH5:
|
||||||
return model.PlatformWxH5, nil
|
return model.PlatformWxH5, nil
|
||||||
@@ -99,6 +99,6 @@ func GetPlatformFromCtx(ctx context.Context) (string, error) {
|
|||||||
case model.PlatformH5:
|
case model.PlatformH5:
|
||||||
return model.PlatformH5, nil
|
return model.PlatformH5, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("不支持的支付平台: %s", platform)
|
return "", fmt.Errorf("不支持的客户端平台: %s", platform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const PARAM_VERIFICATION_ERROR uint32 = 100007
|
|||||||
const CUSTOM_ERROR uint32 = 100008
|
const CUSTOM_ERROR uint32 = 100008
|
||||||
const USER_NOT_FOUND uint32 = 100009
|
const USER_NOT_FOUND uint32 = 100009
|
||||||
const USER_NEED_BIND_MOBILE uint32 = 100010
|
const USER_NEED_BIND_MOBILE uint32 = 100010
|
||||||
|
const USER_DISABLED uint32 = 100011 // 用户已被封禁
|
||||||
|
|
||||||
const LOGIN_FAILED uint32 = 200001
|
const LOGIN_FAILED uint32 = 200001
|
||||||
const LOGIC_QUERY_WAIT uint32 = 200002
|
const LOGIC_QUERY_WAIT uint32 = 200002
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ func init() {
|
|||||||
message[TOKEN_GENERATE_ERROR] = "生成token失败"
|
message[TOKEN_GENERATE_ERROR] = "生成token失败"
|
||||||
message[DB_ERROR] = "系统维护升级中,请稍后再试"
|
message[DB_ERROR] = "系统维护升级中,请稍后再试"
|
||||||
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
|
message[DB_UPDATE_AFFECTED_ZERO_ERROR] = "更新数据影响行数为0"
|
||||||
|
message[USER_DISABLED] = "您的账号已被封禁,如有疑问请联系客服"
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapErrMsg(errcode uint32) string {
|
func MapErrMsg(errcode uint32) string {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ $DB_URL = "tyc:5vg67b3UNHu8@tcp(127.0.0.1:22001)/tyc"
|
|||||||
$OUTPUT_DIR = "./model"
|
$OUTPUT_DIR = "./model"
|
||||||
$TEMPLATE_DIR = "../template"
|
$TEMPLATE_DIR = "../template"
|
||||||
|
|
||||||
# 表名列表
|
# 表名列表(按需开启)
|
||||||
$tables = @(
|
$tables = @(
|
||||||
# "agent"
|
# "agent"
|
||||||
# "agent_active_stat",
|
# "agent_active_stat",
|
||||||
@@ -16,7 +16,7 @@ $tables = @(
|
|||||||
# "agent_commission_deduction"
|
# "agent_commission_deduction"
|
||||||
# "agent_link",
|
# "agent_link",
|
||||||
# "agent_membership_config",
|
# "agent_membership_config",
|
||||||
# "agent_membership_recharge_order"
|
"agent_membership_recharge_order"
|
||||||
# "agent_membership_user_config",
|
# "agent_membership_user_config",
|
||||||
# "agent_order",
|
# "agent_order",
|
||||||
# "agent_platform_deduction"
|
# "agent_platform_deduction"
|
||||||
@@ -29,8 +29,8 @@ $tables = @(
|
|||||||
# "agent_withdrawal_tax_exemption"
|
# "agent_withdrawal_tax_exemption"
|
||||||
# "feature"
|
# "feature"
|
||||||
# "global_notifications"
|
# "global_notifications"
|
||||||
# "order"
|
"order"
|
||||||
# "order_refund"
|
"order_refund"
|
||||||
# "product",
|
# "product",
|
||||||
# "product_feature"
|
# "product_feature"
|
||||||
# "query",
|
# "query",
|
||||||
@@ -54,8 +54,7 @@ $tables = @(
|
|||||||
# "admin_promotion_link_stats_total"
|
# "admin_promotion_link_stats_total"
|
||||||
# "admin_promotion_link_stats_history"
|
# "admin_promotion_link_stats_history"
|
||||||
# "admin_promotion_order"
|
# "admin_promotion_order"
|
||||||
"query_user_record"
|
# "query_user_record"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 为每个表生成模型
|
# 为每个表生成模型
|
||||||
|
|||||||
90
deploy/script/m.sql
Normal file
90
deploy/script/m.sql
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
-- =========================
|
||||||
|
-- 1. 表结构变更:新增 payment_merchant
|
||||||
|
-- =========================
|
||||||
|
|
||||||
|
-- 1.1 order 表:增加支付商户标识
|
||||||
|
ALTER TABLE `order`
|
||||||
|
ADD COLUMN `payment_merchant` varchar(64) NOT NULL DEFAULT '' COMMENT '支付商户标识,例如 one/two' AFTER `payment_scene`;
|
||||||
|
|
||||||
|
-- 1.2 order_refund 表:增加支付商户标识
|
||||||
|
ALTER TABLE `order_refund`
|
||||||
|
ADD COLUMN `payment_merchant` varchar(64) NOT NULL DEFAULT '' COMMENT '退款对应的支付商户标识,例如 one/two' AFTER `product_id`;
|
||||||
|
|
||||||
|
-- 1.3 agent_membership_recharge_order 表:增加支付商户标识
|
||||||
|
ALTER TABLE `agent_membership_recharge_order`
|
||||||
|
ADD COLUMN `payment_merchant` varchar(64) NOT NULL DEFAULT '' COMMENT '支付商户标识,例如 one/two' AFTER `payment_method`;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 2. 历史数据初始化:one / two
|
||||||
|
-- 约定:
|
||||||
|
-- - one:当前主支付宝商户
|
||||||
|
-- - two:当前 bak 支付宝商户
|
||||||
|
-- 时间区间:
|
||||||
|
-- [2026-01-25 16:38:17, 2026-02-02 18:26:00)
|
||||||
|
-- =========================
|
||||||
|
|
||||||
|
-- 2.1 order 表:按时间区间映射 one / two
|
||||||
|
-- 仅处理支付宝订单(payment_platform='alipay')
|
||||||
|
-- 区间内用 two,其余用 one
|
||||||
|
-- 时间优先用 pay_time,pay_time 为空则用 create_time
|
||||||
|
|
||||||
|
-- 2.1.1 全部支付宝订单默认标记为 one
|
||||||
|
UPDATE `order`
|
||||||
|
SET
|
||||||
|
payment_merchant = 'one'
|
||||||
|
WHERE
|
||||||
|
payment_platform = 'alipay'
|
||||||
|
AND del_state = 0;
|
||||||
|
|
||||||
|
-- 2.1.2 区间内的支付宝订单标记为 two
|
||||||
|
UPDATE `order`
|
||||||
|
SET
|
||||||
|
payment_merchant = 'two'
|
||||||
|
WHERE
|
||||||
|
payment_platform = 'alipay'
|
||||||
|
AND del_state = 0
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
pay_time IS NOT NULL
|
||||||
|
AND pay_time >= '2026-01-25 16:38:17'
|
||||||
|
AND pay_time < '2026-02-02 18:26:00'
|
||||||
|
)
|
||||||
|
OR (
|
||||||
|
pay_time IS NULL
|
||||||
|
AND create_time >= '2026-01-25 16:38:17'
|
||||||
|
AND create_time < '2026-02-02 18:26:00'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2.2 agent_membership_recharge_order 表:按创建时间映射 one / two
|
||||||
|
-- 仅处理支付宝支付(payment_method='alipay')
|
||||||
|
-- 区间内创建的订单标记为 two,其余为 one
|
||||||
|
|
||||||
|
-- 2.2.1 所有支付宝代理会员充值订单默认标记为 one
|
||||||
|
UPDATE `agent_membership_recharge_order`
|
||||||
|
SET
|
||||||
|
payment_merchant = 'one'
|
||||||
|
WHERE
|
||||||
|
payment_method = 'alipay'
|
||||||
|
AND del_state = 0;
|
||||||
|
|
||||||
|
-- 2.2.2 区间内的支付宝代理会员订单标记为 two
|
||||||
|
UPDATE `agent_membership_recharge_order`
|
||||||
|
SET
|
||||||
|
payment_merchant = 'two'
|
||||||
|
WHERE
|
||||||
|
payment_method = 'alipay'
|
||||||
|
AND del_state = 0
|
||||||
|
AND create_time >= '2026-01-25 16:38:17'
|
||||||
|
AND create_time < '2026-02-02 18:26:00';
|
||||||
|
|
||||||
|
-- 2.3 order_refund 表:跟随对应订单的 payment_merchant
|
||||||
|
-- 直接复制 order.payment_merchant,避免逻辑重复
|
||||||
|
|
||||||
|
UPDATE `order_refund` r
|
||||||
|
JOIN `order` o ON r.order_id = o.id
|
||||||
|
SET
|
||||||
|
r.payment_merchant = o.payment_merchant
|
||||||
|
WHERE
|
||||||
|
r.del_state = 0
|
||||||
|
AND o.del_state = 0;
|
||||||
2558
deploy/sql/product_feature_inserts.sql
Normal file
2558
deploy/sql/product_feature_inserts.sql
Normal file
File diff suppressed because it is too large
Load Diff
13
deploy/sql/product_inquire_category_items.sql
Normal file
13
deploy/sql/product_inquire_category_items.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- 司法涉诉、婚姻状况等二级分类小项产品(与前端 inquireCategories 配置对应)
|
||||||
|
-- 执行前请确认 id 不与现有 product 表冲突;若已有 31+ 可改为更大起始 id 或去掉 id 让其自增
|
||||||
|
|
||||||
|
INSERT INTO `product` (`id`, `create_time`, `update_time`, `delete_time`, `del_state`, `version`, `product_name`, `product_en`, `description`, `notes`, `cost_price`, `sell_price`) VALUES
|
||||||
|
(31, NOW(), NOW(), NULL, 0, 0, '企业司法涉诉', 'toc_EnterpriseLawsuit', '<p>企业司法涉诉服务,可查询企业涉诉与执行信息,帮助您评估企业法律风险。</p>', NULL, 0.50, 28.80),
|
||||||
|
(32, NOW(), NOW(), NULL, 0, 0, '被执行人', 'toc_ExecutedPerson', '<p>被执行人信息查询,帮助您了解个人或企业是否被列为被执行人及其执行案件情况。</p>', NULL, 0.36, 18.80),
|
||||||
|
(33, NOW(), NOW(), NULL, 0, 0, '限制高消费', 'toc_LimitHigh', '<p>限制高消费信息查询,可核查个人或企业是否被采取限制高消费措施。</p>', NULL, 0.36, 18.80),
|
||||||
|
(34, NOW(), NOW(), NULL, 0, 0, '个人婚姻状态', 'toc_PersonalMarriageStatus', '<p>个人婚姻登记状态查询,帮助您了解个人的婚姻状态(未登记/结婚/离婚)。</p>', NULL, 1.00, 39.90),
|
||||||
|
(35, NOW(), NOW(), NULL, 0, 0, '婚姻状态查询(登记时间版)', 'toc_MarriageStatusRegisterTime', '<p>按登记时间查询婚姻状态,提供婚姻登记时间维度的核查服务。</p>', NULL, 1.00, 39.90),
|
||||||
|
(36, NOW(), NOW(), NULL, 0, 0, '婚姻状态查询(补证版)', 'toc_MarriageStatusSupplement', '<p>补证版婚姻状态查询,适用于补领婚姻登记证明等场景的婚姻状态核查。</p>', NULL, 1.00, 39.90),
|
||||||
|
(37, NOW(), NOW(), NULL, 0, 0, '婚姻状态核验', 'toc_MarriageStatusVerify', '<p>婚姻状态核验服务,对指定人员的婚姻状态进行快速核验。</p>', NULL, 1.00, 39.90),
|
||||||
|
(38, NOW(), NOW(), NULL, 0, 0, '双人婚姻状态(登记时间版)', 'toc_DualMarriageStatusRegisterTime', '<p>双人婚姻状态按登记时间查询,可同时核查双方婚姻登记状态及登记时间信息。</p>', NULL, 1.50, 59.90);
|
||||||
|
COMMIT;
|
||||||
285
deploy/sql/repair_order_refund_q176967208700018143d9f6.sql
Normal file
285
deploy/sql/repair_order_refund_q176967208700018143d9f6.sql
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- 修复订单 Q_176967208700018143d9f6:支付宝已退款成功,但库内未完成
|
||||||
|
-- =============================================================================
|
||||||
|
--
|
||||||
|
-- 【原因简述】
|
||||||
|
-- 后台发起支付宝退款后,支付宝侧已退款成功,但创建退款记录时因 unique_refund_no
|
||||||
|
-- 冲突(Duplicate entry 'refund-Q_176967208700018143d9f6')导致 createRefundRecordAndUpdateOrder
|
||||||
|
-- 失败,后续流程未执行:订单未置为 refunded、未写退款记录、未扣代理佣金与钱包。
|
||||||
|
--
|
||||||
|
-- 【本单实际退款金额】支付宝已退 99.5 元(非整单金额)
|
||||||
|
--
|
||||||
|
-- 【与 adminrefundorderlogic 退款链路对照】
|
||||||
|
-- 代码路径:AdminRefundOrder -> handleAlipayRefund -> createRefundRecordAndUpdateOrder + HandleCommissionAndWalletDeduction
|
||||||
|
--
|
||||||
|
-- handleAlipayRefund 成功分支:
|
||||||
|
-- 1) createRefundRecordAndUpdateOrder(order, req, refundNo, refundResp.TradeNo, ...)
|
||||||
|
-- -> 事务内:Insert order_refund(refund_no, platform_refund_id=TradeNo, order_id, user_id, product_id, refund_amount, status=success, refund_time=NOW())
|
||||||
|
-- -> 事务内:Update order(仅 code 中赋了 status=refunded,未显式设 refund_time/version)
|
||||||
|
-- 2) HandleCommissionAndWalletDeduction(ctx, svcCtx, nil, order, req.RefundAmount)
|
||||||
|
-- -> 该订单下 agent_commission:按 refundAmount 比例冲减,refunded_amount 增加,满额则 status=2(UpdateWithVersion)
|
||||||
|
-- -> 按代理汇总扣减额,agent_wallet 先扣冻结再扣可用(UpdateWithVersion)
|
||||||
|
-- -> agent_wallet_transaction 插入 type=refund、金额为负的流水
|
||||||
|
--
|
||||||
|
-- 本 SQL 对应关系:
|
||||||
|
-- Step 2 = createRefundRecordAndUpdateOrder 的 Insert order_refund(platform_refund_id 修复时无支付宝 TradeNo 填 NULL,可从支付宝补)
|
||||||
|
-- Step 3 = createRefundRecordAndUpdateOrder 的 Update order,并显式补全 refund_time、version+1
|
||||||
|
-- Step 4 = HandleCommissionAndWalletDeduction 对 agent_commission 的冲减(按 99.5 比例)
|
||||||
|
-- Step 5 = HandleCommissionAndWalletDeduction 对 agent_wallet 扣减 + agent_wallet_transaction 插入
|
||||||
|
--
|
||||||
|
-- 【执行前请确认】
|
||||||
|
-- 1)该订单在支付宝侧已退款成功;2)已备份相关表或先在测试环境执行。
|
||||||
|
-- Step 2~5 已包在事务中:任一步报错请执行 ROLLBACK;若 step 1 未查到订单(_order_not_found=1)勿执行后续。
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- 与表字段 collation 一致,避免 #1267 Illegal mix of collations(若表为 utf8mb4_general_ci 则改为 COLLATE utf8mb4_general_ci)
|
||||||
|
SET
|
||||||
|
@order_no = CONVERT(
|
||||||
|
'Q_176967208700018143d9f6' USING utf8mb4
|
||||||
|
) COLLATE utf8mb4_general_ci;
|
||||||
|
-- 本单实际退款金额(元)
|
||||||
|
SET @repair_refund_amount = 99.5;
|
||||||
|
|
||||||
|
-- 1) 取订单信息
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
user_id,
|
||||||
|
product_id,
|
||||||
|
amount,
|
||||||
|
version INTO @order_id,
|
||||||
|
@user_id,
|
||||||
|
@product_id,
|
||||||
|
@order_amount,
|
||||||
|
@order_version
|
||||||
|
FROM `order`
|
||||||
|
WHERE
|
||||||
|
order_no = @order_no
|
||||||
|
AND del_state = 0
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SET @refund_amount = @repair_refund_amount;
|
||||||
|
|
||||||
|
-- 若无记录则说明订单号错误,终止
|
||||||
|
SELECT IF(@order_id IS NULL, 1, 0) AS _order_not_found;
|
||||||
|
-- 若 _order_not_found=1 请勿继续执行后续语句
|
||||||
|
|
||||||
|
-- ---------- 以下为事务:任一步报错请执行 ROLLBACK ----------
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
-- 2) 补写退款记录(若该订单尚无成功状态的退款记录)
|
||||||
|
-- 代码中 platform_refund_id 来自支付宝 refundResp.TradeNo;修复时无则填 NULL,如有支付宝退款单号可事后 UPDATE 补上
|
||||||
|
INSERT INTO
|
||||||
|
order_refund (
|
||||||
|
refund_no,
|
||||||
|
order_id,
|
||||||
|
user_id,
|
||||||
|
product_id,
|
||||||
|
platform_refund_id,
|
||||||
|
refund_amount,
|
||||||
|
refund_reason,
|
||||||
|
status,
|
||||||
|
del_state,
|
||||||
|
version,
|
||||||
|
refund_time,
|
||||||
|
close_time,
|
||||||
|
delete_time
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
CONCAT(
|
||||||
|
'refund-',
|
||||||
|
@order_no,
|
||||||
|
'-repair'
|
||||||
|
),
|
||||||
|
@order_id,
|
||||||
|
@user_id,
|
||||||
|
@product_id,
|
||||||
|
NULL,
|
||||||
|
@refund_amount,
|
||||||
|
NULL,
|
||||||
|
'success',
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NOW(),
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
) _one
|
||||||
|
WHERE
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM order_refund
|
||||||
|
WHERE
|
||||||
|
order_id = @order_id
|
||||||
|
AND status = 'success'
|
||||||
|
AND del_state = 0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3) 订单状态改为已退款
|
||||||
|
UPDATE `order`
|
||||||
|
SET
|
||||||
|
status = 'refunded',
|
||||||
|
refund_time = NOW(),
|
||||||
|
version = version + 1,
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE
|
||||||
|
id = @order_id
|
||||||
|
AND del_state = 0
|
||||||
|
AND status = 'paid';
|
||||||
|
|
||||||
|
-- 4) 代理佣金:按本次退款 99.5 元在该订单的佣金上比例冲减(refunded_amount 增加,满额则 status=2)
|
||||||
|
SET
|
||||||
|
@total_available = (
|
||||||
|
SELECT COALESCE(
|
||||||
|
SUM(amount - refunded_amount), 0
|
||||||
|
)
|
||||||
|
FROM agent_commission
|
||||||
|
WHERE
|
||||||
|
order_id = @order_id
|
||||||
|
AND del_state = 0
|
||||||
|
);
|
||||||
|
|
||||||
|
UPDATE agent_commission
|
||||||
|
SET
|
||||||
|
refunded_amount = LEAST(
|
||||||
|
amount,
|
||||||
|
refunded_amount + @repair_refund_amount * (amount - refunded_amount) / NULLIF(@total_available, 0)
|
||||||
|
),
|
||||||
|
status = CASE
|
||||||
|
WHEN LEAST(
|
||||||
|
amount,
|
||||||
|
refunded_amount + @repair_refund_amount * (amount - refunded_amount) / NULLIF(@total_available, 0)
|
||||||
|
) >= amount THEN 2
|
||||||
|
ELSE status
|
||||||
|
END,
|
||||||
|
version = version + 1,
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE
|
||||||
|
order_id = @order_id
|
||||||
|
AND del_state = 0
|
||||||
|
AND @total_available > 0;
|
||||||
|
|
||||||
|
-- 5) 代理钱包扣减 + 流水(仅对尚未存在本单退款流水的代理扣减,避免重复执行导致重复扣款)
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS _repair_wallet_snapshot;
|
||||||
|
|
||||||
|
CREATE TEMPORARY TABLE _repair_wallet_snapshot (
|
||||||
|
agent_id BIGINT NOT NULL,
|
||||||
|
balance_before DECIMAL(20, 4) NOT NULL,
|
||||||
|
frozen_before DECIMAL(20, 4) NOT NULL,
|
||||||
|
total_deduct DECIMAL(20, 4) NOT NULL,
|
||||||
|
PRIMARY KEY (agent_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 按 99.5 元在该订单各代理间按“可退佣金”比例分配扣减额(与 step 4 一致),仅扣减额>0 的代理
|
||||||
|
INSERT INTO
|
||||||
|
_repair_wallet_snapshot (
|
||||||
|
agent_id,
|
||||||
|
balance_before,
|
||||||
|
frozen_before,
|
||||||
|
total_deduct
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
agent_id,
|
||||||
|
balance_before,
|
||||||
|
frozen_before,
|
||||||
|
total_deduct
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
c.agent_id, w.balance AS balance_before, w.frozen_balance AS frozen_before, @repair_refund_amount * COALESCE(
|
||||||
|
SUM(c.amount - c.refunded_amount), 0
|
||||||
|
) / NULLIF(@total_available, 0) AS total_deduct
|
||||||
|
FROM
|
||||||
|
agent_commission c
|
||||||
|
JOIN agent_wallet w ON w.agent_id = c.agent_id
|
||||||
|
AND w.del_state = 0
|
||||||
|
WHERE
|
||||||
|
c.order_id = @order_id
|
||||||
|
AND c.del_state = 0
|
||||||
|
AND @total_available > 0
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM agent_wallet_transaction t
|
||||||
|
WHERE
|
||||||
|
t.agent_id = c.agent_id
|
||||||
|
AND t.transaction_id = @order_no
|
||||||
|
AND t.transaction_type = 'refund'
|
||||||
|
AND t.del_state = 0
|
||||||
|
)
|
||||||
|
GROUP BY
|
||||||
|
c.agent_id, w.balance, w.frozen_balance
|
||||||
|
) _agent_deduct
|
||||||
|
WHERE
|
||||||
|
total_deduct > 0;
|
||||||
|
|
||||||
|
-- 从钱包扣减:优先扣冻结余额,不足再扣可用余额
|
||||||
|
UPDATE agent_wallet w
|
||||||
|
INNER JOIN _repair_wallet_snapshot r ON w.agent_id = r.agent_id
|
||||||
|
SET
|
||||||
|
w.frozen_balance = w.frozen_balance - LEAST(
|
||||||
|
r.total_deduct,
|
||||||
|
w.frozen_balance
|
||||||
|
),
|
||||||
|
w.balance = w.balance - (
|
||||||
|
r.total_deduct - LEAST(
|
||||||
|
r.total_deduct,
|
||||||
|
w.frozen_balance
|
||||||
|
)
|
||||||
|
),
|
||||||
|
w.version = w.version + 1,
|
||||||
|
w.update_time = NOW()
|
||||||
|
WHERE
|
||||||
|
w.del_state = 0;
|
||||||
|
|
||||||
|
-- 插入退款流水(金额为负数)
|
||||||
|
INSERT INTO
|
||||||
|
agent_wallet_transaction (
|
||||||
|
delete_time,
|
||||||
|
del_state,
|
||||||
|
version,
|
||||||
|
agent_id,
|
||||||
|
transaction_type,
|
||||||
|
amount,
|
||||||
|
balance_before,
|
||||||
|
balance_after,
|
||||||
|
frozen_balance_before,
|
||||||
|
frozen_balance_after,
|
||||||
|
transaction_id,
|
||||||
|
related_user_id,
|
||||||
|
remark
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
r.agent_id,
|
||||||
|
'refund',
|
||||||
|
- r.total_deduct,
|
||||||
|
r.balance_before,
|
||||||
|
r.balance_before - (
|
||||||
|
r.total_deduct - LEAST(
|
||||||
|
r.total_deduct,
|
||||||
|
r.frozen_before
|
||||||
|
)
|
||||||
|
),
|
||||||
|
r.frozen_before,
|
||||||
|
r.frozen_before - LEAST(
|
||||||
|
r.total_deduct,
|
||||||
|
r.frozen_before
|
||||||
|
),
|
||||||
|
@order_no,
|
||||||
|
NULL,
|
||||||
|
'订单退款修复(支付宝已退,库内补单)'
|
||||||
|
FROM _repair_wallet_snapshot r;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS _repair_wallet_snapshot;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
-- ---------- 事务结束 ----------
|
||||||
|
|
||||||
|
-- 6) 校验(可选)
|
||||||
|
-- 订单应为 refunded
|
||||||
|
-- SELECT id, order_no, status, refund_time FROM `order` WHERE id = @order_id;
|
||||||
|
-- 应有 success 退款记录,refund_amount = 99.5
|
||||||
|
-- SELECT * FROM order_refund WHERE order_id = @order_id AND del_state = 0;
|
||||||
|
-- 佣金 refunded_amount 按 99.5 比例增加,满额则 status=2
|
||||||
|
-- SELECT id, agent_id, order_id, amount, refunded_amount, status FROM agent_commission WHERE order_id = @order_id;
|
||||||
2
deploy/sql/user_add_disable.sql
Normal file
2
deploy/sql/user_add_disable.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- 为用户表添加 disable 字段:0 可用,1 禁用,默认 0
|
||||||
|
ALTER TABLE `user` ADD COLUMN `disable` tinyint NOT NULL DEFAULT 0 COMMENT '0可用 1禁用' AFTER `inside`;
|
||||||
228
docs/toolbox_implementation.md
Normal file
228
docs/toolbox_implementation.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# 天远查车询功能实现说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
实现了一个完整的工具查询系统,包含11个实用工具,用户可以通过首页点击工具进行查询。
|
||||||
|
|
||||||
|
## 后端实现
|
||||||
|
|
||||||
|
### 1. 配置和类型定义
|
||||||
|
|
||||||
|
#### 配置文件更新
|
||||||
|
- `config/config.go`: 添加了 `TianxingjuheConfig` 结构体和 `Tianxingjuhe` 字段
|
||||||
|
- `etc/main.yaml` 和 `etc/main.dev.yaml`: 添加了天行聚合API配置
|
||||||
|
|
||||||
|
#### 类型定义
|
||||||
|
- `types/types.go`: 添加了工具相关类型
|
||||||
|
- `ToolInfo`: 工具信息结构
|
||||||
|
- `ToolboxListResp`: 工具列表响应
|
||||||
|
- `ToolboxQueryReq`: 工具查询请求
|
||||||
|
- `ToolboxQueryResp`: 工具查询响应
|
||||||
|
|
||||||
|
### 2. 天行聚合API客户端
|
||||||
|
|
||||||
|
#### 文件结构
|
||||||
|
- `service/tianxingjuhe_sdk/client.go`: 天行聚合API客户端实现
|
||||||
|
|
||||||
|
#### 功能特性
|
||||||
|
- 支持GET和POST请求
|
||||||
|
- 自动添加API密钥
|
||||||
|
- 统一的响应处理
|
||||||
|
- 错误处理机制
|
||||||
|
|
||||||
|
### 3. 工具箱服务
|
||||||
|
|
||||||
|
#### 文件结构
|
||||||
|
- `service/toolboxService.go`: 工具箱服务核心实现
|
||||||
|
|
||||||
|
#### 工具列表(11个工具)
|
||||||
|
1. **IP地址查询** (ip-location)
|
||||||
|
- 查询IP地址归属地、运营商等信息
|
||||||
|
- 支持ipv4和ipv6
|
||||||
|
- 内网IP自动识别
|
||||||
|
|
||||||
|
2. **身份证归属地** (idcard-info)
|
||||||
|
- 查询身份证归属地、性别、出生日期等信息
|
||||||
|
- 校验身份证有效性
|
||||||
|
- 计算年龄
|
||||||
|
|
||||||
|
3. **手机号归属地** (phone-location)
|
||||||
|
- 查询手机号码归属地、运营商等信息
|
||||||
|
|
||||||
|
4. **北京时间查询** (beijing-time)
|
||||||
|
- 获取当前北京时间
|
||||||
|
- 不需要输入参数
|
||||||
|
|
||||||
|
5. **银行卡识别** (bank-card)
|
||||||
|
- 识别银行卡发卡行与卡种
|
||||||
|
|
||||||
|
6. **车牌号解析** (plate-parse)
|
||||||
|
- 解析车牌类型与归属地
|
||||||
|
|
||||||
|
7. **金额大写转换** (money-to-chinese)
|
||||||
|
- 阿拉伯数字转中文大写金额
|
||||||
|
|
||||||
|
8. **密码强度检测** (password-strength)
|
||||||
|
- 检测密码安全性并给出建议
|
||||||
|
|
||||||
|
9. **日期间隔计算** (days-between-dates)
|
||||||
|
- 计算两个日期相差的天数
|
||||||
|
|
||||||
|
10. **文件大小格式化** (file-size-format)
|
||||||
|
- 字节数转为 KB / MB / GB
|
||||||
|
|
||||||
|
11. **文本字数统计** (text-stats)
|
||||||
|
- 统计字符数、单词数、行数
|
||||||
|
|
||||||
|
### 4. API接口
|
||||||
|
|
||||||
|
#### 路由配置
|
||||||
|
- `handler/routes.go`: 添加了工具箱相关路由
|
||||||
|
- `GET /api/v1/toolbox/list`: 获取工具列表
|
||||||
|
- `POST /api/v1/toolbox/query`: 执行工具查询
|
||||||
|
|
||||||
|
#### Handler实现
|
||||||
|
- `handler/toolbox/toolboxlisthandler.go`: 工具列表接口
|
||||||
|
- `handler/toolbox/toolboxqueryhandler.go`: 工具查询接口
|
||||||
|
|
||||||
|
#### Logic实现
|
||||||
|
- `logic/toolbox/toolboxlistlogic.go`: 工具列表业务逻辑
|
||||||
|
- `logic/toolbox/toolboxquerylogic.go`: 工具查询业务逻辑
|
||||||
|
|
||||||
|
### 5. 服务上下文注册
|
||||||
|
|
||||||
|
#### 文件更新
|
||||||
|
- `svc/servicecontext.go`:
|
||||||
|
- 导入天行聚合SDK包
|
||||||
|
- 添加 `TianxingjuheService` 字段
|
||||||
|
- 初始化天行聚合客户端实例
|
||||||
|
- 注册到服务上下文
|
||||||
|
|
||||||
|
## 前端实现
|
||||||
|
|
||||||
|
### 1. 工具配置
|
||||||
|
|
||||||
|
#### 文件结构
|
||||||
|
- `config/toolboxRegistry.js`: 工具配置注册表
|
||||||
|
|
||||||
|
#### 配置内容
|
||||||
|
每个工具配置包含:
|
||||||
|
- `key`: 工具唯一标识
|
||||||
|
- `name`: 工具名称
|
||||||
|
- `desc`: 工具描述
|
||||||
|
- `icon`: 图标类名(使用carbon图标库)
|
||||||
|
- `fields`: 输入字段配置
|
||||||
|
- `validate`: 表单验证函数
|
||||||
|
- `validateMsg`: 验证失败提示信息
|
||||||
|
- `resultLabels`: 结果展示字段映射
|
||||||
|
|
||||||
|
### 2. 首页集成
|
||||||
|
|
||||||
|
#### 文件更新
|
||||||
|
- `pages/index.vue`:
|
||||||
|
- 添加了"天远查车询"卡片
|
||||||
|
- 显示所有11个工具
|
||||||
|
- 点击工具跳转到查询页面
|
||||||
|
- 添加了自定义图标样式
|
||||||
|
|
||||||
|
### 3. 工具查询页面
|
||||||
|
|
||||||
|
#### 文件结构
|
||||||
|
- `pages/toolbox/query.vue`: 工具查询页面
|
||||||
|
|
||||||
|
#### 功能特性
|
||||||
|
- 动态表单生成(支持text、digit、date、textarea等类型)
|
||||||
|
- 表单验证
|
||||||
|
- 自动查询(无需输入参数的工具)
|
||||||
|
- 结果展示
|
||||||
|
- 错误处理
|
||||||
|
- 加载状态
|
||||||
|
|
||||||
|
#### 支持的字段类型
|
||||||
|
- `text`: 普通文本输入
|
||||||
|
- `digit`: 数字输入
|
||||||
|
- `idcard`: 身份证号输入
|
||||||
|
- `date`: 日期选择器
|
||||||
|
- `textarea`: 多行文本输入
|
||||||
|
|
||||||
|
### 4. API调用
|
||||||
|
|
||||||
|
#### 文件结构
|
||||||
|
- `api/toolbox.js`: 工具箱API封装
|
||||||
|
|
||||||
|
#### 接口函数
|
||||||
|
- `postToolboxQuery(toolKey, params)`: 执行工具查询
|
||||||
|
|
||||||
|
## 使用流程
|
||||||
|
|
||||||
|
### 用户使用流程
|
||||||
|
1. 打开首页
|
||||||
|
2. 看到"天远查车询"卡片
|
||||||
|
3. 点击任意工具图标
|
||||||
|
4. 跳转到工具查询页面
|
||||||
|
5. 填写表单(如需)
|
||||||
|
6. 点击"立即查询"按钮
|
||||||
|
7. 查看查询结果
|
||||||
|
|
||||||
|
### 数据流程
|
||||||
|
1. 前端调用 `/api/v1/toolbox/list` 获取工具列表
|
||||||
|
2. 用户选择工具后调用 `/api/v1/toolbox/query` 执行查询
|
||||||
|
3. 后端根据 toolKey 分发到对应的处理函数
|
||||||
|
4. 处理函数执行查询逻辑(可能调用第三方API)
|
||||||
|
5. 返回查询结果
|
||||||
|
6. 前端展示结果
|
||||||
|
|
||||||
|
## 技术特点
|
||||||
|
|
||||||
|
### 后端特点
|
||||||
|
- 统一的工具接口设计
|
||||||
|
- 工具处理器注册表模式
|
||||||
|
- 灵活的参数传递(map[string]interface{})
|
||||||
|
- 完善的错误处理
|
||||||
|
- 支持第三方API集成(天行聚合)
|
||||||
|
|
||||||
|
### 前端特点
|
||||||
|
- 动态表单生成
|
||||||
|
- 类型安全的TypeScript
|
||||||
|
- 响应式UI设计
|
||||||
|
- 统一的错误处理
|
||||||
|
- 良好的用户体验
|
||||||
|
|
||||||
|
## 扩展指南
|
||||||
|
|
||||||
|
### 添加新工具
|
||||||
|
|
||||||
|
#### 后端步骤
|
||||||
|
1. 在 `toolboxService.go` 中添加处理函数
|
||||||
|
2. 在 `toolProcessors` 映射中注册新工具
|
||||||
|
3. 在 `ListTools()` 方法中添加工具信息
|
||||||
|
|
||||||
|
#### 前端步骤
|
||||||
|
1. 在 `toolboxRegistry.js` 中添加工具配置
|
||||||
|
2. 定义字段配置和验证规则
|
||||||
|
3. 定义结果展示字段映射
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **API密钥管理**: 天行聚合API的密钥已配置,请在生产环境中使用真实的API密钥
|
||||||
|
2. **错误处理**: 所有工具都有完善的错误处理机制
|
||||||
|
3. **表单验证**: 前端和后端都有验证,确保数据安全
|
||||||
|
4. **性能优化**: 工具查询响应时间已优化,建议在3秒内完成
|
||||||
|
5. **用户体验**: 无需输入参数的工具会自动查询,提升用户体验
|
||||||
|
|
||||||
|
## 测试建议
|
||||||
|
|
||||||
|
1. 测试每个工具的输入验证
|
||||||
|
2. 测试错误处理(如格式错误的输入)
|
||||||
|
3. 测试网络异常情况
|
||||||
|
4. 测试边界情况(如最大长度、特殊字符等)
|
||||||
|
5. 测试响应速度
|
||||||
|
|
||||||
|
## 部署检查清单
|
||||||
|
|
||||||
|
- [ ] 配置文件中的API密钥已更新为生产环境密钥
|
||||||
|
- [ ] 天行聚合API客户端已正确初始化
|
||||||
|
- [ ] 所有工具路由已正确注册
|
||||||
|
- [ ] 前端工具配置已同步更新
|
||||||
|
- [ ] 表单验证规则已测试通过
|
||||||
|
- [ ] 错误处理已测试
|
||||||
|
- [ ] 性能测试已通过
|
||||||
9
go.mod
9
go.mod
@@ -6,9 +6,9 @@ toolchain go1.23.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.4
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13
|
||||||
github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6
|
github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6
|
||||||
github.com/alibabacloud-go/tea v1.2.2
|
github.com/alibabacloud-go/tea v1.3.13
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
|
||||||
github.com/bytedance/sonic v1.13.0
|
github.com/bytedance/sonic v1.13.0
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
@@ -37,16 +37,17 @@ require (
|
|||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
||||||
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3 // indirect
|
||||||
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
||||||
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
||||||
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
|
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||||
github.com/aliyun/credentials-go v1.3.10 // indirect
|
github.com/aliyun/credentials-go v1.4.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.2 // indirect
|
github.com/bytedance/sonic/loader v0.2.2 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
|||||||
33
go.sum
33
go.sum
@@ -13,6 +13,8 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2
|
|||||||
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=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
||||||
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3 h1:0Aobw12m3x28aeDMPjwjXsfF8MuLvRjlQ4Hhoy5hFOY=
|
||||||
|
github.com/alibabacloud-go/captcha-20230305 v1.1.3/go.mod h1:ydzBIN2OiM7eeQPpAFyBrv1H5TY1MtUP2rQig44C4UQ=
|
||||||
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
||||||
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
||||||
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
||||||
@@ -22,6 +24,8 @@ github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+M
|
|||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
||||||
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw=
|
||||||
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
|
||||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
||||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
|
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
|
||||||
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
|
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
|
||||||
@@ -46,6 +50,8 @@ github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy
|
|||||||
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
|
||||||
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
|
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
|
||||||
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
||||||
|
github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94=
|
||||||
|
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
|
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
|
||||||
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
||||||
@@ -64,6 +70,8 @@ github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTs
|
|||||||
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
|
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
|
||||||
github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA=
|
github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA=
|
||||||
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||||
|
github.com/aliyun/credentials-go v1.4.5 h1:O76WYKgdy1oQYYiJkERjlA2dxGuvLRrzuO2ScrtGWSk=
|
||||||
|
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
@@ -83,6 +91,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
|
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||||
|
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
@@ -326,10 +336,13 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -342,6 +355,9 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -356,10 +372,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -370,6 +389,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.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.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=
|
||||||
@@ -384,20 +406,27 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -406,6 +435,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.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.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
@@ -420,6 +451,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
82
pkg/captcha/aliyun.go
Normal file
82
pkg/captcha/aliyun.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"tyc-server/common/xerr"
|
||||||
|
|
||||||
|
captcha20230305 "github.com/alibabacloud-go/captcha-20230305/client"
|
||||||
|
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config 阿里云验证码配置(与 api internal config 解耦,供 pkg 使用)
|
||||||
|
type Config struct {
|
||||||
|
AccessKeyID string
|
||||||
|
AccessKeySecret string
|
||||||
|
EndpointURL string
|
||||||
|
SceneID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWeChatUserAgent 判断 User-Agent 是否为微信内置浏览器(含 MicroMessenger)。
|
||||||
|
func isWeChatUserAgent(ua string) bool {
|
||||||
|
return strings.Contains(ua, "MicroMessenger")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWithUserAgent 根据 User-Agent 与 captchaVerifyParam 校验。微信请求直接通过。
|
||||||
|
func VerifyWithUserAgent(cfg Config, captchaVerifyParam string, userAgent string) error {
|
||||||
|
if isWeChatUserAgent(userAgent) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Verify(cfg, captchaVerifyParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify 校验前端传入的 captchaVerifyParam。异常时视为通过以保证业务可用。
|
||||||
|
func Verify(cfg Config, captchaVerifyParam string) error {
|
||||||
|
if os.Getenv("ENV") == "development" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if captchaVerifyParam == "" {
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "empty captchaVerifyParam")
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCfg := &openapi.Config{
|
||||||
|
AccessKeyId: tea.String(cfg.AccessKeyID),
|
||||||
|
AccessKeySecret: tea.String(cfg.AccessKeySecret),
|
||||||
|
}
|
||||||
|
clientCfg.Endpoint = tea.String(cfg.EndpointURL)
|
||||||
|
clientCfg.ConnectTimeout = tea.Int(5000)
|
||||||
|
clientCfg.ReadTimeout = tea.Int(5000)
|
||||||
|
|
||||||
|
client, err := captcha20230305.NewClient(clientCfg)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("init aliyun captcha client error: %+v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &captcha20230305.VerifyIntelligentCaptchaRequest{
|
||||||
|
SceneId: tea.String(cfg.SceneID),
|
||||||
|
CaptchaVerifyParam: tea.String(captchaVerifyParam),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.VerifyIntelligentCaptcha(req)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("verify aliyun captcha error: %+v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if resp.Body == nil || resp.Body.Result == nil {
|
||||||
|
logx.Errorf("verify aliyun captcha empty result, resp: %+v", resp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if tea.BoolValue(resp.Body.Result.VerifyResult) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyCode := tea.StringValue(resp.Body.Result.VerifyCode)
|
||||||
|
logx.Errorf("verify aliyun captcha failed, code: %s", verifyCode)
|
||||||
|
return errors.Wrapf(xerr.NewErrMsg("图形验证码校验失败"), "aliyun captcha verify fail, code: %s", verifyCode)
|
||||||
|
}
|
||||||
32
pkg/captcha/encrypt_scene.go
Normal file
32
pkg/captcha/encrypt_scene.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
lzcrypto "tyc-server/pkg/lzkit/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateEncryptedSceneID 按阿里云文档生成 EncryptedSceneId(仅适用于 V3 架构加密模式)。
|
||||||
|
// 明文格式: sceneId×tamp&expireTime
|
||||||
|
// 加密: AES-256-CBC + PKCS7Padding,结果为 Base64( IV(16字节) + ciphertext )
|
||||||
|
func GenerateEncryptedSceneID(sceneId, ekey string, expireSeconds int) (string, error) {
|
||||||
|
if expireSeconds <= 0 || expireSeconds > 86400 {
|
||||||
|
expireSeconds = 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := time.Now().Unix() // 秒级时间戳
|
||||||
|
plaintext := fmt.Sprintf("%s&%d&%d", sceneId, ts, expireSeconds)
|
||||||
|
|
||||||
|
keyBytes, err := base64.StdEncoding.DecodeString(ekey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("decode ekey error: %w", err)
|
||||||
|
}
|
||||||
|
if len(keyBytes) != 32 {
|
||||||
|
return "", fmt.Errorf("invalid ekey length, need 32 bytes after base64 decode, got %d", len(keyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复用已有的 AES-CBC + PKCS7 实现,输出即为 Base64(IV + ciphertext)
|
||||||
|
return lzcrypto.AesEncrypt([]byte(plaintext), keyBytes)
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
func TestAesEcbMobileEncryption(t *testing.T) {
|
func TestAesEcbMobileEncryption(t *testing.T) {
|
||||||
// 测试手机号加密
|
// 测试手机号加密
|
||||||
mobile := "18653052547"
|
mobile := "15029295957"
|
||||||
key := []byte("ff83609b2b24fc73196aac3d3dfb874f") // 16字节AES-128密钥
|
key := []byte("ff83609b2b24fc73196aac3d3dfb874f") // 16字节AES-128密钥
|
||||||
|
|
||||||
keyStr := hex.EncodeToString(key)
|
keyStr := hex.EncodeToString(key)
|
||||||
@@ -19,7 +19,8 @@ func TestAesEcbMobileEncryption(t *testing.T) {
|
|||||||
t.Fatalf("手机号加密失败: %v", err)
|
t.Fatalf("手机号加密失败: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("encrypted: %s\n", encrypted)
|
fmt.Printf("encrypted: %s\n", encrypted)
|
||||||
jmStr := "m9EEeW9ZBBJmi1hx1k1uIQ=="
|
|
||||||
|
jmStr := "Am8/KpmBnsbXZoZOZq/oVQ=="
|
||||||
// 测试解密
|
// 测试解密
|
||||||
decrypted, err := DecryptMobile(jmStr, keyStr)
|
decrypted, err := DecryptMobile(jmStr, keyStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user