feat(camel_oil): 支持Token验证码输入与重新发送功能

- 在CamelOil接口中新增InputVerificationCode和ResendVerificationCode方法
- 扩展Token状态,增加验证码验证失败状态
- 调整Token创建逻辑,初始化状态为验证码已发送
- 删除旧的发送验证码和登录方法,改为统一的验证码输入处理
- 增加验证Token状态后验证码登录逻辑,更新登录Token信息
- 实现重新发送验证码接口,限制状态后允许重发
- API和控制层新增输入验证码与重新发送验证码的请求响应结构与处理逻辑
- Token列表及详情响应中新增LoginToken字段返回登录令牌信息
This commit is contained in:
danial
2025-12-09 13:58:05 +08:00
parent 3ef482357b
commit 8c457895c8
12 changed files with 222 additions and 134 deletions

View File

@@ -30,5 +30,7 @@ type ICamelOilV1 interface {
ListTokens(ctx context.Context, req *v1.ListTokensReq) (res *v1.ListTokensRes, err error)
UpdateToken(ctx context.Context, req *v1.UpdateTokenReq) (res *v1.UpdateTokenRes, err error)
DeleteToken(ctx context.Context, req *v1.DeleteTokenReq) (res *v1.DeleteTokenRes, err error)
InputVerificationCode(ctx context.Context, req *v1.InputVerificationCodeReq) (res *v1.InputVerificationCodeRes, err error)
ResendVerificationCode(ctx context.Context, req *v1.ResendVerificationCodeReq) (res *v1.ResendVerificationCodeRes, err error)
ListCardBindingsByToken(ctx context.Context, req *v1.ListCardBindingsByTokenReq) (res *v1.ListCardBindingsByTokenRes, err error)
}

View File

@@ -2,10 +2,10 @@ package v1
import (
"encoding/json"
"kami/api/commonApi"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"kami/api/commonApi"
"kami/internal/consts"
)
// ====================================================================================
@@ -15,7 +15,7 @@ import (
// CreateTokenReq 创建 Token 请求
type CreateTokenReq struct {
g.Meta `path:"/token/create" tags:"JD V2 Token Management" method:"post" summary:"创建 Token"`
Name string `json:"name" v:"required" description:"名称"`
Name string `json:"name" v:"required" description:"Token名称"`
Phone string `json:"phone" v:"required" description:"绑定的手机号"`
Remark string `json:"remark" description:"备注"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" v:"required" description:"充值金额限制"`
@@ -35,22 +35,22 @@ type GetTokenReq struct {
// TokenInfo Token 信息
type TokenInfo struct {
Id int64 `json:"id" description:"Token ID"`
UserId string `json:"userId" description:"用户ID空字符串表示管理员创建"`
Name string `json:"name" description:"名称"`
Phone string `json:"phone" description:"绑定的手机号"`
Status consts.CamelOilTokenStatus `json:"status" description:"状态"`
BindCount int `json:"bindCount" description:"已绑定卡密数量"`
TotalBindAmount float64 `json:"totalBindAmount" description:"累计绑定金额"`
TotalRechargeAmount float64 `json:"totalRechargeAmount" description:"总充值金额"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" description:"充值金额限制"`
RechargeLimitCount int `json:"rechargeLimitCount" description:"充值次数限制"`
LastBindAt *gtime.Time `json:"lastBindAt" description:"最后绑定时间"`
LastUsedAt *gtime.Time `json:"lastUsedAt" description:"最后使用时间"`
LastLoginAt *gtime.Time `json:"lastLoginAt" description:"最后登录时间"`
Remark string `json:"remark" description:"备注"`
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"`
Id int64 `json:"id" description:"Token ID"`
UserId string `json:"userId" description:"用户ID空字符串表示管理员创建"`
Name string `json:"name" description:"Token名称"`
LoginToken string `json:"loginToken" description:"登录Token"`
Phone string `json:"phone" description:"绑定的手机号"`
Status int `json:"status" description:"状态"`
BindCount int `json:"bindCount" description:"已绑定卡密数量"`
TotalBindAmount float64 `json:"totalBindAmount" description:"累计绑定金额"`
TotalRechargeAmount float64 `json:"totalRechargeAmount" description:"充值金额"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" description:"充值金额限制"`
RechargeLimitCount int `json:"rechargeLimitCount" 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 信息响应
@@ -67,15 +67,15 @@ type ListTokensRes struct {
type ListTokensReq struct {
g.Meta `path:"/token/list" tags:"JD V2 Token Management" method:"get" summary:"列出 Token"`
commonApi.CommonPageReq
Name string `json:"name" description:"名称"`
Status consts.CamelOilTokenStatus `json:"status" description:"状态"`
Name string `json:"name" description:"Token名称"`
Status int `json:"status" description:"状态"`
}
// UpdateTokenReq 修改 Token 请求
type UpdateTokenReq struct {
g.Meta `path:"/token/update" tags:"JD V2 Token Management" method:"post" summary:"修改 Token"`
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
Name string `json:"name" v:"required" description:"名称"`
Name string `json:"name" v:"required" description:"Token名称"`
Phone string `json:"phone" description:"绑定的手机号"`
Remark string `json:"remark" description:"备注"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" v:"required" description:"充值金额限制"`
@@ -98,6 +98,29 @@ type DeleteTokenRes struct {
Message string `json:"message" description:"信息"`
}
// InputVerificationCodeReq 输入验证码请求
type InputVerificationCodeReq struct {
g.Meta `path:"/token/input-verification-code" tags:"JD V2 Token Management" method:"post" summary:"输入验证码"`
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
Code string `json:"code" v:"required" description:"验证码"`
}
// InputVerificationCodeRes 输入验证码响应
type InputVerificationCodeRes struct {
Message string `json:"message" description:"信息"`
}
// ResendVerificationCodeReq 重新发送验证码请求
type ResendVerificationCodeReq struct {
g.Meta `path:"/token/resend-verification-code" tags:"JD V2 Token Management" method:"post" summary:"重新发送验证码"`
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
}
// ResendVerificationCodeRes 重新发送验证码响应
type ResendVerificationCodeRes struct {
Message string `json:"message" description:"信息"`
}
// ====================================================================================
// 卡密绑定 API 请求/响应结构
// ====================================================================================
@@ -133,6 +156,7 @@ func (t TokenInfo) MarshalJSON() ([]byte, error) {
Id: t.Id,
UserId: t.UserId,
Name: t.Name,
LoginToken: t.LoginToken,
Phone: t.Phone,
Status: t.Status,
BindCount: t.BindCount,
@@ -142,7 +166,6 @@ func (t TokenInfo) MarshalJSON() ([]byte, error) {
RechargeLimitCount: t.RechargeLimitCount,
LastBindAt: t.LastBindAt,
LastUsedAt: t.LastUsedAt,
LastLoginAt: t.LastLoginAt,
Remark: t.Remark,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,

View File

@@ -2,6 +2,7 @@ package consts
import (
"github.com/gogf/gf/v2/os/gtime"
"time"
)
// ====================================================================================
@@ -89,7 +90,8 @@ const (
CamelOilTokenStatusDisabled CamelOilTokenStatus = 2 // 已禁用
CamelOilTokenStatusExpired CamelOilTokenStatus = 3 // 已过期
CamelOilTokenStatusLoginFailed CamelOilTokenStatus = 4 // 登录失败
CamelOilTokenStatusCodeSent CamelOilTokenStatus = 5 // 验证码已发送待登录
CamelOilTokenStatusCodeSent CamelOilTokenStatus = 5 // 验证码已发送
CamelOilTokenStatusVerificationFailed CamelOilTokenStatus = 6 // 验证码验证失败
)
// CamelOilTokenStatusText Token状态文本映射
@@ -100,6 +102,7 @@ var CamelOilTokenStatusText = map[CamelOilTokenStatus]string{
CamelOilTokenStatusExpired: "已过期",
CamelOilTokenStatusLoginFailed: "登录失败",
CamelOilTokenStatusCodeSent: "验证码已发送",
CamelOilTokenStatusVerificationFailed: "验证码验证失败",
}
// ====================================================================================
@@ -216,7 +219,7 @@ var CamelOilPrefetchOrderChangeTypeText = map[CamelOilPrefetchOrderChangeType]st
const (
// CamelOilPrefetchOrderExpireDuration 预拉取订单过期时间(小时)
CamelOilPrefetchOrderExpireDuration = gtime.M * 5
CamelOilPrefetchOrderExpireDuration = time.Hour * 24
// CamelOilPrefetchOrderLockKey Redis中预拉取订单的分布式锁键名前缀
CamelOilPrefetchOrderLockKey = "camel_oil_api:prefetch:order:lock:"

View File

@@ -6,7 +6,6 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
v1 "kami/api/camel_oil/v1"
"kami/internal/errHandler"
"kami/internal/model"
"kami/internal/service"
)
@@ -16,13 +15,7 @@ func (c *ControllerV1) CreateToken(ctx context.Context, req *v1.CreateTokenReq)
err = errHandler.WrapError(ctx, gcode.CodeInternalError, err, "登录校验失败")
return
}
tokenId, err := service.CamelOil().CreateToken(ctx, &model.CamelOilTokenCreateInput{
Name: req.Name,
Phone: req.Phone,
Remark: req.Remark,
RechargeLimitAmount: req.RechargeLimitAmount,
RechargeLimitCount: req.RechargeLimitCount,
})
tokenId, err := service.CamelOil().CreateToken(ctx, req.Name, "", req.Phone, req.Remark, req.RechargeLimitAmount, req.RechargeLimitCount)
if err != nil {
return nil, err
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
v1 "kami/api/camel_oil/v1"
"kami/internal/consts"
"kami/internal/errHandler"
"kami/internal/service"
)
@@ -25,8 +24,9 @@ func (c *ControllerV1) GetToken(ctx context.Context, req *v1.GetTokenReq) (res *
Id: token.Id,
UserId: token.UserId,
Name: token.Name,
LoginToken: token.LoginToken,
Phone: token.Phone,
Status: consts.CamelOilTokenStatus(token.Status),
Status: token.Status,
BindCount: token.BindCount,
TotalBindAmount: token.TotalBindAmount.InexactFloat64(),
TotalRechargeAmount: token.TotalRechargeAmount.InexactFloat64(),
@@ -34,7 +34,6 @@ func (c *ControllerV1) GetToken(ctx context.Context, req *v1.GetTokenReq) (res *
RechargeLimitCount: token.RechargeLimitCount,
LastBindAt: token.LastBindAt,
LastUsedAt: token.LastUsedAt,
LastLoginAt: token.LastLoginAt,
Remark: token.Remark,
CreatedAt: token.CreatedAt,
UpdatedAt: token.UpdatedAt,

View File

@@ -0,0 +1,36 @@
package camel_oil
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
v1 "kami/api/camel_oil/v1"
"kami/internal/errHandler"
"kami/internal/model"
"kami/internal/service"
)
func (c *ControllerV1) InputVerificationCode(ctx context.Context, req *v1.InputVerificationCodeReq) (res *v1.InputVerificationCodeRes, err error) {
// 登录验证
_, err = service.SysAuth().LoginOnlyLogin(ctx)
if err != nil {
err = errHandler.WrapError(ctx, gcode.CodeInternalError, err, "登录校验失败")
return
}
// 构建输入结构
input := &model.CamelOilTokenLoginInput{
TokenId: req.TokenId,
Code: req.Code,
}
// 调用业务逻辑
_, err = service.CamelOil().InputVerificationCode(ctx, input)
if err != nil {
return nil, err
}
return &v1.InputVerificationCodeRes{
Message: "验证码验证成功",
}, nil
}

View File

@@ -25,7 +25,7 @@ func (c *ControllerV1) ListCardBindingsByToken(ctx context.Context, req *v1.List
bindingInfos = append(bindingInfos, v1.CardBindingInfo{
Id: binding.Id,
TokenId: binding.TokenId,
TokenName: "", // 需要通过查询获取,这里暂时留空
Name: "", // 需要通过查询获取,这里暂时留空
OrderId: binding.OrderId,
CardNumber: binding.CardNumber,
CardPassword: binding.CardPassword,

View File

@@ -6,7 +6,6 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
v1 "kami/api/camel_oil/v1"
"kami/api/commonApi"
"kami/internal/consts"
"kami/internal/errHandler"
"kami/internal/service"
)
@@ -21,7 +20,7 @@ func (c *ControllerV1) ListTokens(ctx context.Context, req *v1.ListTokensReq) (r
// 调用服务层方法,它会根据当前用户类型自动筛选数据
// 管理员可以查看所有token包括用户创建和管理员创建的
// 普通用户只能查看自己创建的token
tokens, total, err := service.CamelOil().ListTokensWithPagination(ctx, req.CommonPageReq, req.Name, int(req.Status))
tokens, total, err := service.CamelOil().ListTokensWithPagination(ctx, req.CommonPageReq, req.Name, req.Status)
if err != nil {
return nil, err
}
@@ -31,8 +30,9 @@ func (c *ControllerV1) ListTokens(ctx context.Context, req *v1.ListTokensReq) (r
Id: token.Id,
UserId: token.UserId,
Name: token.Name,
LoginToken: token.LoginToken,
Phone: token.Phone,
Status: consts.CamelOilTokenStatus(token.Status),
Status: token.Status,
BindCount: token.BindCount,
TotalBindAmount: token.TotalBindAmount.InexactFloat64(),
TotalRechargeAmount: token.TotalRechargeAmount.InexactFloat64(),
@@ -40,7 +40,6 @@ func (c *ControllerV1) ListTokens(ctx context.Context, req *v1.ListTokensReq) (r
RechargeLimitCount: token.RechargeLimitCount,
LastBindAt: token.LastBindAt,
LastUsedAt: token.LastUsedAt,
LastLoginAt: token.LastLoginAt,
Remark: token.Remark,
CreatedAt: token.CreatedAt,
UpdatedAt: token.UpdatedAt,

View File

@@ -0,0 +1,27 @@
package camel_oil
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
v1 "kami/api/camel_oil/v1"
"kami/internal/errHandler"
"kami/internal/service"
)
func (c *ControllerV1) ResendVerificationCode(ctx context.Context, req *v1.ResendVerificationCodeReq) (res *v1.ResendVerificationCodeRes, err error) {
_, err = service.SysAuth().LoginOnlyLogin(ctx)
if err != nil {
err = errHandler.WrapError(ctx, gcode.CodeInternalError, err, "登录校验失败")
return
}
err = service.CamelOil().ResendVerificationCode(ctx, req.TokenId)
if err != nil {
return nil, err
}
return &v1.ResendVerificationCodeRes{
Message: "验证码重新发送成功",
}, nil
}

View File

@@ -28,7 +28,7 @@ import (
// ====================================================================================
// CreateToken 创建 Token
func (s *sCamelOil) CreateToken(ctx context.Context, input *model.CamelOilTokenCreateInput) (tokenId int64, err error) {
func (s *sCamelOil) CreateToken(ctx context.Context, tokenName string, tokenValue string, phone string, remark string, rechargeLimitAmount float64, rechargeLimitCount int) (tokenId int64, err error) {
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
// 获取当前用户信息
@@ -45,19 +45,19 @@ func (s *sCamelOil) CreateToken(ctx context.Context, input *model.CamelOilTokenC
}
// 将 float64 转换为 decimal.Decimal 存储到数据库
rechargeLimitAmountDecimal := decimal.NewFromFloat(input.RechargeLimitAmount)
rechargeLimitAmountDecimal := decimal.NewFromFloat(rechargeLimitAmount)
result, err := m.Insert(&do.V1CamelOilToken{
UserId: userId,
Name: input.Name,
Phone: input.Phone,
Status: int(consts.CamelOilTokenStatusPendingVerification), // 待验证码状态
Name: tokenName,
Phone: phone,
Status: int(consts.CamelOilTokenStatusCodeSent),
BindCount: 0,
TotalBindAmount: decimal.Zero,
TotalRechargeAmount: decimal.Zero,
RechargeLimitAmount: rechargeLimitAmountDecimal,
RechargeLimitCount: input.RechargeLimitCount,
Remark: input.Remark,
RechargeLimitCount: rechargeLimitCount,
Remark: remark,
})
if err != nil {
@@ -65,14 +65,7 @@ func (s *sCamelOil) CreateToken(ctx context.Context, input *model.CamelOilTokenC
}
tokenId, _ = result.LastInsertId()
glog.Infof(ctx, "Token创建成功: tokenId=%d, name=%s, phone=%s, 充值金额限制=%.2f, 充值次数限制=%d", tokenId, input.Name, input.Phone, input.RechargeLimitAmount, input.RechargeLimitCount)
// 异步发送验证码
go func() {
if err := s.SendVerificationCode(ctx, tokenId, input.Phone); err != nil {
glog.Errorf(ctx, "发送验证码失败: tokenId=%d, phone=%s, error=%v", tokenId, input.Phone, err)
}
}()
glog.Infof(ctx, "Token创建成功: tokenId=%d, tokenName=%s, phone=%s, 充值金额限制=%.2f, 充值次数限制=%d", tokenId, tokenName, phone, rechargeLimitAmount, rechargeLimitCount)
return tokenId, nil
}
@@ -496,38 +489,10 @@ func (s *sCamelOil) CalculateTotalBindingAmount(ctx context.Context) (totalAmoun
return result.Total, nil
}
// ====================================================================================
// 验证码和登录相关方法
// ====================================================================================
// SendVerificationCode 发送验证码
func (s *sCamelOil) SendVerificationCode(ctx context.Context, tokenId int64, phone string) error {
client := camel_oil_api.NewClient(ctx)
success, err := client.SendCaptcha(ctx, phone)
if err != nil {
// 更新状态为登录失败
s.UpdateTokenStatus(ctx, tokenId, consts.CamelOilTokenStatusLoginFailed, "发送验证码失败")
return gerror.Wrap(err, "发送验证码失败")
}
if !success {
// 更新状态为登录失败
s.UpdateTokenStatus(ctx, tokenId, consts.CamelOilTokenStatusLoginFailed, "发送验证码失败")
return gerror.New("发送验证码失败")
}
// 更新状态为验证码已发送
s.UpdateTokenStatus(ctx, tokenId, consts.CamelOilTokenStatusCodeSent, "验证码已发送")
glog.Infof(ctx, "验证码发送成功: tokenId=%d, phone=%s", tokenId, phone)
return nil
}
// LoginToken 使用验证码登录 Token
func (s *sCamelOil) LoginToken(ctx context.Context, input *model.CamelOilTokenLoginInput) (string, error) {
// InputVerificationCode 输入验证码
func (s *sCamelOil) InputVerificationCode(ctx context.Context, req *model.CamelOilTokenLoginInput) (string, error) {
// 获取 Token 信息
token, err := s.GetTokenInfo(ctx, input.TokenId)
token, err := s.GetTokenInfo(ctx, req.TokenId)
if err != nil {
return "", gerror.Wrap(err, "查询Token信息失败")
}
@@ -536,24 +501,24 @@ func (s *sCamelOil) LoginToken(ctx context.Context, input *model.CamelOilTokenLo
return "", gerror.New("Token不存在")
}
// 检查状态是否已登录
if token.Status == int(consts.CamelOilTokenStatusAvailable) {
return "", gerror.New("Token已登录")
// 检查 Token 状态,只有验证码已发送状态的 Token 才能输入验证码
if token.Status != int(consts.CamelOilTokenStatusCodeSent) {
return "", gerror.New("Token状态不允许输入验证码")
}
client := camel_oil_api.NewClient(ctx)
// 从 Redis 获取验证码(这里简化处理,直接使用传入的验证码
loginToken, err := client.LoginWithCaptcha(ctx, token.Phone, input.Code)
// 使用验证码登录
loginToken, err := client.LoginWithCaptcha(ctx, token.Phone, req.Code)
if err != nil {
// 更新状态为登录失败
s.UpdateTokenStatus(ctx, input.TokenId, consts.CamelOilTokenStatusLoginFailed, "登录失败")
return "", gerror.Wrap(err, "登录失败")
// 更新状态为验证码验证失败
s.UpdateTokenStatus(ctx, req.TokenId, consts.CamelOilTokenStatusVerificationFailed, "验证码验证失败")
return "", gerror.Wrap(err, "验证码验证失败")
}
// 更新 Token 信息
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, input.TokenId).Update(&do.V1CamelOilToken{
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Update(&do.V1CamelOilToken{
LoginToken: loginToken,
LoginTokenExpiresAt: gtime.Now().Add(24 * 3600 * time.Second), // 24小时后过期
LastLoginAt: gtime.Now(),
@@ -565,14 +530,14 @@ func (s *sCamelOil) LoginToken(ctx context.Context, input *model.CamelOilTokenLo
return "", gerror.Wrap(err, "更新Token登录信息失败")
}
glog.Infof(ctx, "Token登录成功: tokenId=%d, phone=%s", input.TokenId, token.Phone)
glog.Infof(ctx, "验证码验证成功,Token登录: tokenId=%d, phone=%s", req.TokenId, token.Phone)
return loginToken, nil
}
// ResendVerificationCode 重验证码
func (s *sCamelOil) ResendVerificationCode(ctx context.Context, input *model.CamelOilTokenResendCodeInput) error {
// ResendVerificationCode 重新发送验证码
func (s *sCamelOil) ResendVerificationCode(ctx context.Context, tokenId int64) error {
// 获取 Token 信息
token, err := s.GetTokenInfo(ctx, input.TokenId)
token, err := s.GetTokenInfo(ctx, tokenId)
if err != nil {
return gerror.Wrap(err, "查询Token信息失败")
}
@@ -581,22 +546,42 @@ func (s *sCamelOil) ResendVerificationCode(ctx context.Context, input *model.Cam
return gerror.New("Token不存在")
}
return s.SendVerificationCode(ctx, input.TokenId, token.Phone)
}
// GetTokensForLogin 获取需要登录的 Token定时任务使用
func (s *sCamelOil) GetTokensForLogin(ctx context.Context) ([]*entity.V1CamelOilToken, error) {
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
var tokens []*entity.V1CamelOilToken
// 获取待验证码状态的 Token
err := m.Where(dao.V1CamelOilToken.Columns().Status, int(consts.CamelOilTokenStatusPendingVerification)).
Scan(&tokens)
if err != nil {
return nil, gerror.Wrap(err, "查询待登录Token失败")
// 检查 Token 状态,只有特定状态的 Token 才能重新发送验证码
allowedStatuses := []int{
int(consts.CamelOilTokenStatusPendingVerification), // 待验证码
int(consts.CamelOilTokenStatusCodeSent), // 验证码已发送
int(consts.CamelOilTokenStatusVerificationFailed), // 验证码验证失败
int(consts.CamelOilTokenStatusLoginFailed), // 登录失败
}
return tokens, nil
statusAllowed := false
for _, status := range allowedStatuses {
if token.Status == status {
statusAllowed = true
break
}
}
if !statusAllowed {
return gerror.New("Token状态不允许重新发送验证码")
}
client := camel_oil_api.NewClient(ctx)
// 发送验证码
_, err = client.SendCaptcha(ctx, token.Phone)
if err != nil {
// 更新状态为登录失败
s.UpdateTokenStatus(ctx, tokenId, consts.CamelOilTokenStatusLoginFailed, "发送验证码失败")
return gerror.Wrap(err, "发送验证码失败")
}
// 更新 Token 状态为验证码已发送
err = s.UpdateTokenStatus(ctx, tokenId, consts.CamelOilTokenStatusCodeSent, "重新发送验证码成功")
if err != nil {
return gerror.Wrap(err, "更新Token状态失败")
}
glog.Infof(ctx, "验证码重新发送成功: tokenId=%d, phone=%s", tokenId, token.Phone)
return nil
}

View File

@@ -42,3 +42,37 @@ type CamelOilTokenLoginInput struct {
type CamelOilTokenResendCodeInput struct {
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
}
// ====================================================================================
// Token 相关模型
// ====================================================================================
// CamelOilTokenCreateInput 创建 Token 输入结构
type CamelOilTokenCreateInput struct {
Name string `json:"name" v:"required" description:"名称"`
Phone string `json:"phone" v:"required" description:"绑定的手机号"`
Remark string `json:"remark" description:"备注"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" v:"required" description:"充值金额限制"`
RechargeLimitCount int `json:"rechargeLimitCount" v:"required" description:"充值次数限制"`
}
// CamelOilTokenUpdateInput 更新 Token 输入结构
type CamelOilTokenUpdateInput struct {
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
Name string `json:"name" v:"required" description:"名称"`
Phone string `json:"phone" description:"绑定的手机号"`
Remark string `json:"remark" description:"备注"`
RechargeLimitAmount float64 `json:"rechargeLimitAmount" v:"required" description:"充值金额限制"`
RechargeLimitCount int `json:"rechargeLimitCount" v:"required" description:"充值次数限制"`
}
// CamelOilTokenLoginInput Token 登录输入结构
type CamelOilTokenLoginInput struct {
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
Code string `json:"code" v:"required" description:"验证码"`
}
// CamelOilTokenResendCodeInput 重发验证码输入结构
type CamelOilTokenResendCodeInput struct {
TokenId int64 `json:"tokenId" v:"required" description:"Token ID"`
}

View File

@@ -73,15 +73,6 @@ type (
CronCardBindingTask(ctx context.Context) error
// CronCleanExpiredPrefetchOrders 清理过期的预拉取订单
CronCleanExpiredPrefetchOrders(ctx context.Context) (cleanedCount int, err error)
// CronTokenLoginTask Token 自动登录定时任务 - 由cron调度器定期调用
// 流程:检查需要发送验证码或重新登录的 Token自动处理登录流程
CronTokenLoginTask(ctx context.Context) error
// GetTokensNeedingCode 获取需要发送验证码的 Token状态为待验证码
GetTokensNeedingCode(ctx context.Context) ([]*entity.V1CamelOilToken, error)
// GetFailedLoginTokens 获取登录失败的 Token状态为登录失败
GetFailedLoginTokens(ctx context.Context) ([]*entity.V1CamelOilToken, error)
// CleanExpiredLoginTokens 清理登录 token 过期的 Token
CleanExpiredLoginTokens(ctx context.Context) (cleanedCount int, err error)
// UpdateOrderStatus 更新订单状态并记录历史
UpdateOrderStatus(ctx context.Context, orderId int64, newStatus consts.CamelOilOrderStatus, operationType consts.CamelOilOrderChangeType, rawData string, description string) (err error)
// SubmitOrder 提交订单并返回支付宝支付链接
@@ -125,7 +116,7 @@ type (
// GetCamelOilSettings 获取骆驼模块设置的辅助函数
GetCamelOilSettings(ctx context.Context) (*v1.CamelOilSettings, error)
// CreateToken 创建 Token
CreateToken(ctx context.Context, input *model.CamelOilTokenCreateInput) (tokenId int64, err error)
CreateToken(ctx context.Context, tokenName string, tokenValue string, phone string, remark string, rechargeLimitAmount float64, rechargeLimitCount int) (tokenId int64, err error)
// GetTokenInfo 获取 Token 信息
GetTokenInfo(ctx context.Context, tokenId int64) (token *entity.V1CamelOilToken, err error)
// ListTokens 列出所有可用的 Token
@@ -152,14 +143,10 @@ type (
GetTokenBindingStats(ctx context.Context, tokenId int64) (bindCount int, totalAmount decimal.Decimal, err error)
// CalculateTotalBindingAmount 计算所有 Token 的累计绑定金额
CalculateTotalBindingAmount(ctx context.Context) (totalAmount decimal.Decimal, err error)
// SendVerificationCode 发送验证码
SendVerificationCode(ctx context.Context, tokenId int64, phone string) error
// LoginToken 使用验证码登录 Token
LoginToken(ctx context.Context, input *model.CamelOilTokenLoginInput) (string, error)
// ResendVerificationCode 重发验证码
ResendVerificationCode(ctx context.Context, input *model.CamelOilTokenResendCodeInput) error
// GetTokensForLogin 获取需要登录的 Token定时任务使用
GetTokensForLogin(ctx context.Context) ([]*entity.V1CamelOilToken, error)
// InputVerificationCode 输入验证码
InputVerificationCode(ctx context.Context, req *model.CamelOilTokenLoginInput) (string, error)
// ResendVerificationCode 重新发送验证码
ResendVerificationCode(ctx context.Context, tokenId int64) error
}
)