Files
kami_backend/internal/logic/camel_oil/order.go
danial 15e2426e85 feat(camel_oil): 新增骆驼加油账号管理模块
- 实现账号增删改查接口和逻辑
- 支持账号状态更新及状态历史记录功能
- 提供账号列表、历史和统计信息查询API
- 实现账号轮询机制,支持按使用时间轮询获取账号
- 增加账号登录流程及批量登录功能,集成接码平台和平台API
- 管理账号订单容量,支持容量检查与账号登录触发
- 提供账号池状态统计接口
- 账号历史记录查询支持多种变更类型文本展示
- 密码等敏感信息采用脱敏展示
- 完善日志记录和错误处理机制,保证业务稳定运行
2025-11-21 00:49:50 +08:00

165 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package camel_oil
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gmlock"
"github.com/gogf/gf/v2/os/gtime"
"github.com/shopspring/decimal"
"kami/utility/integration/camel_oil_api"
"kami/utility/utils"
v1 "kami/api/camel_oil/v1"
"kami/internal/consts"
"kami/internal/dao"
"kami/internal/model/do"
"kami/internal/model/entity"
"kami/utility/config"
)
// ====================================================================================
// 订单管理相关方法
// ====================================================================================
// SubmitOrder 提交订单并返回支付宝支付链接
func (s *sCamelOil) SubmitOrder(ctx context.Context, req *v1.SubmitOrderReq) (res *v1.SubmitOrderRes, err error) {
// 1. 检查可用订单容量低于50则触发账号登录任务
capacity, err := s.GetAvailableOrderCapacity(ctx)
if err != nil {
return nil, gerror.Wrap(err, "检查账号容量失败")
}
// 容量不足50触发异步登录任务
if capacity <= 50 {
g.Log().Infof(ctx, "可用订单容量不足50当前%d触发账号登录任务", capacity)
go func() {
if err = s.CheckAndTriggerAccountLogin(context.Background()); err != nil {
g.Log().Errorf(ctx, "触发账号登录任务失败:%v", err)
}
}()
}
accountCount, _ := s.GetOrderCountByStatus(ctx, consts.CamelOilAccountStatusOnline)
for i := 0; i < accountCount; i++ {
account, err := s.GetAvailableAccount(ctx)
if err != nil {
return nil, gerror.Wrap(err, "获取可用账号失败")
}
if account == nil {
return nil, gerror.New("暂无可用账号,请稍后重试")
}
platformOrderId, payId, err := camel_oil_api.NewClient().CreateOrder(ctx, account.Phone, account.Token, req.Amount)
if err != nil {
if err.Error() == "auth_error" {
_ = s.UpdateAccountStatus(ctx, account.Id, consts.CamelOilAccountStatusInvalid, consts.CamelOilAccountChangeTypeInvalidate, "账号token失效")
continue
}
return nil, err
}
// 生成系统订单号
orderNo := fmt.Sprintf("CO%s", utils.GenerateRandomUUID())
gmlock.LockFunc(fmt.Sprintf("camelSubmitOrder_%d", account.Id), func() {
// 4. 保存订单记录并更新账号使用信息(使用事务)
err = dao.V1CamelOilOrder.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 插入订单
_, err = dao.V1CamelOilOrder.Ctx(ctx).DB(config.GetDatabaseV1()).Insert(&do.V1CamelOilOrder{
OrderNo: orderNo,
MerchantOrderId: req.MerchantOrderId,
AccountId: account.Id,
AccountName: account.AccountName,
PlatformOrderNo: platformOrderId,
Amount: decimal.NewFromFloat(req.Amount),
AlipayUrl: payId,
Status: 1, // 1=待支付
PayStatus: 0, // 0=未支付
NotifyStatus: 0, // 0=未回调
NotifyCount: 0,
Attach: req.Attach,
})
if err != nil {
return gerror.Wrap(err, "保存订单记录失败")
}
_, err = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Increment(dao.V1CamelOilAccount.Columns().DailyOrderCount, 1)
if err != nil {
return gerror.Wrap(err, "更新账号使用记录失败")
}
_, err = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Increment(dao.V1CamelOilAccount.Columns().TotalOrderCount, 1)
if err != nil {
return gerror.Wrap(err, "更新账号使用记录失败")
}
// 检查账号是否达到单日限额10单
var updatedAccount *entity.V1CamelOilAccount
err = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Scan(&updatedAccount)
if err != nil {
return gerror.Wrap(err, "查询账号失败")
}
if updatedAccount.DailyOrderCount >= 10 {
// 达到限额,标记为已暂停 (status=3)
_, err = dao.V1CamelOilAccount.Ctx(ctx).DB(config.GetDatabaseV1()).
Where(dao.V1CamelOilAccount.Columns().Id, account.Id).
Update(do.V1CamelOilAccount{
Status: consts.CamelOilAccountStatusPaused, // 3=暂停
})
if err != nil {
return gerror.Wrap(err, "更新账号状态失败")
}
g.Log().Infof(ctx, "账号[%s]达到单日限额10单已暂停", account.Phone)
}
// 记录订单历史
_, err = dao.V1CamelOilOrderHistory.Ctx(ctx).DB(config.GetDatabaseV1()).Data(&do.V1CamelOilOrderHistory{
HistoryUuid: utils.GenerateRandomUUID(),
OrderNo: orderNo,
ChangeType: "create",
AccountId: account.Id,
AccountName: account.AccountName,
Remark: fmt.Sprintf("创建订单:商户订单号=%s平台订单号=%s", req.MerchantOrderId, platformOrderId),
}).Insert()
if err != nil {
g.Log().Errorf(ctx, "记录订单历史失败:%v", err)
}
// 记录账号历史
_ = s.RecordAccountHistory(ctx, account.Id, consts.CamelOilAccountChangeTypeOrderBind,
consts.CamelOilAccountStatus(account.Status), consts.CamelOilAccountStatus(account.Status),
fmt.Sprintf("绑定订单:%s", orderNo))
return nil
})
})
if err != nil {
return nil, err
}
// 5. 返回支付链接
res = &v1.SubmitOrderRes{
OrderNo: orderNo,
AlipayUrl: "",
Amount: req.Amount,
CreatedAt: gtime.Now(),
}
g.Log().Infof(ctx, "订单创建成功:商户订单号=%s平台订单号=%s账号=%s 系统订单号=%s",
req.MerchantOrderId, platformOrderId, account.Phone, orderNo)
return res, nil
}
return nil, gerror.New("暂无可用账号,请稍后重试")
}