feat(camel_oil): 支持Token管理与卡密绑定功能
- 新增CamelOilToken和CamelOilCardBinding数据库表,实现Token及卡密绑定记录管理 - 在service层增加Token的创建、查询、更新、删除及分页功能 - 实现卡密与Token绑定的业务逻辑,支持基于Token的卡密管理 - 在API层新增Token和卡密绑定相关接口:创建Token、获取Token详情、删除Token、列出Token及根据Token查询绑定卡密 - camel_oil_api新增绑卡接口,支持绑卡状态分类及错误处理 - 在定时任务中增加卡密绑定任务,实现自动处理已支付订单的卡密绑定 - 优化订单提交及支付流程,包含日志调整和请求参数随机扰动 - 统一调整camel_oil模块多控制器实现,完成账号状态查询及订单相关接口实现 - 注册更多camel_oil定时任务,包括订单支付检查、账号日重置和待回调订单处理任务
This commit is contained in:
@@ -21,4 +21,9 @@ type ICamelOilV1 interface {
|
||||
OrderHistory(ctx context.Context, req *v1.OrderHistoryReq) (res *v1.OrderHistoryRes, err error)
|
||||
AccountOrderList(ctx context.Context, req *v1.AccountOrderListReq) (res *v1.AccountOrderListRes, err error)
|
||||
OrderCallback(ctx context.Context, req *v1.OrderCallbackReq) (res *v1.OrderCallbackRes, err error)
|
||||
CreateToken(ctx context.Context, req *v1.CreateTokenReq) (res *v1.CreateTokenRes, err error)
|
||||
GetToken(ctx context.Context, req *v1.GetTokenReq) (res *v1.GetTokenRes, err error)
|
||||
ListTokens(ctx context.Context, req *v1.ListTokensReq) (res *v1.ListTokensRes, err error)
|
||||
DeleteToken(ctx context.Context, req *v1.DeleteTokenReq) (res *v1.DeleteTokenRes, err error)
|
||||
ListCardBindingsByToken(ctx context.Context, req *v1.ListCardBindingsByTokenReq) (res *v1.ListCardBindingsByTokenRes, err error)
|
||||
}
|
||||
|
||||
106
api/camel_oil/v1/token.go
Normal file
106
api/camel_oil/v1/token.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"kami/api/commonApi"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// ====================================================================================
|
||||
// Token 管理 API 请求/响应结构
|
||||
// ====================================================================================
|
||||
|
||||
// CreateTokenReq 创建 Token 请求
|
||||
type CreateTokenReq struct {
|
||||
g.Meta `path:"/token/create" tags:"JD V2 Token Management" method:"post" summary:"创建 Token"`
|
||||
TokenName string `json:"tokenName" v:"required" description:"Token名称"`
|
||||
TokenValue string `json:"tokenValue" v:"required" description:"Token值"`
|
||||
Phone string `json:"phone" description:"绑定的手机号"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
}
|
||||
|
||||
// CreateTokenRes 创建 Token 响应
|
||||
type CreateTokenRes struct {
|
||||
TokenId int64 `json:"tokenId" description:"Token ID"`
|
||||
}
|
||||
|
||||
// GetTokenReq 获取 Token 信息请求
|
||||
type GetTokenReq struct {
|
||||
g.Meta `path:"/token/get" tags:"JD V2 Token Management" method:"get" summary:"获取 Token 信息"`
|
||||
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
|
||||
}
|
||||
|
||||
// TokenInfo Token 信息
|
||||
type TokenInfo struct {
|
||||
Id int64 `json:"id" description:"Token ID"`
|
||||
TokenName string `json:"tokenName" description:"Token名称"`
|
||||
TokenValue string `json:"tokenValue" description:"Token值"`
|
||||
Phone string `json:"phone" description:"绑定的手机号"`
|
||||
Status int `json:"status" description:"状态"`
|
||||
BindCount int `json:"bindCount" description:"已绑定卡密数量"`
|
||||
TotalBindAmount decimal.Decimal `json:"totalBindAmount" description:"累计绑定金额"`
|
||||
LastBindAt *gtime.Time `json:"lastBindAt" description:"最后绑定时间"`
|
||||
LastUsedAt *gtime.Time `json:"lastUsedAt" description:"最后使用时间"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"`
|
||||
}
|
||||
|
||||
// GetTokenRes 获取 Token 信息响应
|
||||
type GetTokenRes struct {
|
||||
Token TokenInfo `json:"token" description:"Token信息"`
|
||||
}
|
||||
|
||||
// ListTokensRes 列出 Token 响应
|
||||
type ListTokensRes struct {
|
||||
commonApi.CommonPageRes[TokenInfo]
|
||||
}
|
||||
|
||||
// ListTokensReq 列出 Token 请求
|
||||
type ListTokensReq struct {
|
||||
g.Meta `path:"/token/list" tags:"JD V2 Token Management" method:"get" summary:"列出 Token"`
|
||||
commonApi.CommonPageReq
|
||||
TokenName string `json:"tokenName" description:"Token名称"`
|
||||
Status int `json:"status" description:"状态"`
|
||||
}
|
||||
|
||||
// DeleteTokenReq 删除 Token 请求
|
||||
type DeleteTokenReq struct {
|
||||
g.Meta `path:"/token/delete" tags:"JD V2 Token Management" method:"post" summary:"删除 Token"`
|
||||
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
|
||||
}
|
||||
|
||||
// DeleteTokenRes 删除 Token 响应
|
||||
type DeleteTokenRes struct {
|
||||
Message string `json:"message" description:"信息"`
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// 卡密绑定 API 请求/响应结构
|
||||
// ====================================================================================
|
||||
|
||||
// CardBindingInfo 卡密绑定信息
|
||||
type CardBindingInfo struct {
|
||||
Id int64 `json:"id" description:"绑定记录ID"`
|
||||
TokenId int64 `json:"tokenId" description:"Token ID"`
|
||||
TokenName string `json:"tokenName" description:"Token名称"`
|
||||
OrderId int64 `json:"orderId" description:"订单ID"`
|
||||
CardNumber string `json:"cardNumber" description:"卡号"`
|
||||
CardPassword string `json:"cardPassword" description:"卡密"`
|
||||
Amount decimal.Decimal `json:"amount" description:"绑定金额"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
}
|
||||
|
||||
// ListCardBindingsByTokenReq 根据 tokenId 查询绑卡记录请求
|
||||
type ListCardBindingsByTokenReq struct {
|
||||
g.Meta `path:"/card-binding/list-by-token" tags:"JD V2 Token Management" method:"get" summary:"查询 Token 绑卡记录"`
|
||||
commonApi.CommonPageReq
|
||||
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
|
||||
}
|
||||
|
||||
// ListCardBindingsByTokenRes 根据 tokenId 查询绑卡记录响应
|
||||
type ListCardBindingsByTokenRes struct {
|
||||
commonApi.CommonPageRes[CardBindingInfo]
|
||||
}
|
||||
@@ -7,7 +7,7 @@ package card_info_apple
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/card_info_apple/v1"
|
||||
"kami/api/card_info_apple/v1"
|
||||
)
|
||||
|
||||
type ICardInfoAppleV1 interface {
|
||||
|
||||
@@ -76,6 +76,22 @@ var CamelOilCallbackStatusText = map[CamelOilNotifyStatus]string{
|
||||
CamelOilCallbackStatusFailed: "回调失败",
|
||||
}
|
||||
|
||||
// CamelOilTokenStatus Token管理状态枚举
|
||||
type CamelOilTokenStatus int
|
||||
|
||||
const (
|
||||
CamelOilTokenStatusAvailable CamelOilTokenStatus = 1 // 可用
|
||||
CamelOilTokenStatusDisabled CamelOilTokenStatus = 2 // 已禁用
|
||||
CamelOilTokenStatusExpired CamelOilTokenStatus = 3 // 已过期
|
||||
)
|
||||
|
||||
// CamelOilTokenStatusText Token状态文本映射
|
||||
var CamelOilTokenStatusText = map[CamelOilTokenStatus]string{
|
||||
CamelOilTokenStatusAvailable: "可用",
|
||||
CamelOilTokenStatusDisabled: "已禁用",
|
||||
CamelOilTokenStatusExpired: "已过期",
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// 变更类型常量定义
|
||||
// ====================================================================================
|
||||
|
||||
@@ -3,12 +3,10 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) AccountHistory(ctx context.Context, req *v1.AccountHistoryReq) (res *v1.AccountHistoryRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
return service.CamelOil().GetAccountHistory(ctx, req)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) AccountOrderList(ctx context.Context, req *v1.AccountOrderListReq) (res *v1.AccountOrderListRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
return service.CamelOil().GetAccountOrders(ctx, req)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) AccountStatistics(ctx context.Context, req *v1.AccountStatisticsReq) (res *v1.AccountStatisticsRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
return service.CamelOil().GetAccountStatistics(ctx, req)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,41 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/consts"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) CheckAccount(ctx context.Context, req *v1.CheckAccountReq) (res *v1.CheckAccountRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
account, err := service.CamelOil().GetAccountInfo(ctx, req.AccountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &v1.CheckAccountRes{
|
||||
IsOnline: account.Status == int(consts.CamelOilAccountStatusOnline),
|
||||
Status: consts.CamelOilAccountStatus(account.Status),
|
||||
StatusText: getAccountStatusTextForStatus(account.Status),
|
||||
FailureReason: account.FailureReason,
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// getAccountStatusTextForStatus 获取账号状态文本
|
||||
func getAccountStatusTextForStatus(status int) string {
|
||||
switch status {
|
||||
case 1:
|
||||
return "待登录"
|
||||
case 2:
|
||||
return "在线"
|
||||
case 3:
|
||||
return "暂停"
|
||||
case 4:
|
||||
return "已失效"
|
||||
case 5:
|
||||
return "登录失败"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
19
internal/controller/camel_oil/camel_oil_v1_create_token.go
Normal file
19
internal/controller/camel_oil/camel_oil_v1_create_token.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) CreateToken(ctx context.Context, req *v1.CreateTokenReq) (res *v1.CreateTokenRes, err error) {
|
||||
tokenId, err := service.CamelOil().CreateToken(ctx, req.TokenName, req.TokenValue, req.Phone, req.Remark)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &v1.CreateTokenRes{
|
||||
TokenId: tokenId,
|
||||
}, nil
|
||||
}
|
||||
18
internal/controller/camel_oil/camel_oil_v1_delete_token.go
Normal file
18
internal/controller/camel_oil/camel_oil_v1_delete_token.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) DeleteToken(ctx context.Context, req *v1.DeleteTokenReq) (res *v1.DeleteTokenRes, err error) {
|
||||
err = service.CamelOil().DeleteToken(ctx, req.TokenId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.DeleteTokenRes{
|
||||
Message: "Token deleted successfully",
|
||||
}, nil
|
||||
}
|
||||
31
internal/controller/camel_oil/camel_oil_v1_get_token.go
Normal file
31
internal/controller/camel_oil/camel_oil_v1_get_token.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetToken(ctx context.Context, req *v1.GetTokenReq) (res *v1.GetTokenRes, err error) {
|
||||
token, err := service.CamelOil().GetTokenInfo(ctx, req.TokenId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.GetTokenRes{
|
||||
Token: v1.TokenInfo{
|
||||
Id: token.Id,
|
||||
TokenName: token.TokenName,
|
||||
TokenValue: token.TokenValue,
|
||||
Phone: token.Phone,
|
||||
Status: token.Status,
|
||||
BindCount: token.BindCount,
|
||||
TotalBindAmount: token.TotalBindAmount,
|
||||
LastBindAt: token.LastBindAt,
|
||||
LastUsedAt: token.LastUsedAt,
|
||||
Remark: token.Remark,
|
||||
CreatedAt: token.CreatedAt,
|
||||
UpdatedAt: token.UpdatedAt,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/api/commonApi"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) ListCardBindingsByToken(ctx context.Context, req *v1.ListCardBindingsByTokenReq) (res *v1.ListCardBindingsByTokenRes, err error) {
|
||||
bindings, total, err := service.CamelOil().GetCardBindingsByToken(ctx, req.TokenId, req.Current, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindingInfos := make([]v1.CardBindingInfo, 0, len(bindings))
|
||||
for _, binding := range bindings {
|
||||
bindingInfos = append(bindingInfos, v1.CardBindingInfo{
|
||||
Id: binding.Id,
|
||||
TokenId: binding.TokenId,
|
||||
OrderId: binding.OrderId,
|
||||
CardNumber: binding.CardNumber,
|
||||
CardPassword: binding.CardPassword,
|
||||
Amount: binding.Amount,
|
||||
CreatedAt: binding.CreatedAt,
|
||||
})
|
||||
}
|
||||
return &v1.ListCardBindingsByTokenRes{
|
||||
CommonPageRes: commonApi.CommonPageRes[v1.CardBindingInfo]{
|
||||
Total: total,
|
||||
CommonDataRes: commonApi.CommonDataRes[v1.CardBindingInfo]{
|
||||
List: bindingInfos,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
|
||||
41
internal/controller/camel_oil/camel_oil_v1_list_tokens.go
Normal file
41
internal/controller/camel_oil/camel_oil_v1_list_tokens.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/api/commonApi"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) ListTokens(ctx context.Context, req *v1.ListTokensReq) (res *v1.ListTokensRes, err error) {
|
||||
tokens, total, err := service.CamelOil().ListTokensWithPagination(ctx, req.CommonPageReq, req.TokenName, req.Status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenInfos := make([]v1.TokenInfo, 0, len(tokens))
|
||||
for _, token := range tokens {
|
||||
tokenInfos = append(tokenInfos, v1.TokenInfo{
|
||||
Id: token.Id,
|
||||
TokenName: token.TokenName,
|
||||
TokenValue: token.TokenValue,
|
||||
Phone: token.Phone,
|
||||
Status: token.Status,
|
||||
BindCount: token.BindCount,
|
||||
TotalBindAmount: token.TotalBindAmount,
|
||||
LastBindAt: token.LastBindAt,
|
||||
LastUsedAt: token.LastUsedAt,
|
||||
Remark: token.Remark,
|
||||
CreatedAt: token.CreatedAt,
|
||||
UpdatedAt: token.UpdatedAt,
|
||||
})
|
||||
}
|
||||
return &v1.ListTokensRes{
|
||||
CommonPageRes: commonApi.CommonPageRes[v1.TokenInfo]{
|
||||
Total: total,
|
||||
CommonDataRes: commonApi.CommonDataRes[v1.TokenInfo]{
|
||||
List: tokenInfos,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -3,12 +3,10 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) OrderCallback(ctx context.Context, req *v1.OrderCallbackReq) (res *v1.OrderCallbackRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
return service.CamelOil().TriggerOrderCallback(ctx, req)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,12 +3,10 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) OrderHistory(ctx context.Context, req *v1.OrderHistoryReq) (res *v1.OrderHistoryRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
return service.CamelOil().GetOrderHistory(ctx, req)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package camel_oil
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kami/api/camel_oil/v1"
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
"kami/internal/service"
|
||||
)
|
||||
|
||||
|
||||
97
internal/dao/internal/v_1_camel_oil_card_binding.go
Normal file
97
internal/dao/internal/v_1_camel_oil_card_binding.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// V1CamelOilCardBindingDao is the data access object for the table camel_oil_card_binding.
|
||||
type V1CamelOilCardBindingDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of the current DAO.
|
||||
columns V1CamelOilCardBindingColumns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []gdb.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// V1CamelOilCardBindingColumns defines and stores column names for the table camel_oil_card_binding.
|
||||
type V1CamelOilCardBindingColumns struct {
|
||||
Id string // 主键ID
|
||||
TokenId string // Token ID
|
||||
OrderId string // 订单ID
|
||||
CardNumber string // 卡号
|
||||
CardPassword string // 卡密
|
||||
Amount string // 绑定金额
|
||||
Remark string // 备注
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
DeletedAt string // 删除时间(软删除)
|
||||
}
|
||||
|
||||
// v1CamelOilCardBindingColumns holds the columns for the table camel_oil_card_binding.
|
||||
var v1CamelOilCardBindingColumns = V1CamelOilCardBindingColumns{
|
||||
Id: "id",
|
||||
TokenId: "token_id",
|
||||
OrderId: "order_id",
|
||||
CardNumber: "card_number",
|
||||
CardPassword: "card_password",
|
||||
Amount: "amount",
|
||||
Remark: "remark",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
}
|
||||
|
||||
// NewV1CamelOilCardBindingDao creates and returns a new DAO object for table data access.
|
||||
func NewV1CamelOilCardBindingDao(handlers ...gdb.ModelHandler) *V1CamelOilCardBindingDao {
|
||||
return &V1CamelOilCardBindingDao{
|
||||
group: "default",
|
||||
table: "camel_oil_card_binding",
|
||||
columns: v1CamelOilCardBindingColumns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *V1CamelOilCardBindingDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *V1CamelOilCardBindingDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *V1CamelOilCardBindingDao) Columns() V1CamelOilCardBindingColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *V1CamelOilCardBindingDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
|
||||
func (dao *V1CamelOilCardBindingDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
model := dao.DB().Model(dao.table)
|
||||
for _, handler := range dao.handlers {
|
||||
model = handler(model)
|
||||
}
|
||||
return model.Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rolls back the transaction and returns the error if function f returns a non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note: Do not commit or roll back the transaction in function f,
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *V1CamelOilCardBindingDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
103
internal/dao/internal/v_1_camel_oil_token.go
Normal file
103
internal/dao/internal/v_1_camel_oil_token.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// V1CamelOilTokenDao is the data access object for the table camel_oil_token.
|
||||
type V1CamelOilTokenDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of the current DAO.
|
||||
columns V1CamelOilTokenColumns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []gdb.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// V1CamelOilTokenColumns defines and stores column names for the table camel_oil_token.
|
||||
type V1CamelOilTokenColumns struct {
|
||||
Id string // 主键ID
|
||||
TokenName string // Token名称/标识
|
||||
TokenValue string // Token具体值
|
||||
Phone string // 绑定的手机号
|
||||
Status string // 状态:1可用 2已禁用 3已过期
|
||||
BindCount string // 已绑定卡密数量
|
||||
TotalBindAmount string // 累计绑定金额
|
||||
LastBindAt string // 最后绑定时间
|
||||
LastUsedAt string // 最后使用时间
|
||||
Remark string // 备注
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
DeletedAt string // 删除时间(软删除)
|
||||
}
|
||||
|
||||
// v1CamelOilTokenColumns holds the columns for the table camel_oil_token.
|
||||
var v1CamelOilTokenColumns = V1CamelOilTokenColumns{
|
||||
Id: "id",
|
||||
TokenName: "token_name",
|
||||
TokenValue: "token_value",
|
||||
Phone: "phone",
|
||||
Status: "status",
|
||||
BindCount: "bind_count",
|
||||
TotalBindAmount: "total_bind_amount",
|
||||
LastBindAt: "last_bind_at",
|
||||
LastUsedAt: "last_used_at",
|
||||
Remark: "remark",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeletedAt: "deleted_at",
|
||||
}
|
||||
|
||||
// NewV1CamelOilTokenDao creates and returns a new DAO object for table data access.
|
||||
func NewV1CamelOilTokenDao(handlers ...gdb.ModelHandler) *V1CamelOilTokenDao {
|
||||
return &V1CamelOilTokenDao{
|
||||
group: "default",
|
||||
table: "camel_oil_token",
|
||||
columns: v1CamelOilTokenColumns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *V1CamelOilTokenDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *V1CamelOilTokenDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *V1CamelOilTokenDao) Columns() V1CamelOilTokenColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *V1CamelOilTokenDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
|
||||
func (dao *V1CamelOilTokenDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
model := dao.DB().Model(dao.table)
|
||||
for _, handler := range dao.handlers {
|
||||
model = handler(model)
|
||||
}
|
||||
return model.Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rolls back the transaction and returns the error if function f returns a non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note: Do not commit or roll back the transaction in function f,
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *V1CamelOilTokenDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
22
internal/dao/v_1_camel_oil_card_binding.go
Normal file
22
internal/dao/v_1_camel_oil_card_binding.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"kami/internal/dao/internal"
|
||||
)
|
||||
|
||||
// v1CamelOilCardBindingDao is the data access object for the table camel_oil_card_binding.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type v1CamelOilCardBindingDao struct {
|
||||
*internal.V1CamelOilCardBindingDao
|
||||
}
|
||||
|
||||
var (
|
||||
// V1CamelOilCardBinding is a globally accessible object for table camel_oil_card_binding operations.
|
||||
V1CamelOilCardBinding = v1CamelOilCardBindingDao{internal.NewV1CamelOilCardBindingDao()}
|
||||
)
|
||||
|
||||
// Add your custom methods and functionality below.
|
||||
22
internal/dao/v_1_camel_oil_token.go
Normal file
22
internal/dao/v_1_camel_oil_token.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"kami/internal/dao/internal"
|
||||
)
|
||||
|
||||
// v1CamelOilTokenDao is the data access object for the table camel_oil_token.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type v1CamelOilTokenDao struct {
|
||||
*internal.V1CamelOilTokenDao
|
||||
}
|
||||
|
||||
var (
|
||||
// V1CamelOilToken is a globally accessible object for table camel_oil_token operations.
|
||||
V1CamelOilToken = v1CamelOilTokenDao{internal.NewV1CamelOilTokenDao()}
|
||||
)
|
||||
|
||||
// Add your custom methods and functionality below.
|
||||
@@ -138,6 +138,7 @@ func (s *sCamelOil) CronOrderPaymentCheckTask(ctx context.Context) error {
|
||||
PayStatus: consts.CamelOilPaymentStatusPaid,
|
||||
})
|
||||
_ = s.RecordOrderHistory(ctx, order.OrderNo, consts.CamelOilOrderChangeTypePaid, "", fmt.Sprintf("支付成功,金额: %.2f", queryResult.Balance))
|
||||
|
||||
paidCount++
|
||||
}
|
||||
continue
|
||||
@@ -162,7 +163,7 @@ func (s *sCamelOil) CronOrderPaymentCheckTask(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CronAccountDailyResetTask 账号日重置任务 - 由cron调度器在每日00:05调用
|
||||
// CronAccountDailyResetTask 账号日重置任务 - 由cron调度器在每日00:00调用
|
||||
func (s *sCamelOil) CronAccountDailyResetTask(ctx context.Context) error {
|
||||
glog.Info(ctx, "开始执行账号日重置任务")
|
||||
|
||||
@@ -307,3 +308,68 @@ func (s *sCamelOil) CronVerifyCodeCheckTask(ctx context.Context) error {
|
||||
glog.Infof(ctx, "验证码检测任务完成: 成功=%d, 失败=%d", successCount, failCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CronCardBindingTask 卡密绑定定时任务 - 由cron调度器定期调用
|
||||
// 流程:处理已支付但未绑定 Token 的订单,进行卡密绑定
|
||||
func (s *sCamelOil) CronCardBindingTask(ctx context.Context) error {
|
||||
glog.Info(ctx, "开始执行卡密绑定任务")
|
||||
|
||||
// 查询已支付但未绑定 Token 的订单
|
||||
var orders []*entity.V1CamelOilOrder
|
||||
err := dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1CamelOilOrder.Columns().PayStatus, consts.CamelOilPaymentStatusPaid).
|
||||
WhereNotIn(dao.V1CamelOilOrder.Columns().Id,
|
||||
dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Fields(dao.V1CamelOilCardBinding.Columns().OrderId)).
|
||||
Limit(50).
|
||||
Scan(&orders)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(ctx, "查询待绑定订单失败", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(orders) == 0 {
|
||||
glog.Debug(ctx, "无待绑定订单")
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "查询到 %d 个待绑定订单", len(orders))
|
||||
|
||||
successCount := 0
|
||||
failCount := 0
|
||||
|
||||
for _, order := range orders {
|
||||
// 检查是否有可用的 Token
|
||||
availableTokenCount, err := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1CamelOilToken.Columns().Status, consts.CamelOilTokenStatusAvailable).
|
||||
Count()
|
||||
|
||||
if err != nil || availableTokenCount == 0 {
|
||||
glog.Warningf(ctx, "无可用 Token,订单 ID: %d 无法绑定", order.Id)
|
||||
failCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查卡号和卡密是否存在
|
||||
if order.CardNumber == "" || order.CardPassword == "" {
|
||||
glog.Warningf(ctx, "订单 %d 卡号或卡密未填写,无法绑定", order.Id)
|
||||
failCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试绑定卡密到 Token
|
||||
_, err = s.BindCardToToken(ctx, order.Id, order.CardNumber, order.CardPassword, order.Amount)
|
||||
if err != nil {
|
||||
glog.Errorf(ctx, "绑定卡密到 Token 失败,订单 ID: %d, 错误: %v", order.Id, err)
|
||||
failCount++
|
||||
continue
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "订单 %d 卡密绑定成功", order.Id)
|
||||
successCount++
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "卡密绑定任务完成: 成功=%d, 失败=%d", successCount, failCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ func (s *sCamelOil) TriggerOrderCallback(ctx context.Context, req *v1.OrderCallb
|
||||
|
||||
// executeCallback 执行回调(内部方法)
|
||||
func (s *sCamelOil) executeCallback(ctx context.Context, order *entity.V1CamelOilOrder) (err error) {
|
||||
// 查询订单信息
|
||||
var orderInfo *entity.V1OrderInfo
|
||||
if err = dao.V1OrderInfo.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1OrderInfo.Columns().BankOrderId, order.MerchantOrderId).Scan(&orderInfo); err != nil || orderInfo == nil || orderInfo.Id == 0 {
|
||||
glog.Error(ctx, "查询订单失败", g.Map{"userOrderId": order.MerchantOrderId, "err": err})
|
||||
@@ -113,7 +114,7 @@ func (s *sCamelOil) executeCallback(ctx context.Context, order *entity.V1CamelOi
|
||||
glog.Error(ctx, "查询商户信息失败", g.Map{"merchantId": orderInfo.MerchantUid, "err": err})
|
||||
return errors.New("商户不存在")
|
||||
}
|
||||
// 发送HTTP回调请求
|
||||
// 发送 HTTP 回调请求
|
||||
_, _ = gclient.New().Get(ctx, "http://kami_gateway:12309/myself/notify", g.Map{
|
||||
"orderNo": order.OrderNo,
|
||||
"orderPrice": order.Amount,
|
||||
@@ -129,12 +130,17 @@ func (s *sCamelOil) executeCallback(ctx context.Context, order *entity.V1CamelOi
|
||||
|
||||
// ProcessPendingCallbacks 处理待回调订单(定时任务使用)
|
||||
func (s *sCamelOil) ProcessPendingCallbacks(ctx context.Context) error {
|
||||
// 查询需要回调的订单
|
||||
// 查询需要回调的订单(仅查询已绑定到 Token 的订单)
|
||||
var orders []*entity.V1CamelOilOrder
|
||||
err := dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
InnerJoin(dao.V1CamelOilCardBinding.Table(), fmt.Sprintf("%s.%s = %s.%s",
|
||||
dao.V1CamelOilOrder.Table(), dao.V1CamelOilOrder.Columns().Id,
|
||||
dao.V1CamelOilCardBinding.Table(), dao.V1CamelOilCardBinding.Columns().OrderId)).
|
||||
Where(dao.V1CamelOilOrder.Columns().PayStatus, consts.CamelOilPaymentStatusPaid).
|
||||
Where(dao.V1CamelOilOrder.Columns().NotifyStatus, consts.CamelOilCallbackStatusPending).
|
||||
WhereLTE(dao.V1CamelOilOrder.Columns().NotifyCount, 3).
|
||||
Fields(dao.V1CamelOilOrder.Table() + ".*").
|
||||
Distinct().
|
||||
Limit(50).
|
||||
Scan(&orders)
|
||||
|
||||
|
||||
359
internal/logic/camel_oil/token.go
Normal file
359
internal/logic/camel_oil/token.go
Normal file
@@ -0,0 +1,359 @@
|
||||
package camel_oil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"kami/api/commonApi"
|
||||
"kami/internal/consts"
|
||||
"kami/internal/dao"
|
||||
"kami/internal/model/do"
|
||||
"kami/internal/model/entity"
|
||||
"kami/utility/config"
|
||||
"kami/utility/integration/camel_oil_api"
|
||||
)
|
||||
|
||||
// ====================================================================================
|
||||
// Token 管理相关方法
|
||||
// ====================================================================================
|
||||
|
||||
// CreateToken 创建 Token
|
||||
func (s *sCamelOil) CreateToken(ctx context.Context, tokenName string, tokenValue string, phone string, remark string) (tokenId int64, err error) {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
result, err := m.Insert(&do.V1CamelOilToken{
|
||||
TokenName: tokenName,
|
||||
TokenValue: tokenValue,
|
||||
Phone: phone,
|
||||
Status: int(consts.CamelOilTokenStatusAvailable),
|
||||
BindCount: 0,
|
||||
Remark: remark,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 0, gerror.Wrap(err, "创建 Token失败")
|
||||
}
|
||||
|
||||
tokenId, _ = result.LastInsertId()
|
||||
glog.Infof(ctx, "Token创建成功: tokenId=%d, tokenName=%s, phone=%s", tokenId, tokenName, phone)
|
||||
|
||||
return tokenId, nil
|
||||
}
|
||||
|
||||
// GetTokenInfo 获取 Token 信息
|
||||
func (s *sCamelOil) GetTokenInfo(ctx context.Context, tokenId int64) (token *entity.V1CamelOilToken, err error) {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
err = m.Where(dao.V1CamelOilToken.Columns().Id, tokenId).Scan(&token)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询Token信息失败")
|
||||
}
|
||||
if token == nil {
|
||||
return nil, gerror.New("Token不存在")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// ListTokens 列出所有可用的 Token
|
||||
func (s *sCamelOil) ListTokens(ctx context.Context) (tokens []*entity.V1CamelOilToken, err error) {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
err = m.Where(dao.V1CamelOilToken.Columns().Status, consts.CamelOilTokenStatusAvailable).
|
||||
OrderAsc(dao.V1CamelOilToken.Columns().LastUsedAt).
|
||||
OrderAsc(dao.V1CamelOilToken.Columns().LastBindAt).
|
||||
Scan(&tokens)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询Token列表失败")
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
// ListTokensWithPagination 列出所有可用的 Token(支持分页和查询条件)
|
||||
func (s *sCamelOil) ListTokensWithPagination(ctx context.Context, pageReq commonApi.CommonPageReq, tokenName string, status int) (tokens []*entity.V1CamelOilToken, total int, err error) {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
query := m.Where(dao.V1CamelOilToken.Columns().DeletedAt, nil)
|
||||
|
||||
// 添加查询条件
|
||||
if tokenName != "" {
|
||||
query = query.WhereLike(dao.V1CamelOilToken.Columns().TokenName, "%"+tokenName+"%")
|
||||
}
|
||||
|
||||
if status > 0 {
|
||||
query = query.Where(dao.V1CamelOilToken.Columns().Status, status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
totalCount, err := query.Count()
|
||||
if err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "查询Token总数失败")
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
err = query.
|
||||
Offset((pageReq.Current - 1) * pageReq.PageSize).
|
||||
Limit(pageReq.PageSize).
|
||||
OrderDesc(dao.V1CamelOilToken.Columns().CreatedAt).
|
||||
Scan(&tokens)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "查询Token列表失败")
|
||||
}
|
||||
|
||||
return tokens, int(totalCount), nil
|
||||
}
|
||||
|
||||
// UpdateToken 更新 Token 信息
|
||||
func (s *sCamelOil) UpdateToken(ctx context.Context, tokenId int64, tokenName string, status int, remark string) error {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
_, err := m.Where(dao.V1CamelOilToken.Columns().Id, tokenId).Update(&do.V1CamelOilToken{
|
||||
TokenName: tokenName,
|
||||
Status: status,
|
||||
Remark: remark,
|
||||
UpdatedAt: gtime.Now(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "更新Token失败")
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "Token更新成功: tokenId=%d", tokenId)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteToken 删除 Token(软删除)
|
||||
func (s *sCamelOil) DeleteToken(ctx context.Context, tokenId int64) error {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
_, err := m.Where(dao.V1CamelOilToken.Columns().Id, tokenId).
|
||||
Update(&do.V1CamelOilToken{
|
||||
DeletedAt: gtime.Now(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "删除Token失败")
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "Token删除成功: tokenId=%d", tokenId)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTokenStatus 更新 Token 状态并记录日志
|
||||
func (s *sCamelOil) UpdateTokenStatus(ctx context.Context, tokenId int64, newStatus consts.CamelOilTokenStatus, remark string) error {
|
||||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
// 获取当前 Token 信息
|
||||
var token *entity.V1CamelOilToken
|
||||
err := m.Where(dao.V1CamelOilToken.Columns().Id, tokenId).Scan(&token)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "查询Token失败")
|
||||
}
|
||||
if token == nil {
|
||||
return gerror.New("Token不存在")
|
||||
}
|
||||
|
||||
oldStatus := consts.CamelOilTokenStatus(token.Status)
|
||||
|
||||
// 如果状态没有变化,则不更新
|
||||
if oldStatus == newStatus {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新 Token 状态
|
||||
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, tokenId).Update(&do.V1CamelOilToken{
|
||||
Status: int(newStatus),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "更新Token状态失败")
|
||||
}
|
||||
|
||||
glog.Infof(ctx, "Token状态更新成功: tokenId=%d, 原状态=%s, 新状态=%s, 备注=%s", tokenId, consts.CamelOilTokenStatusText[oldStatus], consts.CamelOilTokenStatusText[newStatus], remark)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// 卡密绑定相关方法
|
||||
// ====================================================================================
|
||||
|
||||
// BindCardToToken 绑定卡密到 Token(使用轮询算法选择 Token)
|
||||
func (s *sCamelOil) BindCardToToken(ctx context.Context, orderId int64, cardNumber string, cardPassword string, amount decimal.Decimal) (bindingId int64, err error) {
|
||||
// 1. 获取订单信息
|
||||
var order *entity.V1CamelOilOrder
|
||||
err = dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Where(dao.V1CamelOilOrder.Columns().Id, orderId).
|
||||
Scan(&order)
|
||||
if err != nil {
|
||||
return 0, gerror.Wrap(err, "查询订单失败")
|
||||
}
|
||||
if order == nil {
|
||||
return 0, gerror.New("订单不存在")
|
||||
}
|
||||
|
||||
// 2. 获取所有可用的 Token
|
||||
tokens, err := s.ListTokens(ctx)
|
||||
if err != nil {
|
||||
return 0, gerror.Wrap(err, "查询Token列表失败")
|
||||
}
|
||||
|
||||
if len(tokens) == 0 {
|
||||
return 0, gerror.New("没有可用的Token")
|
||||
}
|
||||
|
||||
// 3. 使用轮询算法选择 Token(选择绑定金额最少的 Token)
|
||||
var selectedToken *entity.V1CamelOilToken
|
||||
minAmount := tokens[0].TotalBindAmount
|
||||
selectedToken = tokens[0]
|
||||
|
||||
for _, token := range tokens {
|
||||
if token.TotalBindAmount.Cmp(minAmount) < 0 {
|
||||
minAmount = token.TotalBindAmount
|
||||
selectedToken = token
|
||||
}
|
||||
}
|
||||
|
||||
// 4.2 调用绑卡接口(使用选中 Token 的 TokenValue)
|
||||
rechargeErrType, rechargeErr := camel_oil_api.NewClient().RechargeCard(ctx, selectedToken.TokenValue, selectedToken.Phone, cardPassword)
|
||||
if rechargeErr != nil {
|
||||
switch rechargeErrType {
|
||||
case camel_oil_api.RechargeCardErrorCode:
|
||||
// 卡密错误:标记订单为绑定失败
|
||||
glog.Warningf(ctx, "卡密错误: %v", rechargeErr)
|
||||
// 调用已实现的方法更新订单状态
|
||||
_ = s.UpdateOrderStatus(ctx, orderId, consts.CamelOilOrderStatusFailed, consts.CamelOilOrderChangeTypeFail, "", "卡密样检失败")
|
||||
return 0, gerror.Wrap(rechargeErr, "卡密样检失败")
|
||||
case camel_oil_api.RechargeCardErrorToken:
|
||||
// Token 过期/无效:标记 Token 为已过期
|
||||
glog.Warningf(ctx, "Token 过期: %v", rechargeErr)
|
||||
// 调用已实现的方法更新 Token 状态
|
||||
_ = s.UpdateTokenStatus(ctx, selectedToken.Id, consts.CamelOilTokenStatusExpired, "Token已过期")
|
||||
return 0, gerror.Wrap(rechargeErr, "Token过期,需要重新登录")
|
||||
default:
|
||||
// 网络或其他错误:不更新状态,由定时任务重试
|
||||
glog.Errorf(ctx, "绑卡失败(网络或其他错误): %v", rechargeErr)
|
||||
return 0, gerror.Wrap(rechargeErr, "绑卡操作失败,稍后重试")
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 创建绑定记录
|
||||
bindingResult, err := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1()).
|
||||
Insert(&do.V1CamelOilCardBinding{
|
||||
TokenId: selectedToken.Id,
|
||||
OrderId: orderId,
|
||||
CardNumber: cardNumber,
|
||||
CardPassword: cardPassword,
|
||||
Amount: amount,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 0, gerror.Wrap(err, "创建绑定记录失败")
|
||||
}
|
||||
|
||||
bindingId, _ = bindingResult.LastInsertId()
|
||||
|
||||
_, _ = dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1CamelOilToken.Columns().Id, selectedToken.Id).
|
||||
Data(dao.V1CamelOilToken.Columns().TotalBindAmount, &gdb.Counter{
|
||||
Field: dao.V1CamelOilToken.Columns().TotalBindAmount,
|
||||
Value: gconv.Float64(amount),
|
||||
}).Data(dao.V1CamelOilToken.Columns().BindCount, &gdb.Counter{
|
||||
Field: dao.V1CamelOilToken.Columns().BindCount,
|
||||
Value: gconv.Float64(amount),
|
||||
}).Data(dao.V1CamelOilToken.Columns().LastBindAt, gtime.Now()).Data(dao.V1CamelOilToken.Columns().LastUsedAt, gtime.Now()).Update()
|
||||
|
||||
glog.Infof(ctx, "卡密绑定成功: bindingId=%d, tokenId=%d, 订单ID=%d, 绑定金额=%.2f",
|
||||
bindingId, selectedToken.Id, orderId, amount.InexactFloat64())
|
||||
|
||||
return bindingId, nil
|
||||
}
|
||||
|
||||
// GetCardBindingInfo 获取卡密绑定信息
|
||||
func (s *sCamelOil) GetCardBindingInfo(ctx context.Context, bindingId int64) (binding *entity.V1CamelOilCardBinding, err error) {
|
||||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
err = m.Where(dao.V1CamelOilCardBinding.Columns().Id, bindingId).Scan(&binding)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询绑定信息失败")
|
||||
}
|
||||
if binding == nil {
|
||||
return nil, gerror.New("绑定记录不存在")
|
||||
}
|
||||
|
||||
return binding, nil
|
||||
}
|
||||
|
||||
// GetCardBindingByOrder 获取订单绑定的卡密信息
|
||||
func (s *sCamelOil) GetCardBindingByOrder(ctx context.Context, orderId int64) (binding *entity.V1CamelOilCardBinding, err error) {
|
||||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
err = m.Where(dao.V1CamelOilCardBinding.Columns().OrderId, orderId).Scan(&binding)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询绑定信息失败")
|
||||
}
|
||||
|
||||
return binding, nil
|
||||
}
|
||||
|
||||
// GetCardBindingsByToken 根据 tokenId 查询绑定的卡密信息
|
||||
func (s *sCamelOil) GetCardBindingsByToken(ctx context.Context, tokenId int64, current int, pageSize int) (bindings []*entity.V1CamelOilCardBinding, total int, err error) {
|
||||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
count, err := m.Where(dao.V1CamelOilCardBinding.Columns().TokenId, tokenId).Count()
|
||||
if err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "查询绑定记录计数失败")
|
||||
}
|
||||
|
||||
err = m.Where(dao.V1CamelOilCardBinding.Columns().TokenId, tokenId).
|
||||
Page(current, pageSize).
|
||||
Scan(&bindings)
|
||||
if err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "查询绑定记录失败")
|
||||
}
|
||||
|
||||
return bindings, int(count), nil
|
||||
}
|
||||
|
||||
// GetTokenBindingStats 获取 Token 的绑定统计
|
||||
func (s *sCamelOil) GetTokenBindingStats(ctx context.Context, tokenId int64) (bindCount int, totalAmount decimal.Decimal, err error) {
|
||||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
var stats struct {
|
||||
BindCount int
|
||||
TotalAmount decimal.Decimal
|
||||
}
|
||||
|
||||
err = m.Where(dao.V1CamelOilCardBinding.Columns().TokenId, tokenId).
|
||||
Fields("COUNT(*) as bind_count, SUM(amount) as total_amount").
|
||||
Scan(&stats)
|
||||
|
||||
if err != nil {
|
||||
return 0, decimal.Zero, gerror.Wrap(err, "查询Token绑定统计失败")
|
||||
}
|
||||
|
||||
return stats.BindCount, stats.TotalAmount, nil
|
||||
}
|
||||
|
||||
// CalculateTotalBindingAmount 计算所有 Token 的累计绑定金额
|
||||
func (s *sCamelOil) CalculateTotalBindingAmount(ctx context.Context) (totalAmount decimal.Decimal, err error) {
|
||||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||||
|
||||
var result struct {
|
||||
Total decimal.Decimal
|
||||
}
|
||||
|
||||
err = m.Fields("SUM(amount) as total").Scan(&result)
|
||||
if err != nil {
|
||||
return decimal.Zero, gerror.Wrap(err, "计算累计绑定金额失败")
|
||||
}
|
||||
|
||||
return result.Total, nil
|
||||
}
|
||||
@@ -46,6 +46,7 @@ func whiteListAuth(r *ghttp.Request) gcode.Code {
|
||||
"/api/restriction/collection/userInfo",
|
||||
"/api/cookieInfo/jd/order/placeOrder",
|
||||
"/api/jd-cookie/order/create",
|
||||
"/api/jd-v2/order/submit",
|
||||
}
|
||||
internalWhiteListAuth := []string{
|
||||
"/api/aes/encryption/params",
|
||||
|
||||
25
internal/model/do/v_1_camel_oil_card_binding.go
Normal file
25
internal/model/do/v_1_camel_oil_card_binding.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// V1CamelOilCardBinding is the golang structure of table camel_oil_card_binding for DAO operations like Where/Data.
|
||||
type V1CamelOilCardBinding struct {
|
||||
g.Meta `orm:"table:camel_oil_card_binding, do:true"`
|
||||
Id any // 主键ID
|
||||
TokenId any // Token ID
|
||||
OrderId any // 订单ID
|
||||
CardNumber any // 卡号
|
||||
CardPassword any // 卡密
|
||||
Amount any // 绑定金额
|
||||
Remark any // 备注
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
DeletedAt *gtime.Time // 删除时间(软删除)
|
||||
}
|
||||
28
internal/model/do/v_1_camel_oil_token.go
Normal file
28
internal/model/do/v_1_camel_oil_token.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// V1CamelOilToken is the golang structure of table camel_oil_token for DAO operations like Where/Data.
|
||||
type V1CamelOilToken struct {
|
||||
g.Meta `orm:"table:camel_oil_token, do:true"`
|
||||
Id any // 主键ID
|
||||
TokenName any // Token名称/标识
|
||||
TokenValue any // Token具体值
|
||||
Phone any // 绑定的手机号
|
||||
Status any // 状态:1可用 2已禁用 3已过期
|
||||
BindCount any // 已绑定卡密数量
|
||||
TotalBindAmount any // 累计绑定金额
|
||||
LastBindAt *gtime.Time // 最后绑定时间
|
||||
LastUsedAt *gtime.Time // 最后使用时间
|
||||
Remark any // 备注
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
DeletedAt *gtime.Time // 删除时间(软删除)
|
||||
}
|
||||
24
internal/model/entity/v_1_camel_oil_card_binding.go
Normal file
24
internal/model/entity/v_1_camel_oil_card_binding.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// V1CamelOilCardBinding is the golang structure for table v1camel_oil_card_binding.
|
||||
type V1CamelOilCardBinding struct {
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"`
|
||||
TokenId int64 `json:"tokenId" orm:"token_id" description:"Token ID"`
|
||||
OrderId int64 `json:"orderId" orm:"order_id" description:"订单ID"`
|
||||
CardNumber string `json:"cardNumber" orm:"card_number" description:"卡号"`
|
||||
CardPassword string `json:"cardPassword" orm:"card_password" description:"卡密"`
|
||||
Amount decimal.Decimal `json:"amount" orm:"amount" description:"绑定金额"`
|
||||
Remark string `json:"remark" orm:"remark" description:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"删除时间(软删除)"`
|
||||
}
|
||||
27
internal/model/entity/v_1_camel_oil_token.go
Normal file
27
internal/model/entity/v_1_camel_oil_token.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// V1CamelOilToken is the golang structure for table v1camel_oil_token.
|
||||
type V1CamelOilToken struct {
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"`
|
||||
TokenName string `json:"tokenName" orm:"token_name" description:"Token名称/标识"`
|
||||
TokenValue string `json:"tokenValue" orm:"token_value" description:"Token具体值"`
|
||||
Phone string `json:"phone" orm:"phone" description:"绑定的手机号"`
|
||||
Status int `json:"status" orm:"status" description:"状态:1可用 2已禁用 3已过期"`
|
||||
BindCount int `json:"bindCount" orm:"bind_count" description:"已绑定卡密数量"`
|
||||
TotalBindAmount decimal.Decimal `json:"totalBindAmount" orm:"total_bind_amount" description:"累计绑定金额"`
|
||||
LastBindAt *gtime.Time `json:"lastBindAt" orm:"last_bind_at" description:"最后绑定时间"`
|
||||
LastUsedAt *gtime.Time `json:"lastUsedAt" orm:"last_used_at" description:"最后使用时间"`
|
||||
Remark string `json:"remark" orm:"remark" description:"备注"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"删除时间(软删除)"`
|
||||
}
|
||||
@@ -7,10 +7,14 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "kami/api/camel_oil/v1"
|
||||
commonApi "kami/api/commonApi"
|
||||
"kami/internal/consts"
|
||||
"kami/internal/model"
|
||||
"kami/internal/model/entity"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -65,10 +69,10 @@ type (
|
||||
// CronAccountDailyResetTask 账号日重置任务 - 由cron调度器在每日00:05调用
|
||||
CronAccountDailyResetTask(ctx context.Context) error
|
||||
CronVerifyCodeCheckTask(ctx context.Context) error
|
||||
// SubmitOrder 提交订单并返回支付宝支付链接
|
||||
SubmitOrder(ctx context.Context, req *v1.SubmitOrderReq) (res *v1.SubmitOrderRes, err error)
|
||||
// UpdateOrderStatus 更新订单状态并记录历史
|
||||
UpdateOrderStatus(ctx context.Context, orderId int64, newStatus consts.CamelOilOrderStatus, operationType consts.CamelOilOrderChangeType, rawData string, description string) (err error)
|
||||
// SubmitOrder 提交订单并返回支付宝支付链接
|
||||
SubmitOrder(ctx context.Context, req *v1.SubmitOrderReq) (res *v1.SubmitOrderRes, err error)
|
||||
// TriggerOrderCallback 触发订单回调
|
||||
TriggerOrderCallback(ctx context.Context, req *v1.OrderCallbackReq) (res *v1.OrderCallbackRes, err error)
|
||||
// ProcessPendingCallbacks 处理待回调订单(定时任务使用)
|
||||
@@ -99,6 +103,36 @@ type (
|
||||
RecordPrefetchOrderHistory(ctx context.Context, prefetchId int64, changeType consts.CamelOilPrefetchOrderChangeType, accountId int64, accountName string, remark string) error
|
||||
// CleanExpiredPrefetchOrders 清理过期的预拉取订单
|
||||
CleanExpiredPrefetchOrders(ctx context.Context) (cleanedCount int, err error)
|
||||
|
||||
// ====================================================================================
|
||||
// Token 管理相关方法
|
||||
// ====================================================================================
|
||||
|
||||
// CreateToken 创建 Token
|
||||
CreateToken(ctx context.Context, tokenName string, tokenValue string, phone string, remark string) (tokenId int64, err error)
|
||||
// GetTokenInfo 获取 Token 信息
|
||||
GetTokenInfo(ctx context.Context, tokenId int64) (token *entity.V1CamelOilToken, err error)
|
||||
// ListTokens 列出所有可用的 Token
|
||||
ListTokens(ctx context.Context) (tokens []*entity.V1CamelOilToken, err error)
|
||||
// ListTokensWithPagination 列出所有可用的 Token(支持分页和查询条件)
|
||||
ListTokensWithPagination(ctx context.Context, pageReq commonApi.CommonPageReq, tokenName string, status int) (tokens []*entity.V1CamelOilToken, total int, err error)
|
||||
// UpdateToken 更新 Token 信息
|
||||
UpdateToken(ctx context.Context, tokenId int64, tokenName string, status int, remark string) error
|
||||
// DeleteToken 删除 Token(软删除)
|
||||
DeleteToken(ctx context.Context, tokenId int64) error
|
||||
|
||||
// BindCardToToken 绑定卡密到 Token(使用轮询算法选择 Token)
|
||||
BindCardToToken(ctx context.Context, orderId int64, cardNumber string, cardPassword string, amount decimal.Decimal) (bindingId int64, err error)
|
||||
// GetCardBindingInfo 获取卡密绑定信息
|
||||
GetCardBindingInfo(ctx context.Context, bindingId int64) (binding *entity.V1CamelOilCardBinding, err error)
|
||||
// GetCardBindingByOrder 获取订单绑定的卡密信息
|
||||
GetCardBindingByOrder(ctx context.Context, orderId int64) (binding *entity.V1CamelOilCardBinding, err error)
|
||||
// GetCardBindingsByToken 根据 tokenId 查询绑定的卡密信息
|
||||
GetCardBindingsByToken(ctx context.Context, tokenId int64, current int, pageSize int) (bindings []*entity.V1CamelOilCardBinding, total int, err error)
|
||||
// GetTokenBindingStats 获取 Token 的绑定统计
|
||||
GetTokenBindingStats(ctx context.Context, tokenId int64) (bindCount int, totalAmount decimal.Decimal, err error)
|
||||
// CalculateTotalBindingAmount 计算所有 Token 的累计绑定金额
|
||||
CalculateTotalBindingAmount(ctx context.Context) (totalAmount decimal.Decimal, err error)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -171,3 +171,55 @@ CREATE TABLE `camel_oil_prefetch_order_history` (
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '骆驼加油预拉取订单历史表';
|
||||
|
||||
-- 7. 骆驼加油 Token 管理表
|
||||
DROP TABLE IF EXISTS `camel_oil_token`;
|
||||
|
||||
CREATE TABLE `camel_oil_token` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`token_name` varchar(128) NOT NULL COMMENT 'Token名称/标识',
|
||||
`token_value` text NOT NULL COMMENT 'Token具体值',
|
||||
`phone` varchar(20) DEFAULT NULL COMMENT '绑定的手机号',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1可用 2已禁用 3已过期',
|
||||
`bind_count` int NOT NULL DEFAULT 0 COMMENT '已绑定卡密数量',
|
||||
`total_bind_amount` decimal(15,2) NOT NULL DEFAULT 0 COMMENT '累计绑定金额',
|
||||
`last_bind_at` datetime DEFAULT NULL COMMENT '最后绑定时间',
|
||||
`last_used_at` datetime DEFAULT NULL COMMENT '最后使用时间',
|
||||
`remark` text DEFAULT NULL COMMENT '备注',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_token_value` (`token_value`(255)),
|
||||
KEY `idx_token_name` (`token_name`),
|
||||
KEY `idx_phone` (`phone`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_bind_count` (`bind_count`),
|
||||
KEY `idx_total_bind_amount` (`total_bind_amount`),
|
||||
KEY `idx_last_bind_at` (`last_bind_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '骆驼加油Token管理表';
|
||||
|
||||
-- 8. 骆驼加油卡密绑定记录表
|
||||
DROP TABLE IF EXISTS `camel_oil_card_binding`;
|
||||
|
||||
CREATE TABLE `camel_oil_card_binding` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`token_id` bigint NOT NULL COMMENT 'Token ID',
|
||||
`order_id` bigint NOT NULL COMMENT '订单ID',
|
||||
`card_number` varchar(256) DEFAULT NULL COMMENT '卡号',
|
||||
`card_password` varchar(256) DEFAULT NULL COMMENT '卡密',
|
||||
`amount` decimal(10,2) NOT NULL COMMENT '绑定金额',
|
||||
`remark` text DEFAULT NULL COMMENT '备注',
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间(软删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_token_id` (`token_id`),
|
||||
CONSTRAINT `fk_camel_oil_card_binding_token_id` FOREIGN KEY (`token_id`) REFERENCES `camel_oil_token` (`id`) ON DELETE RESTRICT,
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
CONSTRAINT `fk_camel_oil_card_binding_order_id` FOREIGN KEY (`order_id`) REFERENCES `camel_oil_order` (`id`) ON DELETE CASCADE,
|
||||
KEY `idx_amount` (`amount`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '骆驼加油卡密绑定记录表';
|
||||
|
||||
@@ -76,20 +76,25 @@ func registerMainTasks(ctx context.Context) {
|
||||
|
||||
// registerCamelOilTasks 注册骆驼加油模块的定时任务
|
||||
func registerCamelOilTasks(ctx context.Context) {
|
||||
// 账户预拉取任务 - 每30秒执行一次
|
||||
// 流程:并发拉取账户到指定数量
|
||||
//_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
// _ = service.CamelOil().CronAccountPrefetchTask(ctx)
|
||||
//}, "CamelOilAccountPrefetch")
|
||||
//
|
||||
//_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
// _ = service.CamelOil().CronVerifyCodeCheckTask(ctx)
|
||||
//}, "CamelOilAccountVerifyCodeCheck")
|
||||
_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
_ = service.CamelOil().CronAccountPrefetchTask(ctx)
|
||||
}, "CamelOilAccountPrefetch")
|
||||
|
||||
//// 流程:检查预拉取订单库存,不足时补充
|
||||
//_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
// _ = service.CamelOil().CronPrefetchOrderSupplementTask(ctx)
|
||||
//}, "CamelOilPrefetchOrderSupplement")
|
||||
_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
_ = service.CamelOil().CronVerifyCodeCheckTask(ctx)
|
||||
}, "CamelOilAccountVerifyCodeCheck")
|
||||
|
||||
_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
_ = service.CamelOil().CronOrderPaymentCheckTask(ctx)
|
||||
}, "CamelOilOrderPaymentCheck")
|
||||
|
||||
_, _ = gcron.AddSingleton(ctx, "0 1 0 * * ?", func(ctx context.Context) {
|
||||
_ = service.CamelOil().CronAccountDailyResetTask(ctx)
|
||||
}, "CamelOilAccountDailyReset")
|
||||
|
||||
_, _ = gcron.AddSingleton(ctx, "@every 10s", func(ctx context.Context) {
|
||||
_ = service.CamelOil().ProcessPendingCallbacks(ctx)
|
||||
}, "CamelOilProcessPendingCallbacks")
|
||||
|
||||
glog.Info(ctx, "骆驼加油模块定时任务注册完成")
|
||||
}
|
||||
|
||||
@@ -4,10 +4,20 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/rand/v2"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 绑卡错误类型枚举
|
||||
const (
|
||||
RechargeCardSuccess = 0 // 绑卡成功
|
||||
RechargeCardErrorCode = 1 // 卡密错误
|
||||
RechargeCardErrorToken = 2 // Token 过期/无效
|
||||
RechargeCardErrorNetwork = 3 // 网络或其他错误
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@@ -142,14 +152,14 @@ func (c *Client) CreateOrder(ctx context.Context, phone, token string, amount fl
|
||||
Brand string `json:"brand"`
|
||||
DeviceId string `json:"deviceId"`
|
||||
}{
|
||||
OpenId: "app2511181557205741495",
|
||||
OpenId: "app2511221747117503039",
|
||||
Phone: phone,
|
||||
GoodId: goodId,
|
||||
GoodNum: 1,
|
||||
BindPhone: phone,
|
||||
PayType: "appAli",
|
||||
ParamY: 26.996671,
|
||||
ParamX: 77.450347,
|
||||
ParamY: 31.2304 + (rand.Float64()-0.5)*2,
|
||||
ParamX: 121.4737 + (rand.Float64()-0.5)*2,
|
||||
Yanqian: true,
|
||||
MobileOperatingPlatform: "iOS",
|
||||
SysVersion: "iOS 15.7",
|
||||
@@ -157,8 +167,9 @@ func (c *Client) CreateOrder(ctx context.Context, phone, token string, amount fl
|
||||
NetWork: "unknown",
|
||||
Platform: "ios",
|
||||
Brand: "apple",
|
||||
DeviceId: strings.Replace(uuid.NewString(), "-", "", -1),
|
||||
DeviceId: strings.ToUpper(strings.ReplaceAll(uuid.NewString(), "-", "")),
|
||||
}
|
||||
|
||||
pubkey := "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkc6Xr/JhWEx/WPxG2q3VHLQ+FYk/oCmQ1y14B5j4xOJY+mAWoDOOti3sAXg0Kk662gWjWET1nLI6YED4wb9HWon1NAZn47lgc5ohIpEdU91Jao85X/kgkD3NvTTvhFicttepUOsrYUZN8rAQCE7AhzwGgKnCiIRY/kE8jOCCeZQIDAQAB"
|
||||
body, _ := json.Marshal(&bodyStr)
|
||||
bodyS, _ := EncryptWithPublicKey(pubkey, string(body))
|
||||
@@ -176,7 +187,7 @@ func (c *Client) CreateOrder(ctx context.Context, phone, token string, amount fl
|
||||
return "", "", err
|
||||
}
|
||||
respStr := resp.ReadAllString()
|
||||
glog.Info(ctx, "登录", req, respStr)
|
||||
glog.Info(ctx, "登录", bodyStr, respStr)
|
||||
respStruct := struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
@@ -195,10 +206,15 @@ func (c *Client) CreateOrder(ctx context.Context, phone, token string, amount fl
|
||||
if respStruct.Code == "auth_error" {
|
||||
return "", "", errors.New("auth_error")
|
||||
}
|
||||
if respStruct.Code == "error" && strings.Contains(respStruct.Message, "系统繁忙") {
|
||||
continue
|
||||
}
|
||||
if respStruct.Code == "success" {
|
||||
base64PrivateKey := "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKRzpev8mFYTH9Y/EbardUctD4ViT+gKZDXLXgHmPjE4lj6YBagM462LewBeDqTrraBaNYRPWcsjpgQPjBv0daifU0BmfjuWBzmiEikR1T3Ulqjzlf+SCQPc29NO+EWJy216l6ythRk3ysBAITsCHPAaAqcKIhFj+QTyM4IJ5lAgMBAAECgYEAg/vlMI7b3EkhBhw8JTVavLMnf8+1fe/JGXuMiU22oF5gBwCPmZ4upLwLDfJt2Q1J7WPTNetEMqgKEXUH1GwKJkFGm2tSEMHSHdTmUTQ3w6bS1C0peZghyhmlWRXUlpKk5tDOQ24sWO268YrwZyueXnVGKJ4s0hY0KOiZIU2trUECQQDxA+lzq/t/L09M/bUybjsiP6eb/HBeZeu+2+JnHekb8z9BMXTOKTHqAI0Cs9UvE6BDT3aU9IJbWHbRogIMypT9AkEArq0fccphwWtAIyS0+fns4Hqs4On7yTfXSXWiAbSVif1LxP60b5n5Xm8lo12oHkdwOvKaesvgDpnIGUM9xjFfiQJASTZrABxKNYRljnmzRTJ+/BRiEdxJNiO3zS52Q+SuHzNxD5i6ZrXU18R7EUsXg0lu8YN9/hmYT687yMpx3Pjc8QJAZBs1lSouQgIsPLfRvB1+otvLbg7KzPPivufak+2hcfanUNvEHt14a6V5RZnsOoYojK/y1oM3AkchxVCi+43aOQJAC0gI6qsZ3VaPu9QDddrHPJ1dCHTXyfcNJ0op3srCVF92HoBWX54pzeagj+9g/Z4oUT9IhaO0Q3YE07N03HuVrQ=="
|
||||
respData, _ := DecryptWithPrivateKey(base64PrivateKey, respStruct.OrderRes.Body)
|
||||
return respStruct.OrderId, respData, err
|
||||
}
|
||||
}
|
||||
return "", "", errors.New("创建订单超时")
|
||||
}
|
||||
|
||||
@@ -285,3 +301,61 @@ func (c *Client) QueryOrder(ctx context.Context, phone, token, orderId string) (
|
||||
glog.Warningf(ctx, "查询订单%s超过最大页数%d,未找到", orderId, maxPages)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RechargeCard 绑卡接口
|
||||
// 返回值说明:
|
||||
// - errType == RechargeCardSuccess: 绑卡成功
|
||||
// - errType == RechargeCardErrorCode: 卡密错误
|
||||
// - errType == RechargeCardErrorToken: token 过期/无效
|
||||
// - errType == RechargeCardErrorNetwork: 网络或其他错误
|
||||
func (c *Client) RechargeCard(ctx context.Context, token, phone, eCardCode string) (errType int, err error) {
|
||||
c.Client.SetHeader("Authorization", "Bearer "+c.getAuth(ctx, token))
|
||||
|
||||
req := struct {
|
||||
ECardCode string `json:"eCardCode"`
|
||||
OpenId string `json:"openId"`
|
||||
Phone string `json:"phone"`
|
||||
Channel string `json:"channel"`
|
||||
}{
|
||||
ECardCode: eCardCode,
|
||||
OpenId: "app2511181557205741495",
|
||||
Phone: phone,
|
||||
Channel: "app",
|
||||
}
|
||||
|
||||
resp, err := c.Client.ContentJson().Post(ctx, "https://recharge3.bac365.com/camel_wechat_mini_oil_server/eCardMall/eCardRecharge", req)
|
||||
if err != nil {
|
||||
glog.Errorf(ctx, "绑卡请求失败,错误: %v", err)
|
||||
return RechargeCardErrorNetwork, err
|
||||
}
|
||||
|
||||
respStruct := struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
err = json.Unmarshal(resp.ReadAll(), &respStruct)
|
||||
if err != nil {
|
||||
glog.Errorf(ctx, "解析绑卡响应失败,错误: %v", err)
|
||||
return RechargeCardErrorNetwork, err
|
||||
}
|
||||
|
||||
// 根据不同的错误码进行分类
|
||||
switch respStruct.Code {
|
||||
case "success":
|
||||
glog.Infof(ctx, "卡密绑卡成功,手机号: %s", phone)
|
||||
return RechargeCardSuccess, nil
|
||||
case "codeError":
|
||||
err = errors.New(respStruct.Message)
|
||||
glog.Warningf(ctx, "卡密错误: %v", err)
|
||||
return RechargeCardErrorCode, err
|
||||
case "auth_error", "unauthorized":
|
||||
err = errors.New(respStruct.Message)
|
||||
glog.Warningf(ctx, "Token 错误或已过期: %v", err)
|
||||
return RechargeCardErrorToken, err
|
||||
default:
|
||||
err = errors.New(respStruct.Message)
|
||||
glog.Errorf(ctx, "绑卡失败: %v", err)
|
||||
return RechargeCardErrorNetwork, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func TestClient_SendCaptcha(t *testing.T) {
|
||||
for range 10 {
|
||||
NewClient().CreateOrder(t.Context(), "18627350353", "X/c/tYh6aFvm7AeBeMlCkiPaTtOGrMpI1NPwL93RqUkb0JKYjK1Qe8OUIa85y621qHauOJBaEl977RmtJU8YHPAaLYhHi7BB1nhosYJOyIO6035w7uvWRc8Rg4lhA2TgUU4XnF+4jCvt8s+qWzEQLCNOd7VzpC2oYx60RqKvN60=", 100)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ func TestDec(t *testing.T) {
|
||||
prikey := "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKRzpev8mFYTH9Y/EbardUctD4ViT+gKZDXLXgHmPjE4lj6YBagM462LewBeDQqTrraBaNYRPWcsjpgQPjBv0daifU0BmfjuWBzmiEikR1T3Ulqjzlf+SCQPc29NO+EWJy216lQ6ythRk3ysBAITsCHPAaAqcKIhFj+QTyM4IJ5lAgMBAAECgYEAg/vlMI7b3EkhBhw8JTVavLMnf8+1fe/JGXuMiU22oF5gBwCPmZ4upLwLDfJt2Q1J7WPTNetEMqgKEXUH1GwKJkFGm2tSEMHSHdTmUTQ3w6bS1C0peZghyhmlWRXUlpKk5tDOQ24sWO268YrwZyueXnVGKJ4s0hY0KOiZIU2trUECQQDxA+lzq/t/L09M/bUybjsiP6eb/HBeZeu+2+JnHekb8z9BMXTOKTHqAI0Cs9UvE6BDT3aU9IJbWHbRogIMypT9AkEArq0fccphwWtAIyS0+fns4Hqs4On7yTfXSXWiAbSVif1LxP60b5n5Xm8lo12oHkdwOvKaesvgDpnIGUM9xjFfiQJASTZrABxKNYRljnmzRTJ+/BRiEdxJNiO3zS52Q+SuHzNxD5i6ZrXU18R7EUsXg0lu8YN9/hmYT687yMpx3Pjc8QJAZBs1lSouQgIsPLfRvB1+otvLbg7KzPPivufak+2hcfanUNvEHt14a6V5RZnsOoYojK/y1oM3AkchxVCi+43aOQJAC0gI6qsZ3VaPu9QDddrHPJ1dCHTXyfcNJ0op3srCVF92HoBWX54pzeagj+9g/Z4oUT9IhaO0Q3YE07N03HuVrQ=="
|
||||
//encryptedBody := "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"
|
||||
|
||||
encryptedBody := "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"
|
||||
|
||||
encryptedBody := "DiOna3ZZzIhriSHzQFMAyfRvt/aye8pkC/RBC6wKYoyrsjDQ+MjWYBkb8eml8S9zTyiuyRmsB/4Shr0O5HeFecA+XfyAIlk4tuVHnjK5eOLF6Ovfr2LlZ+yR23kB5jmJAVG7+n0Jovx9/UqMzl9pPafx47Rz3Ab+kOaL8ffjBspT5sRH7H+P4VgbQgTAPMAZNh7A+hREQneAPlhEefYs1bIyYof8E1QQk7K6HaoRCd9bwO1l3EiTsp8goOIH4LFFJISAsuAm63O1r9eIcCqWgR7DogD62mHCf4aCOwbGOrAojnq1nN1P2ITxJH78NjlFZvmMgwZrS8sOi5zuYyV+xCoyDokSe5sQxjG6QxnVT2qI38yuBJE4BYEev5bFVPEbVs0DOVMYnqULMX3yk8ggqTcx8C4lwlW5FYL0PJRiOpBQeomOzdH2Yp1ZE6s5o6EuQLiAvJoLAjf722jnYDYy6q0ZqgRdGp8HE/NsniZf+PASDD0e8JrhcvJ+lJbCpB9WksVT7fRXR8i1Pv2bQvNJtp3/M+C9XwvrMK/O/8FJZ51MKelMZi9l+YafvxboB0ryC9kmHO3P2aTZQnKrtYwbIIiuTgbdtMbVQAVvkMtBmC6i0apJTvxo1VA3j46agu6Mhi5jpo+mQXWqMbbMGZ+Sof2FmwZyBFjfLRntueyoAtU="
|
||||
// Use DecryptWithPrivateKey function to decrypt
|
||||
decrypted, err := DecryptWithPrivateKey(prikey, encryptedBody)
|
||||
if err != nil {
|
||||
@@ -47,10 +46,4 @@ func TestDec(t *testing.T) {
|
||||
}
|
||||
t.Logf("Decrypted: %s", decrypted)
|
||||
t.Log("Test passed! Decryption matches Java result.")
|
||||
|
||||
// const orderStr = 'alipay_sdk=xxx...';
|
||||
//const encodedPayUrl = encodeURIComponent(orderStr);
|
||||
//alipay: //platformapi/startapp?appId=20000067&url=alipay-sdk-java-4.40.33.ALL%26app_id%3d2021005196642063%26biz_content%3d%257B%2522business_params%2522%253A%2522%257B%255C%2522outTradeRiskInfo%255C%2522%253A%255C%2522%257B%255C%255C%255C%2522sysVersion%255C%255C%255C%2522%253A%255C%255C%255C%2522iOS%2b26.2%255C%255C%255C%2522%252C%255C%255C%255C%2522mcCreateTradePackage%255C%255C%255C%2522%253A%255C%255C%255C%2522com.ltjy.uni1005507%255C%255C%255C%2522%252C%255C%255C%255C%2522mcCreateTradeTime%255C%255C%255C%2522%253A%255C%255C%255C%25222025-11-22%2b01%253A39%253A56%255C%255C%255C%2522%252C%255C%255C%255C%2522mcCreateTradeLbs%255C%255C%255C%2522%253A%255C%255C%255C%2522118.9885703919527%252C36.36133429662592%255C%255C%255C%2522%252C%255C%255C%255C%2522mobileOperatingPlatform%255C%255C%255C%2522%253A%255C%255C%255C%2522ios%255C%255C%255C%2522%252C%255C%255C%255C%2522platformType%255C%255C%255C%2522%253A%255C%255C%255C%2522iPad%2bPro%2b%252812.9-inch%2529%2b%25283rd%2bgeneration%2529%255C%255C%255C%2522%252C%255C%255C%255C%2522mcCreateTradeChannel%255C%255C%255C%2522%253A%255C%255C%255C%2522app%255C%255C%255C%2522%252C%255C%255C%255C%2522extraAccountRegTime%255C%255C%255C%2522%253A%255C%255C%255C%25222025-11-18%2b15%253A57%253A21%255C%255C%255C%2522%252C%255C%255C%255C%2522extraAccountPhone%255C%255C%255C%2522%253A%255C%255C%255C%252217862666120%255C%255C%255C%2522%252C%255C%255C%255C%2522netWork%255C%255C%255C%2522%253A%255C%255C%255C%2522unknown%255C%255C%255C%2522%257D%255C%2522%252C%255C%2522mc_create_trade_ip%255C%2522%253A%255C%2522123.168.253.227%255C%2522%257D%2522%252C%2522out_trade_no%2522%253A%25222511220139557299472%2522%252C%2522subject%2522%253A%2522%25E9%25AA%2586%25E9%25A9%25BC%25E6%258A%25B5%25E6%2589%25A3%25E5%2588%25B8-50%25E5%2585%2583%25E9%259D%25A2%25E5%2580%25BC%2522%252C%2522timeout_express%2522%253A%252210m%2522%252C%2522total_amount%2522%253A%252250.00%2522%257D%26charset%3dUTF-8%26format%3djson%26method%3dalipay.trade.app.pay%26notify_url%3dhttps%253A%252F%252Frecharge3.bac365.com%252Fpayment%252Falipay%252Fnotify%26sign%3dDQ0Kf5KCZ1jUHptXuAriStVEHDbKdeNWofxZr%252FAL%252BAA%252FYytu0kXNTDkviGLKMG9je2wS419dKGqX8inT9y7G%252FtcactTKoLavi3gy6RLys0Yof97B1umkopzqIhQ2nA9lxDsaLEfqXfdv33zaRetQFgPGzIv5KrOPRY18RWvwp5PqKodSp9AzbJ5bAGELTzbJ8Oj4YOeNSFelsOUaD0J4Ecw%252B7qmDIDb8UDIBYXFaNUquPtWE%252FjAZ%252FGM9PWtMYx7%252Fiq2mmbJJTe5ErN7L%252BtsXGV51axZ2f%252F0EZKISMKkgI0I5ECnHZjGa2pw%252FByMeMREYF05ZuF%252Fowcn53Snag3j0aw%253D%253D%26sign_type%3dRSA2%26timestamp%3d2025-11-22%2b01%253A39%253A56%26version%3d1.0
|
||||
//window.location.href = alipayScheme;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user