- Add ExportOrder RPC method to camel_oil API and service interfaces - Implement service logic to query orders and generate Excel file with order data - Include card number and password fields in order export - Create HTTP handler to stream Excel file with proper headers for download - Handle token status update on frequent error ban (oneDay case) - Fix order processing query to filter by status and pay status correctly - Add new error code for one-day ban in camel_oil_api and handle in client logic - Update order model and response to include card number and password - Remove redundant logging of SendCaptcha request data in camel_oil_api client - Add access control checks on ExportOrder endpoint for authorized users only
581 lines
20 KiB
Go
581 lines
20 KiB
Go
package camel_oil
|
||
|
||
import (
|
||
"context"
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/errors/gcode"
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/os/glog"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
"github.com/shopspring/decimal"
|
||
"kami/internal/model"
|
||
"kami/utility/utils"
|
||
|
||
"kami/internal/consts"
|
||
"kami/internal/dao"
|
||
"kami/internal/model/do"
|
||
"kami/internal/model/entity"
|
||
"kami/internal/service"
|
||
"kami/utility/config"
|
||
"kami/utility/integration/camel_oil_api"
|
||
)
|
||
|
||
// ====================================================================================
|
||
// Token 管理相关方法
|
||
// ====================================================================================
|
||
|
||
// CreateToken 创建 Token
|
||
func (s *sCamelOil) CreateToken(ctx context.Context, req *model.CamelOilTokenCreateInput) (tokenId int64, err error) {
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 获取当前用户信息
|
||
_, userInfo, err := service.SysUser().GetUserFromToken(ctx)
|
||
if err != nil {
|
||
return 0, gerror.Wrap(err, "获取用户信息失败")
|
||
}
|
||
|
||
var userId string
|
||
if userInfo != nil && userInfo.Id != "" {
|
||
userId = userInfo.Id
|
||
} else {
|
||
return 0, gerror.NewCode(gcode.CodeNotAuthorized, "权限不足")
|
||
}
|
||
|
||
// 将 float64 转换为 decimal.Decimal 存储到数据库
|
||
rechargeLimitAmountDecimal := decimal.NewFromFloat(req.RechargeLimitAmount)
|
||
isOk, err := camel_oil_api.NewClient(ctx).SendCaptcha(ctx, req.Phone)
|
||
if !isOk {
|
||
return 0, gerror.NewCode(gcode.CodeBusinessValidationFailed, "验证码发送失败")
|
||
}
|
||
result, err := m.Insert(&do.V1CamelOilToken{
|
||
UserId: userId,
|
||
Name: req.Name,
|
||
Phone: req.Phone,
|
||
Status: consts.CamelOilTokenStatusCodeSent,
|
||
BindCount: 0,
|
||
TotalBindAmount: decimal.Zero,
|
||
TotalRechargeAmount: decimal.Zero,
|
||
RechargeLimitAmount: rechargeLimitAmountDecimal,
|
||
RechargeLimitCount: req.RechargeLimitCount,
|
||
Remark: req.Remark,
|
||
})
|
||
|
||
if err != nil {
|
||
return 0, gerror.Wrap(err, "创建 Token失败")
|
||
}
|
||
|
||
tokenId, _ = result.LastInsertId()
|
||
return tokenId, nil
|
||
}
|
||
|
||
// GetTokenInfo 获取 Token 信息
|
||
func (s *sCamelOil) GetTokenInfo(ctx context.Context, req *model.CamelOilTokenQueryInput) (token *entity.V1CamelOilToken, err error) {
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Scan(&token)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "查询Token信息失败")
|
||
}
|
||
if token == nil {
|
||
return nil, gerror.New("Token不存在")
|
||
}
|
||
|
||
// 权限检查:普通用户只能查看自己的token
|
||
needAuth, userInfo, err := service.SysUser().GetUserFromToken(ctx)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "获取用户信息失败")
|
||
}
|
||
|
||
// 如果是普通用户且token不属于当前用户,拒绝访问
|
||
if needAuth && userInfo != nil && userInfo.Id != "" && token.UserId != userInfo.Id {
|
||
return nil, gerror.NewCode(gcode.CodeNotAuthorized, "无权访问此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, req *model.CamelOilTokenListInput) (tokens []*entity.V1CamelOilToken, total int, err error) {
|
||
query := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 获取当前用户信息
|
||
needAuth, userInfo, _ := service.SysUser().GetUserFromToken(ctx)
|
||
|
||
// 如果是普通用户(非管理员),只能查看自己的token
|
||
if needAuth && userInfo != nil && userInfo.Id != "" {
|
||
query = query.Where(dao.V1CamelOilToken.Columns().UserId, userInfo.Id)
|
||
}
|
||
// 管理员或不需要认证的情况可以查看所有token(包括user_id为空的token)
|
||
|
||
// 添加查询条件
|
||
if req.Name != "" {
|
||
query = query.WhereLike(dao.V1CamelOilToken.Columns().Name, utils.OrmLike(req.Name))
|
||
}
|
||
|
||
if req.Status > 0 {
|
||
query = query.Where(dao.V1CamelOilToken.Columns().Status, req.Status)
|
||
}
|
||
|
||
// 获取总数
|
||
totalCount, err := query.Count()
|
||
if err != nil {
|
||
return nil, 0, gerror.Wrap(err, "查询Token总数失败")
|
||
}
|
||
|
||
// 分页查询
|
||
err = query.
|
||
Offset((req.Current - 1) * req.PageSize).
|
||
Limit(req.PageSize).
|
||
OrderDesc(dao.V1CamelOilToken.Columns().CreatedAt).
|
||
Scan(&tokens)
|
||
|
||
if err != nil {
|
||
return nil, 0, gerror.Wrap(err, "查询Token列表失败")
|
||
}
|
||
|
||
return tokens, totalCount, nil
|
||
}
|
||
|
||
// UpdateTokenInfo 修改 Token 基本信息(不包括 tokenValue)
|
||
func (s *sCamelOil) UpdateTokenInfo(ctx context.Context, req *model.CamelOilTokenUpdateInput) error {
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 权限检查:获取token信息验证所有权
|
||
var token *entity.V1CamelOilToken
|
||
err := m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Scan(&token)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "查询Token信息失败")
|
||
}
|
||
if token == nil {
|
||
return gerror.New("Token不存在")
|
||
}
|
||
|
||
// 权限检查:普通用户只能修改自己的token
|
||
needAuth, userInfo, err := service.SysUser().GetUserFromToken(ctx)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "获取用户信息失败")
|
||
}
|
||
|
||
// 如果是普通用户且token不属于当前用户,拒绝修改
|
||
if needAuth && userInfo != nil && userInfo.Id != "" && token.UserId != userInfo.Id {
|
||
return gerror.NewCode(gcode.CodeNotAuthorized, "无权修改此Token")
|
||
}
|
||
|
||
// 将 float64 转换为 decimal.Decimal 存储到数据库
|
||
rechargeLimitAmountDecimal := decimal.NewFromFloat(req.RechargeLimitAmount)
|
||
|
||
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Update(&do.V1CamelOilToken{
|
||
Name: req.Name,
|
||
Remark: req.Remark,
|
||
RechargeLimitAmount: rechargeLimitAmountDecimal,
|
||
RechargeLimitCount: req.RechargeLimitCount,
|
||
})
|
||
|
||
if err != nil {
|
||
return gerror.Wrap(err, "修改Token信息失败")
|
||
}
|
||
|
||
glog.Infof(ctx, "Token信息修改成功: tokenId=%d, tokenName=%s", req.TokenId, req.Name)
|
||
return nil
|
||
}
|
||
|
||
// DeleteToken 删除 Token(软删除)
|
||
func (s *sCamelOil) DeleteToken(ctx context.Context, req *model.CamelOilTokenDeleteInput) error {
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 权限检查:获取token信息验证所有权
|
||
var token *entity.V1CamelOilToken
|
||
err := m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Scan(&token)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "查询Token信息失败")
|
||
}
|
||
if token == nil {
|
||
return gerror.New("Token不存在")
|
||
}
|
||
|
||
// 权限检查:普通用户只能删除自己的token
|
||
needAuth, userInfo, err := service.SysUser().GetUserFromToken(ctx)
|
||
if err != nil {
|
||
return gerror.Wrap(err, "获取用户信息失败")
|
||
}
|
||
|
||
// 如果是普通用户且token不属于当前用户,拒绝删除
|
||
if needAuth && userInfo != nil && userInfo.Id != "" && token.UserId != userInfo.Id {
|
||
return gerror.NewCode(gcode.CodeNotAuthorized, "无权删除此Token")
|
||
}
|
||
|
||
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Delete()
|
||
|
||
if err != nil {
|
||
return gerror.Wrap(err, "删除Token失败")
|
||
}
|
||
|
||
glog.Infof(ctx, "Token删除成功: tokenId=%d", req.TokenId)
|
||
return nil
|
||
}
|
||
|
||
// UpdateTokenStatus 更新 Token 状态并记录日志
|
||
func (s *sCamelOil) UpdateTokenStatus(ctx context.Context, req *model.CamelOilTokenStatusUpdateInput) error {
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 获取当前 Token 信息
|
||
var token *entity.V1CamelOilToken
|
||
err := m.Where(dao.V1CamelOilToken.Columns().Id, req.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 == req.NewStatus {
|
||
return nil
|
||
}
|
||
|
||
// 更新 Token 状态
|
||
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Update(&do.V1CamelOilToken{
|
||
Status: int(req.NewStatus),
|
||
Remark: req.Remark,
|
||
})
|
||
|
||
if err != nil {
|
||
return gerror.Wrap(err, "更新Token状态失败")
|
||
}
|
||
|
||
glog.Infof(ctx, "Token状态更新成功: tokenId=%d, 原状态=%s, 新状态=%s, 备注=%s", req.TokenId, consts.CamelOilTokenStatusText[oldStatus], consts.CamelOilTokenStatusText[req.NewStatus], req.Remark)
|
||
return nil
|
||
}
|
||
|
||
// ====================================================================================
|
||
// 卡密绑定相关方法
|
||
// ====================================================================================
|
||
|
||
// BindCardToToken 绑定卡密到 Token(使用轮询算法选择 Token)
|
||
func (s *sCamelOil) BindCardToToken(ctx context.Context, req *model.CamelOilCardBindInput) (bindingId int64, err error) {
|
||
orderId := req.OrderId
|
||
cardNumber := req.CardNumber
|
||
cardPassword := req.CardPassword
|
||
amount := req.Amount
|
||
// 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 的 LoginToken)
|
||
rechargeErrType, rechargeErr := camel_oil_api.NewClient(ctx).RechargeCard(ctx, selectedToken.LoginToken, 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.RechargeCardBannedOneDay:
|
||
_ = s.UpdateTokenStatus(ctx, &model.CamelOilTokenStatusUpdateInput{
|
||
TokenId: selectedToken.Id,
|
||
NewStatus: consts.CamelOilTokenStatusDisabled,
|
||
Remark: "输错过于频繁,Token被封禁",
|
||
})
|
||
case camel_oil_api.RechargeCardErrorToken:
|
||
// Token 过期/无效:标记 Token 为已过期
|
||
glog.Warningf(ctx, "Token 过期: %v", rechargeErr)
|
||
// 调用已实现的方法更新 Token 状态
|
||
_ = s.UpdateTokenStatus(ctx, &model.CamelOilTokenStatusUpdateInput{
|
||
TokenId: selectedToken.Id,
|
||
NewStatus: consts.CamelOilTokenStatusExpired,
|
||
Remark: "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()
|
||
|
||
//给商户加宽
|
||
_, _ = service.SysUserPayment().Consumption(ctx, &model.SysUserPaymentRechargeOrConsumeInput{
|
||
UserId: selectedToken.UserId,
|
||
OrderNo: gconv.String(orderId),
|
||
Amount: amount,
|
||
Remark: "核销扣款",
|
||
TransactionType: consts.UserPaymentRecordStatusConsume,
|
||
Category: consts.CardRedeemAccountCategoryCamelOil,
|
||
}, nil)
|
||
|
||
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, req *model.CamelOilCardListInput) (bindings []*entity.V1CamelOilCardBinding, total int, err error) {
|
||
m := dao.V1CamelOilCardBinding.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
|
||
// 权限检查:获取token信息验证所有权
|
||
token, err := s.GetTokenInfo(ctx, &model.CamelOilTokenQueryInput{TokenId: req.TokenId})
|
||
if err != nil {
|
||
return nil, 0, err // GetTokenInfo已经包含权限检查
|
||
}
|
||
if token == nil {
|
||
return nil, 0, gerror.New("Token不存在")
|
||
}
|
||
|
||
count, err := m.Where(dao.V1CamelOilCardBinding.Columns().TokenId, req.TokenId).Count()
|
||
if err != nil {
|
||
return nil, 0, gerror.Wrap(err, "查询绑定记录计数失败")
|
||
}
|
||
|
||
err = m.Where(dao.V1CamelOilCardBinding.Columns().TokenId, req.TokenId).
|
||
Page(req.Current, req.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
|
||
}
|
||
|
||
// InputVerificationCode 输入验证码
|
||
func (s *sCamelOil) InputVerificationCode(ctx context.Context, req *model.CamelOilTokenLoginInput) (string, error) {
|
||
// 获取 Token 信息
|
||
token, err := s.GetTokenInfo(ctx, &model.CamelOilTokenQueryInput{TokenId: req.TokenId})
|
||
if err != nil {
|
||
return "", gerror.Wrap(err, "查询Token信息失败")
|
||
}
|
||
|
||
if token == nil {
|
||
return "", gerror.New("Token不存在")
|
||
}
|
||
|
||
// 检查 Token 状态,只有验证码已发送或验证失败的 Token 才能输入验证码
|
||
if token.Status != int(consts.CamelOilTokenStatusCodeSent) && token.Status != int(consts.CamelOilTokenStatusVerificationFailed) {
|
||
return "", gerror.New("Token状态不允许输入验证码")
|
||
}
|
||
|
||
client := camel_oil_api.NewClient(ctx)
|
||
|
||
// 使用验证码登录
|
||
loginToken, err := client.LoginWithCaptcha(ctx, token.Phone, req.Code)
|
||
if err != nil {
|
||
return "", gerror.Wrap(err, "验证码验证失败")
|
||
}
|
||
if loginToken == "" {
|
||
return "", gerror.New("验证码验证失败")
|
||
}
|
||
|
||
// 更新 Token 信息
|
||
m := dao.V1CamelOilToken.Ctx(ctx).DB(config.GetDatabaseV1())
|
||
_, err = m.Where(dao.V1CamelOilToken.Columns().Id, req.TokenId).Update(&do.V1CamelOilToken{
|
||
LoginToken: loginToken,
|
||
LoginTokenExpiresAt: gtime.Now().Add(gtime.D * 30), // 30天后过期
|
||
LastLoginAt: gtime.Now(),
|
||
Status: int(consts.CamelOilTokenStatusAvailable), // 可用状态
|
||
})
|
||
|
||
if err != nil {
|
||
return "", gerror.Wrap(err, "更新Token登录信息失败")
|
||
}
|
||
|
||
glog.Infof(ctx, "验证码验证成功,Token登录: tokenId=%d, phone=%s", req.TokenId, token.Phone)
|
||
return loginToken, nil
|
||
}
|
||
|
||
// ResendVerificationCode 重新发送验证码
|
||
func (s *sCamelOil) ResendVerificationCode(ctx context.Context, req *model.CamelOilTokenResendCodeInput) error {
|
||
// 获取 Token 信息
|
||
token, err := s.GetTokenInfo(ctx, &model.CamelOilTokenQueryInput{TokenId: req.TokenId})
|
||
if err != nil {
|
||
return gerror.Wrap(err, "查询Token信息失败")
|
||
}
|
||
|
||
if token == nil {
|
||
return gerror.New("Token不存在")
|
||
}
|
||
|
||
// 检查 Token 状态,只有特定状态的 Token 才能重新发送验证码
|
||
allowedStatuses := []int{
|
||
int(consts.CamelOilTokenStatusPendingVerification), // 待验证码
|
||
int(consts.CamelOilTokenStatusCodeSent), // 验证码已发送
|
||
int(consts.CamelOilTokenStatusVerificationFailed), // 验证码验证失败
|
||
int(consts.CamelOilTokenStatusLoginFailed), // 登录失败
|
||
}
|
||
|
||
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, &model.CamelOilTokenStatusUpdateInput{
|
||
TokenId: req.TokenId,
|
||
NewStatus: consts.CamelOilTokenStatusLoginFailed,
|
||
Remark: "发送验证码失败",
|
||
})
|
||
return gerror.Wrap(err, "发送验证码失败")
|
||
}
|
||
|
||
// 更新 Token 状态为验证码已发送
|
||
err = s.UpdateTokenStatus(ctx, &model.CamelOilTokenStatusUpdateInput{
|
||
TokenId: req.TokenId,
|
||
NewStatus: consts.CamelOilTokenStatusCodeSent,
|
||
Remark: "重新发送验证码成功",
|
||
})
|
||
if err != nil {
|
||
return gerror.Wrap(err, "更新Token状态失败")
|
||
}
|
||
|
||
glog.Infof(ctx, "验证码重新发送成功: tokenId=%d, phone=%s", req.TokenId, token.Phone)
|
||
return nil
|
||
}
|