1、自动续费

This commit is contained in:
2025-07-28 18:15:40 +08:00
parent f552188ff9
commit 426a79da49
23 changed files with 921 additions and 29 deletions

View File

@ -429,7 +429,7 @@ func (e MemberProxy) UserRenewal(c *gin.Context) {
if err != nil {
e.Logger.Error(err)
e.Error(500, nil, "server error")
e.Error(500, nil, err.Error())
return
}
@ -469,3 +469,30 @@ func (e MemberProxy) ChangeAutoRenewal(c *gin.Context) {
e.OK(nil, "success")
}
// 删除自己的代理
func (e MemberProxy) DeleteMyProxy(c *gin.Context) {
req := dto.MemberProxyDeleteMyProxyReq{}
s := service.MemberProxy{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, nil, "server error")
}
userId := user.GetUserId(c)
err = s.DeleteMyProxy(&req, userId)
if err != nil {
e.Logger.Error(err)
e.Error(500, nil, err.Error())
return
}
e.OK(nil, "success")
}

View File

@ -377,3 +377,30 @@ func (e SmsPhone) CancelNumber(c *gin.Context) {
e.OK(nil, "取消号码成功")
}
// 修改自动续费状态
func (e SmsPhone) ChangeAutoRenew(c *gin.Context) {
req := dto.SmsPhoneChangeAutoRenewReq{}
s := service.SmsPhone{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
userId := user.GetUserId(c)
code := s.ChangeAutoRenew(&req, userId)
if code != statuscode.Success {
e.Error(code, nil, statuscode.GetMsg(code, "zh"))
return
}
e.OK(nil, "修改自动续费状态成功")
}

View File

@ -0,0 +1,192 @@
package apis
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/sdk/api"
"github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth/user"
_ "github.com/go-admin-team/go-admin-core/sdk/pkg/response"
"go-admin/app/admin/models"
"go-admin/app/admin/service"
"go-admin/app/admin/service/dto"
"go-admin/common/actions"
)
type SmsRenewalLog struct {
api.Api
}
// GetPage 获取短信续期记录列表
// @Summary 获取短信续期记录列表
// @Description 获取短信续期记录列表
// @Tags 短信续期记录
// @Param type query int64 false "类型 0-长效 1-短效"
// @Param pageSize query int false "页条数"
// @Param pageIndex query int false "页码"
// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsRenewalLog}} "{"code": 200, "data": [...]}"
// @Router /api/v1/sms-renewal-log [get]
// @Security Bearer
func (e SmsRenewalLog) GetPage(c *gin.Context) {
req := dto.SmsRenewalLogGetPageReq{}
s := service.SmsRenewalLog{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
p := actions.GetPermissionFromContext(c)
list := make([]models.SmsRenewalLog, 0)
var count int64
err = s.GetPage(&req, p, &list, &count)
if err != nil {
e.Error(500, err, fmt.Sprintf("获取短信续期记录失败,\r\n失败信息 %s", err.Error()))
return
}
e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
}
// Get 获取短信续期记录
// @Summary 获取短信续期记录
// @Description 获取短信续期记录
// @Tags 短信续期记录
// @Param id path int false "id"
// @Success 200 {object} response.Response{data=models.SmsRenewalLog} "{"code": 200, "data": [...]}"
// @Router /api/v1/sms-renewal-log/{id} [get]
// @Security Bearer
func (e SmsRenewalLog) Get(c *gin.Context) {
req := dto.SmsRenewalLogGetReq{}
s := service.SmsRenewalLog{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
var object models.SmsRenewalLog
p := actions.GetPermissionFromContext(c)
err = s.Get(&req, p, &object)
if err != nil {
e.Error(500, err, fmt.Sprintf("获取短信续期记录失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK( object, "查询成功")
}
// Insert 创建短信续期记录
// @Summary 创建短信续期记录
// @Description 创建短信续期记录
// @Tags 短信续期记录
// @Accept application/json
// @Product application/json
// @Param data body dto.SmsRenewalLogInsertReq true "data"
// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}"
// @Router /api/v1/sms-renewal-log [post]
// @Security Bearer
func (e SmsRenewalLog) Insert(c *gin.Context) {
req := dto.SmsRenewalLogInsertReq{}
s := service.SmsRenewalLog{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
// 设置创建人
req.SetCreateBy(user.GetUserId(c))
err = s.Insert(&req)
if err != nil {
e.Error(500, err, fmt.Sprintf("创建短信续期记录失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK(req.GetId(), "创建成功")
}
// Update 修改短信续期记录
// @Summary 修改短信续期记录
// @Description 修改短信续期记录
// @Tags 短信续期记录
// @Accept application/json
// @Product application/json
// @Param id path int true "id"
// @Param data body dto.SmsRenewalLogUpdateReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}"
// @Router /api/v1/sms-renewal-log/{id} [put]
// @Security Bearer
func (e SmsRenewalLog) Update(c *gin.Context) {
req := dto.SmsRenewalLogUpdateReq{}
s := service.SmsRenewalLog{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
req.SetUpdateBy(user.GetUserId(c))
p := actions.GetPermissionFromContext(c)
err = s.Update(&req, p)
if err != nil {
e.Error(500, err, fmt.Sprintf("修改短信续期记录失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK( req.GetId(), "修改成功")
}
// Delete 删除短信续期记录
// @Summary 删除短信续期记录
// @Description 删除短信续期记录
// @Tags 短信续期记录
// @Param data body dto.SmsRenewalLogDeleteReq true "body"
// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}"
// @Router /api/v1/sms-renewal-log [delete]
// @Security Bearer
func (e SmsRenewalLog) Delete(c *gin.Context) {
s := service.SmsRenewalLog{}
req := dto.SmsRenewalLogDeleteReq{}
err := e.MakeContext(c).
MakeOrm().
Bind(&req).
MakeService(&s.Service).
Errors
if err != nil {
e.Logger.Error(err)
e.Error(500, err, err.Error())
return
}
// req.SetUpdateBy(user.GetUserId(c))
p := actions.GetPermissionFromContext(c)
err = s.Remove(&req, p)
if err != nil {
e.Error(500, err, fmt.Sprintf("删除短信续期记录失败,\r\n失败信息 %s", err.Error()))
return
}
e.OK( req.GetId(), "删除成功")
}

View File

@ -24,6 +24,7 @@ type SmsPhone struct {
ExpireTime *time.Time `json:"expireTime" gorm:"type:datetime;comment:过期时间"`
Actived int `json:"actived" gorm:"type:tinyint;comment:是否激活(长期租赁如果第一次没接收到验证码 则不会激活号码) 1-未激活 2-已激活 3-已失效"`
Price decimal.Decimal `json:"price" gorm:"type:decimal(10,2);comment:价格"`
AutoRenewal int `json:"autoRenewal" gorm:"type:tinyint;comment:是否自动续费 1-自动续费 2-手动续费"`
models.ModelTime
models.ControlBy
}

View File

@ -0,0 +1,35 @@
package models
import (
"time"
"go-admin/common/models"
"github.com/shopspring/decimal"
)
type SmsRenewalLog struct {
models.Model
PhoneId int `json:"phoneId" gorm:"type:bigint;comment:号码id"`
Type int `json:"type" gorm:"type:tinyint;comment:类型 0-长效 1-短效"`
UserId int `json:"userId" gorm:"type:bigint;comment:用户id"`
Amount decimal.Decimal `json:"amount" gorm:"type:decimal(10,2);comment:扣费金额"`
BeforeTime time.Time `json:"beforeTime" gorm:"type:datetime;comment:续费前过期时间"`
Period int `json:"period" gorm:"type:int;comment:时间段"`
models.ModelTime
models.ControlBy
}
func (SmsRenewalLog) TableName() string {
return "sms_renewal_log"
}
func (e *SmsRenewalLog) Generate() models.ActiveRecord {
o := *e
return &o
}
func (e *SmsRenewalLog) GetId() interface{} {
return e.Id
}

View File

@ -28,6 +28,8 @@ func registerMemberProxyRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMi
r2.POST("reset-generate-proxy", api.ResetGenerateProxy) //重置流量代理账号
r2.POST("user-renewal", api.UserRenewal) //用户续费
r2.POST("change-auto-renewal", api.ChangeAutoRenewal) //修改自动续费
r2.DELETE("my", api.DeleteMyProxy) //删除我的代理
}
r := v1.Group("/member-proxy").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())

View File

@ -25,6 +25,7 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl
r1.POST("weakUp", api.WeakUp) // 新增号码
r1.DELETE("my-number", api.DeleteMyNumber) // 删除自己的号码
r1.PUT("cancel", api.CancelNumber) //取消号码
r1.PUT("auto-renewal", api.ChangeAutoRenew) //修改自动续费状态
}
r := v1.Group("/sms-phone").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())

View File

@ -0,0 +1,27 @@
package router
import (
"github.com/gin-gonic/gin"
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
"go-admin/app/admin/apis"
"go-admin/common/middleware"
"go-admin/common/actions"
)
func init() {
routerCheckRole = append(routerCheckRole, registerSmsRenewalLogRouter)
}
// registerSmsRenewalLogRouter
func registerSmsRenewalLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
api := apis.SmsRenewalLog{}
r := v1.Group("/sms-renewal-log").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
{
r.GET("", actions.PermissionAction(), api.GetPage)
r.GET("/:id", actions.PermissionAction(), api.Get)
r.POST("", api.Insert)
r.PUT("/:id", actions.PermissionAction(), api.Update)
r.DELETE("", api.Delete)
}
}

View File

@ -249,6 +249,47 @@ func (e *CliProxyService) GetUserIpList(page int, limit int, lang, code string)
return resp.Data, nil
}
// 续期静态ip
func (e *CliProxyService) Renewal(ip string, day int) error {
url := "/api/static/renewal"
key, err := e.GetKey()
if err != nil {
e.Log.Errorf("获取key失败 %v", err)
return err
}
headers, _ := GetHeaders()
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyApiUrl, headers)
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
// 添加字段
_ = writer.WriteField("lang", "en")
_ = writer.WriteField("ip", ip)
_ = writer.WriteField("day", strconv.Itoa(day))
_ = writer.WriteField("key", key)
writer.Close()
resp := dto.CliProxyResultResp[dto.CliProxyUserListResp]{}
contentType := writer.FormDataContentType()
err = client.PostWithContentType(url, &buf, contentType, nil, &resp)
if err != nil {
return err
}
if resp.Code != 0 {
if resp.Code == 300 {
e.Log.Errorf("key 失效 code:%d", resp.Code)
return errors.New("key 失效")
}
e.Log.Errorf("请求失败 params:%v err: %v", nil, resp.Msg)
return errors.New(resp.Msg)
}
return nil
}
// 获取私钥
func (e *CliProxyService) GetKey() (string, error) {
sysConfigService := SysConfig{Service: e.Service}
@ -264,6 +305,86 @@ func (e *CliProxyService) GetKey() (string, error) {
return configResp.ConfigValue, nil
}
// 获取token
func (e *CliProxyService) GetToken() (string, error) {
val, _ := redishelper.DefaultRedis.GetString(rediskey.CliProxyTokenPrefix)
if val != "" {
resp := dto.CliProxySigninResp{}
sonic.UnmarshalString(val, &resp)
if resp.Token != "" {
return resp.Token, nil
}
}
resp, err := e.resetToken()
if err != nil {
return resp.Token, err
}
return resp.Token, nil
}
// 充值token
func (e *CliProxyService) resetToken() (dto.CliProxySigninResp, error) {
configService := SysConfig{}
configService.Orm = e.Orm
configService.Log = e.Log
configReq := dto.SysConfigByKeyReq{
ConfigKey: "proxy_dash_email",
}
configResp := dto.GetSysConfigByKEYForServiceResp{}
passwordConfigReq := dto.SysConfigByKeyReq{
ConfigKey: "proxy_dash_password",
}
passwordConfigResp := dto.GetSysConfigByKEYForServiceResp{}
if err := configService.GetWithKey(&configReq, &configResp); err != nil {
return dto.CliProxySigninResp{}, err
}
if err := configService.GetWithKey(&passwordConfigReq, &passwordConfigResp); err != nil {
return dto.CliProxySigninResp{}, err
}
resp := dto.CliProxyResultResp[dto.CliProxySigninResp]{}
api := "/v1/signin"
headers, _ := GetHeaders()
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyApiUrl, headers)
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
// 添加字段
_ = writer.WriteField("lang", "en")
_ = writer.WriteField("email", configResp.ConfigValue)
_ = writer.WriteField("pwd", passwordConfigResp.ConfigValue)
writer.Close()
contentType := writer.FormDataContentType()
err := client.PostWithContentType(api, &buf, contentType, nil, &resp)
if err != nil {
return dto.CliProxySigninResp{}, err
}
if resp.Code != 0 || resp.Data.Token == "" {
return dto.CliProxySigninResp{}, errors.New(resp.Msg)
}
val, err := sonic.MarshalString(resp.Data)
if err != nil {
return dto.CliProxySigninResp{}, err
}
if val != "" {
if err := redishelper.DefaultRedis.SetString(rediskey.CliProxyTokenPrefix, val); err != nil {
return dto.CliProxySigninResp{}, err
}
}
return resp.Data, nil
}
// 获取线路
func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerReq) ([]dto.CliProxyTrafficServerResp, error) {
api := "/v1/traffic/server"
@ -271,10 +392,10 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyApiUrl, headers)
resp := dto.CliProxyResultResp[[]dto.CliProxyTrafficServerResp]{}
key, err := e.GetKey()
token, err := e.GetToken()
if err != nil {
e.Log.Errorf("获取key失败 %v", err)
e.Log.Errorf("获取token失败 %v", err)
return nil, err
}
@ -282,7 +403,7 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR
writer := multipart.NewWriter(&buf)
// 添加字段
_ = writer.WriteField("lang", "en")
_ = writer.WriteField("key", key)
_ = writer.WriteField("token", token)
writer.Close()

View File

@ -101,3 +101,7 @@ type MemberProxyChangeAutoRenewalReq struct {
ProxyId int `json:"proxyId"`
AutoRenewal bool `json:"autoRenewal"`
}
type MemberProxyDeleteMyProxyReq struct {
Id int `json:"id"`
}

View File

@ -167,3 +167,8 @@ func (s *DeleteMyNumberReq) Validate() error {
type SmsPhoneCancelNumberReq struct {
Id int `json:"id" comment:"短信号码id"`
}
type SmsPhoneChangeAutoRenewReq struct {
Id int `json:"id" comment:"短信号码id"`
AutoRenew int `json:"autoRenew" comment:"是否自动续费 1-不自动续费 2-自动续费"`
}

View File

@ -0,0 +1,110 @@
package dto
import (
"time"
"go-admin/app/admin/models"
"go-admin/common/dto"
common "go-admin/common/models"
"github.com/shopspring/decimal"
)
type SmsRenewalLogGetPageReq struct {
dto.Pagination `search:"-"`
Type int64 `form:"type" search:"type:exact;column:type;table:sms_renewal_log" comment:"类型 0-长效 1-短效"`
SmsRenewalLogOrder
}
type SmsRenewalLogOrder struct {
Id string `form:"idOrder" search:"type:order;column:id;table:sms_renewal_log"`
PhoneId string `form:"phoneIdOrder" search:"type:order;column:phone_id;table:sms_renewal_log"`
Type string `form:"typeOrder" search:"type:order;column:type;table:sms_renewal_log"`
UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:sms_renewal_log"`
Amount string `form:"amountOrder" search:"type:order;column:amount;table:sms_renewal_log"`
BeforeTime string `form:"beforeTimeOrder" search:"type:order;column:before_time;table:sms_renewal_log"`
Period string `form:"periodOrder" search:"type:order;column:period;table:sms_renewal_log"`
CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_renewal_log"`
UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_renewal_log"`
DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_renewal_log"`
CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_renewal_log"`
UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_renewal_log"`
}
func (m *SmsRenewalLogGetPageReq) GetNeedSearch() interface{} {
return *m
}
type SmsRenewalLogInsertReq struct {
Id int `json:"-" comment:"主键id"` // 主键id
PhoneId int `json:"phoneId" comment:"号码id"`
Type int `json:"type" comment:"类型 0-长效 1-短效"`
UserId int `json:"userId" comment:"用户id"`
Amount decimal.Decimal `json:"amount" comment:"扣费金额"`
BeforeTime time.Time `json:"beforeTime" comment:"续费前过期时间"`
Period int `json:"period" comment:"时间段"`
common.ControlBy
}
func (s *SmsRenewalLogInsertReq) Generate(model *models.SmsRenewalLog) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.PhoneId = s.PhoneId
model.Type = s.Type
model.UserId = s.UserId
model.Amount = s.Amount
model.BeforeTime = s.BeforeTime
model.Period = s.Period
model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
}
func (s *SmsRenewalLogInsertReq) GetId() interface{} {
return s.Id
}
type SmsRenewalLogUpdateReq struct {
Id int `uri:"id" comment:"主键id"` // 主键id
PhoneId int `json:"phoneId" comment:"号码id"`
Type int `json:"type" comment:"类型 0-长效 1-短效"`
UserId int `json:"userId" comment:"用户id"`
Amount decimal.Decimal `json:"amount" comment:"扣费金额"`
BeforeTime time.Time `json:"beforeTime" comment:"续费前过期时间"`
Period int `json:"period" comment:"时间段"`
common.ControlBy
}
func (s *SmsRenewalLogUpdateReq) Generate(model *models.SmsRenewalLog) {
if s.Id == 0 {
model.Model = common.Model{Id: s.Id}
}
model.PhoneId = s.PhoneId
model.Type = s.Type
model.UserId = s.UserId
model.Amount = s.Amount
model.BeforeTime = s.BeforeTime
model.Period = s.Period
model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
}
func (s *SmsRenewalLogUpdateReq) GetId() interface{} {
return s.Id
}
// SmsRenewalLogGetReq 功能获取请求参数
type SmsRenewalLogGetReq struct {
Id int `uri:"id"`
}
func (s *SmsRenewalLogGetReq) GetId() interface{} {
return s.Id
}
// SmsRenewalLogDeleteReq 功能删除请求参数
type SmsRenewalLogDeleteReq struct {
Ids []int `json:"ids"`
}
func (s *SmsRenewalLogDeleteReq) GetId() interface{} {
return s.Ids
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"math/rand"
"strconv"
"time"
"github.com/go-admin-team/go-admin-core/sdk/service"
@ -20,6 +21,25 @@ type MemberProxy struct {
service.Service
}
// 删除个人代理
func (e MemberProxy) DeleteMyProxy(req *dto.MemberProxyDeleteMyProxyReq, userId int) error {
var data models.MemberProxy
if err := e.Orm.Model(&data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
e.Log.Errorf("删除代理失败 记录不存在 %v", err)
}
if data.Expired.After(time.Now()) {
return errors.New("该代理还未到期")
}
if err := e.Orm.Model(&data).Delete(&data, data.GetId()).Error; err != nil {
e.Log.Errorf("删除代理失败 err:%v", err.Error())
return errors.New("删除失败")
}
return nil
}
// 切换自动续状态
func (e MemberProxy) ChangeAutoRenewal(req *dto.MemberProxyChangeAutoRenewalReq, userId int) error {
var data models.MemberProxy
@ -64,7 +84,7 @@ func (e MemberProxy) GetAutoRenewalProxys() ([]models.MemberProxy, error) {
endDate := time.Now().Add(24 * time.Hour)
startDate := time.Now()
if err := e.Orm.Model(&models.MemberProxy{}).Where("status =1 and auto_renewal =1 and expired BETWEEN ? AND ?", startDate, endDate).Select("id,type,user_id,expired").Find(&datas).Error; err != nil {
if err := e.Orm.Model(&models.MemberProxy{}).Where("status =1 and auto_renewal =1 and expired BETWEEN ? AND ?", startDate, endDate).Select("id,type,user_id,ip,expired").Find(&datas).Error; err != nil {
return nil, err
}
return datas, nil
@ -142,12 +162,13 @@ func (e MemberProxy) GetMyProxy(req *dto.MemberProxyGetMyProxyReq, resp *[]dto.M
respItem.UserName = item.UserName
respItem.Expired = item.Expired
respItem.Status = item.Status
respItem.Password = item.Password
if item.AutoRenewal == 1 {
respItem.AutoRenewal = true
}
if cliProxyConfig.ConfigValue != "" {
if cliProxyConfig.ConfigValue != "" && item.Password == "" {
respItem.Password = cliProxyConfig.ConfigValue
}
*resp = append(*resp, respItem)
@ -264,13 +285,24 @@ func (e *MemberProxy) CreateProxy(req *dto.MemberProxyInsertReq, userId int) (de
return balance, errors.New("余额不足")
}
configService := SysConfig{Service: e.Service}
cliProxyConfig := dto.GetSysConfigByKEYForServiceResp{}
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "traffic_proxy_effective_day"}, &cliProxyConfig)
if cliProxyConfig.ConfigValue == "" {
e.Log.Error("获取动态ip默认时长失败")
return balance, errors.New("server error")
}
hour, _ := strconv.Atoi(cliProxyConfig.ConfigValue)
data.Type = 2
data.Day = 30
// data.Day = day
data.UserId = userId
data.SessionType = "Sticky IP"
data.UserName = userName
data.Status = 1
data.Expired = time.Now().AddDate(0, 0, 30)
period := time.Duration(hour) * time.Hour
data.Expired = time.Now().Add(period)
err = e.Orm.Transaction(func(tx *gorm.DB) error {
if err1 := tx.Create(&data).Error; err1 != nil {

View File

@ -31,7 +31,7 @@ func (e *MemberRenewalLog) AutoRenewal() error {
balanceService := MemberBalance{Service: e.Service}
configService := SysConfig{Service: e.Service}
configResp := dto.GetSysConfigByKEYForServiceResp{}
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "deduction_standard"}, &configResp)
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "ip_renew_deduction_standard"}, &configResp)
if configResp.ConfigValue == "" {
e.Log.Error("没有设置扣费标准,不自动续期")
@ -70,7 +70,7 @@ func (e *MemberRenewalLog) AutoRenewal() error {
}
for _, proxy := range autoProxys {
if proxy.Type == 1 {
if proxy.Type == 0 {
continue
}
@ -96,17 +96,33 @@ func (e *MemberRenewalLog) DoRenewal(proxy models.MemberProxy, amount decimal.De
renewalLog.Amount = amount
renewalLog.Day = 30
renewalLog.ProxyId = proxy.Id
cliproxyService := CliProxyService{Service: e.Service}
err := e.Orm.Transaction(func(tx *gorm.DB) error {
if err := tx.Save(&renewalLog).Error; err != nil {
return err
}
if err := tx.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =? AND balance >= ?", amount, proxy.UserId, amount).Error; err != nil {
return err
db := tx.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =? AND balance >= ?", amount, proxy.UserId, amount)
if db.RowsAffected != 1 {
e.Log.Errorf("续费扣费失败,ip:%s,err:%s", proxy.Ip, "余额不足")
return errors.New("余额不足")
}
if db.Error != nil {
e.Log.Errorf("续费扣费失败,ip:%s,err:%s", proxy.Ip, db.Error.Error())
return db.Error
}
if err := tx.Model(&proxy).Update("expired", proxy.Expired.AddDate(0, 0, 30)).Error; err != nil {
e.Log.Errorf("续费修改过期时间失败,ip:%s,err:%s", proxy.Ip, err.Error())
return err
}
if err := cliproxyService.Renewal(proxy.Ip, 30); err != nil {
e.Log.Errorf("续费失败,ip:%s,err:%s", proxy.Ip, err.Error())
return err
}
@ -119,17 +135,17 @@ func (e *MemberRenewalLog) DoRenewal(proxy models.MemberProxy, amount decimal.De
func (e *MemberRenewalLog) UserRenewal(req *dto.MemberProxyUserRenewalReq, userId int) (decimal.Decimal, error) {
configService := SysConfig{Service: e.Service}
configResp := dto.GetSysConfigByKEYForServiceResp{}
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "deduction_standard"}, &configResp)
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "ip_renew_deduction_standard"}, &configResp)
if configResp.ConfigValue == "" {
e.Log.Error("没有设置扣费标准,不自动续期")
e.Log.Error("没有设置扣费标准,无法续期")
return decimal.Zero, nil
}
amount, err := decimal.NewFromString(configResp.ConfigValue)
if err != nil {
e.Log.Error("扣费标准设置错误,不自动续期")
e.Log.Error("扣费标准设置错误,无法续期")
return decimal.Zero, errors.New("续费失败,请联系管理员")
}
@ -149,8 +165,8 @@ func (e *MemberRenewalLog) UserRenewal(req *dto.MemberProxyUserRenewalReq, userI
return balance, errors.New("代理不存在")
}
if proxy.Type == 1 {
return balance, errors.New("效ip不支持续费")
if proxy.Type == 0 {
return balance, errors.New("效ip不支持续费")
}
err = e.DoRenewal(proxy, amount)

View File

@ -208,7 +208,8 @@ func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal
smsPhone.Price = price
if req.Type == 1 {
now = now.AddDate(0, req.Period, 0)
days := req.Period * 30
now = now.AddDate(0, 0, days)
smsPhone.ExpireTime = &now
} else {
now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes))
@ -556,6 +557,135 @@ func (e *SmsPhone) CancelRental(activationId int) int {
}
}
// 自动续期
func (e SmsServices) AutoRenewal() error {
var datas []models.SmsPhone
var data models.SmsPhone
startTime := time.Now().Add(-24 * time.Hour)
endTime := time.Now().Add(24 * time.Hour)
configService := SysConfig{Service: e.Service}
configResp := dto.GetSysConfigByKEYForServiceResp{}
configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "long_number_renew_deduction_standard"}, &configResp)
if configResp.ConfigValue == "" {
e.Log.Errorf("获取续费价格失败")
return nil
}
price, _ := decimal.NewFromString(configResp.ConfigValue)
if price.IsZero() {
e.Log.Errorf("获取续费价格失败")
return nil
}
if err := e.Orm.Model(&models.SmsPhone{}).Where("auto_renewal =1 and type =1 and actived =2 and expire_time >=? and expire_time <?", startTime, endTime).Find(&datas).Error; err != nil {
return err
}
for _, item := range datas {
renewLog := models.SmsRenewalLog{}
renewLog.UserId = item.UserId
renewLog.PhoneId = item.Id
renewLog.Type = item.Type
renewLog.Amount = price
renewLog.BeforeTime = *item.ExpireTime
renewLog.Period = 30
if err := e.Orm.Transaction(func(tx *gorm.DB) error {
db := tx.Exec("UPDATE member_balance set balance = balance - ? where user_id = ? and balance >= ?", price, item.UserId, price)
if db.Error != nil {
return db.Error
}
if db.RowsAffected == 0 {
return errors.New("余额不足")
}
if err1 := tx.Create(&renewLog).Error; err1 != nil {
return errors.New("创建续费日志失败")
}
newExpireTime := item.ExpireTime.AddDate(0, 0, 30)
if err1 := tx.Model(data).Where("id =?", item.Id).Update("expire_time", newExpireTime).Error; err1 != nil {
return err1
}
return nil
}); err != nil {
e.Log.Errorf("自动续期失败:%s", err.Error())
}
}
return nil
}
// ChangeAutoRenew 修改自动续期
func (e *SmsPhone) ChangeAutoRenew(req *dto.SmsPhoneChangeAutoRenewReq, userId int) int {
var data models.SmsPhone
if err := e.Orm.Model(data).Where("id =? and user_id =?", req.Id, userId).First(&data).Error; err != nil {
e.Log.Errorf("修改自动续期 数据不存在 id:%d err:%s", req.Id, err.Error())
return statuscode.SmsNotExisted
}
// 短期租赁不支持自动续期
if data.Type == 0 {
return statuscode.SmsNotAutoRenew
}
status := false
changeAutoRenewal := 2
if req.AutoRenew == 1 {
status = true
changeAutoRenewal = 1
}
data.AutoRenewal = req.AutoRenew
code := e.ChangeAutoRenewForApi(data.ActivationId, status)
if code != statuscode.Success {
return statuscode.ServerError
}
if err := e.Orm.Model(&data).Update("auto_renewal", changeAutoRenewal).Error; err != nil {
e.Log.Errorf("修改自动续期失败 id:%d, status:%t", data.ActivationId, status)
}
return statuscode.Success
}
// ChangeAutoRenew 修改自动续期
// activationId 短信id
// status 状态
func (e *SmsPhone) ChangeAutoRenewForApi(activationId int, status bool) int {
key, err := GetApiKey(e)
if err != statuscode.Success {
e.Log.Errorf("查询sms api请求失败 %d", activationId)
return statuscode.ServerError
}
url := fmt.Sprintf("?api_key=%s&action=setAutoRenew&id=%d&value=%t", key.ConfigValue, activationId, status)
client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil)
bytes, err1 := client.GetRaw(url, nil)
if err1 != nil {
e.Log.Errorf("租赁请求失败 %s", err1.Error())
return statuscode.ServerError
}
content := string(bytes)
if content == "OK" {
return statuscode.Success
} else {
e.Log.Errorf("修改自动续期请求失败 id:%d, status:%t, %s", activationId, status, content)
return statuscode.ServerError
}
}
// GetPage 获取SmsPhone列表
func (e *SmsPhone) GetPage(c *dto.SmsPhoneGetPageReq, p *actions.DataPermission, list *[]models.SmsPhone, count *int64) error {
var err error

View File

@ -0,0 +1,109 @@
package service
import (
"errors"
"github.com/go-admin-team/go-admin-core/sdk/service"
"gorm.io/gorm"
"go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/actions"
cDto "go-admin/common/dto"
)
type SmsRenewalLog struct {
service.Service
}
// GetPage 获取SmsRenewalLog列表
func (e *SmsRenewalLog) GetPage(c *dto.SmsRenewalLogGetPageReq, p *actions.DataPermission, list *[]models.SmsRenewalLog, count *int64) error {
var err error
var data models.SmsRenewalLog
err = e.Orm.Model(&data).
Scopes(
cDto.MakeCondition(c.GetNeedSearch()),
cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
actions.Permission(data.TableName(), p),
).
Find(list).Limit(-1).Offset(-1).
Count(count).Error
if err != nil {
e.Log.Errorf("SmsRenewalLogService GetPage error:%s \r\n", err)
return err
}
return nil
}
// Get 获取SmsRenewalLog对象
func (e *SmsRenewalLog) Get(d *dto.SmsRenewalLogGetReq, p *actions.DataPermission, model *models.SmsRenewalLog) error {
var data models.SmsRenewalLog
err := e.Orm.Model(&data).
Scopes(
actions.Permission(data.TableName(), p),
).
First(model, d.GetId()).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
err = errors.New("查看对象不存在或无权查看")
e.Log.Errorf("Service GetSmsRenewalLog error:%s \r\n", err)
return err
}
if err != nil {
e.Log.Errorf("db error:%s", err)
return err
}
return nil
}
// Insert 创建SmsRenewalLog对象
func (e *SmsRenewalLog) Insert(c *dto.SmsRenewalLogInsertReq) error {
var err error
var data models.SmsRenewalLog
c.Generate(&data)
err = e.Orm.Create(&data).Error
if err != nil {
e.Log.Errorf("SmsRenewalLogService Insert error:%s \r\n", err)
return err
}
return nil
}
// Update 修改SmsRenewalLog对象
func (e *SmsRenewalLog) Update(c *dto.SmsRenewalLogUpdateReq, p *actions.DataPermission) error {
var err error
var data = models.SmsRenewalLog{}
e.Orm.Scopes(
actions.Permission(data.TableName(), p),
).First(&data, c.GetId())
c.Generate(&data)
db := e.Orm.Save(&data)
if err = db.Error; err != nil {
e.Log.Errorf("SmsRenewalLogService Save error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权更新该数据")
}
return nil
}
// Remove 删除SmsRenewalLog
func (e *SmsRenewalLog) Remove(d *dto.SmsRenewalLogDeleteReq, p *actions.DataPermission) error {
var data models.SmsRenewalLog
db := e.Orm.Model(&data).
Scopes(
actions.Permission(data.TableName(), p),
).Delete(&data, d.GetId())
if err := db.Error; err != nil {
e.Log.Errorf("Service RemoveSmsRenewalLog error:%s \r\n", err)
return err
}
if db.RowsAffected == 0 {
return errors.New("无权删除该数据")
}
return nil
}

View File

@ -76,13 +76,13 @@ func (e *SysConfig) Update(c *dto.SysConfigControl) error {
}
// if c.ConfigKey == "proxy_dash_email" || c.ConfigKey == "proxy_dash_password" {
// e.Log.Info("更新了邮箱或密码刷新token")
// proxyService := CliProxyService{Service: e.Service}
// if _, err := proxyService.resetToken(); err != nil {
// e.Log.Errorf("修改配置重置token失败:%s", err)
// }
// }
if c.ConfigKey == "proxy_dash_email" || c.ConfigKey == "proxy_dash_password" {
e.Log.Info("更新了邮箱或密码刷新token")
proxyService := CliProxyService{Service: e.Service}
if _, err := proxyService.resetToken(); err != nil {
e.Log.Errorf("修改配置重置token失败:%s", err)
}
}
return nil
}

View File

@ -13,10 +13,11 @@ func InitJob() {
"ExamplesOne": ExamplesOne{},
"TrxPaymentJob": TrxPaymentJob{},
"CliProxyTrafficsJob": CliProxyJob{},
"RenewalJob": RenewalJob{},
"ExpireProxyJob": ExpireProxyJob{},
"CleanExpiredOrderJob": CleanExpiredOrderJob{},
"SmsJob": SmsJob{},
"RenewalJob": RenewalJob{}, //长效ip定时续期
"ExpireProxyJob": ExpireProxyJob{}, //定时过期代理
"CleanExpiredOrderJob": CleanExpiredOrderJob{}, //清理过期订单
"SmsJob": SmsJob{}, //短信定时查询验证码
"SmsRenewalJob": SmsRenewalJob{}, //短信定时自动续期
// ...
}
}

View File

@ -7,6 +7,7 @@ import (
)
type RenewalJob struct{}
type SmsRenewalJob struct{}
type ExpireProxyJob struct{}
// 定时续期任务
@ -18,6 +19,15 @@ func (j RenewalJob) Exec(args interface{}) error {
return memberRenewalService.AutoRenewal()
}
// 定时短信续期任务
func (j SmsRenewalJob) Exec(args interface{}) error {
smsService := service.SmsServices{}
smsService.Orm = GetDb()
smsService.Log = logger.NewHelper(logger.DefaultLogger)
return smsService.AutoRenewal()
}
// 过期任务
func (j ExpireProxyJob) Exec(args interface{}) error {
memberRenewalService := service.MemberRenewalLog{}

View File

@ -0,0 +1,19 @@
package jobs
import (
"go-admin/config"
"testing"
)
func TestProxyJob(t *testing.T) {
initSetting()
config.ExtConfig.CliproxyUrl = "https://f.cliproxy.com"
config.ExtConfig.CliproxyApiUrl = "https://api.cliproxy.com"
config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
renewJob := RenewalJob{}
if err := renewJob.Exec(nil); err != nil {
t.Error(err)
}
}

View File

@ -14,3 +14,14 @@ func TestSendSMS(t *testing.T) {
t.Error(err)
}
}
// 短信自动续期
func TestSmsRenew(t *testing.T) {
initSetting()
config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php"
job := SmsRenewalJob{}
if err := job.Exec(nil); err != nil {
t.Error(err)
}
}

View File

@ -0,0 +1,8 @@
package global
//ConfigKey
const (
流量代理有效天数 = "traffic_proxy_effective_day"
长效号码续费标准 = "long_number_renew_deduction_standard"
长效IP续费kk扣除标准 = "ip_renew_deduction_standard"
)

View File

@ -22,6 +22,7 @@ var StatusCodeZh = map[int]string{
SmsLongNumWaitCode: "短信验证码_长效号码已唤醒",
SmsNotExisted: "号码不存在",
SmsNotExpired: "号码未过期无法删除",
SmsNotAutoRenew: "短效号码无法自动续期",
}
var StatusCodeEn = map[int]string{
@ -46,6 +47,7 @@ var StatusCodeEn = map[int]string{
SmsLongNumWaitCode: "sms code long num wake up",
SmsNotExisted: "number not exist",
SmsNotExpired: "number not expired, can not delete",
SmsNotAutoRenew: "num can not auto renew",
}
func GetMsg(code int, lang string) string {
@ -103,4 +105,6 @@ const (
SmsNotExisted = 20018
//号码未过期无法删除
SmsNotExpired = 20019
//短效号码无法自动续期
SmsNotAutoRenew = 20020
)