From f552188ff96371f785558c3101da148b18aa4996 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 25 Jul 2025 17:22:34 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81sms=E5=8F=96=E6=B6=88=E5=8F=B7?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/member_proxy.go | 7 + app/admin/apis/sms_phone.go | 29 ++- app/admin/models/sms_phone.go | 28 +-- app/admin/models/sms_services.go | 19 +- app/admin/router/sms_phone.go | 1 + app/admin/service/cliproxy_server.go | 233 ++++++++++------------ app/admin/service/cliproxy_server_test.go | 57 ++++++ app/admin/service/dto/cliproxy.go | 22 +- app/admin/service/dto/sms_phone.go | 4 + app/admin/service/sms_phone.go | 129 +++++++++--- app/admin/service/sms_receive_log.go | 2 +- app/admin/service/sys_config.go | 8 + utils/utility/safe_go_helper.go | 44 ++++ 13 files changed, 393 insertions(+), 190 deletions(-) create mode 100644 app/admin/service/cliproxy_server_test.go create mode 100644 utils/utility/safe_go_helper.go diff --git a/app/admin/apis/member_proxy.go b/app/admin/apis/member_proxy.go index b283ada..1b952ea 100644 --- a/app/admin/apis/member_proxy.go +++ b/app/admin/apis/member_proxy.go @@ -392,6 +392,13 @@ func (e MemberProxy) GetIpList(c *gin.Context) { return } + if req.Lang == "zh" { + for i, v := range data { + data[i].City = v.CityZh + data[i].Country = v.CountryZh + } + } + e.OK(data, "success") } diff --git a/app/admin/apis/sms_phone.go b/app/admin/apis/sms_phone.go index 14c319a..3eb7a14 100644 --- a/app/admin/apis/sms_phone.go +++ b/app/admin/apis/sms_phone.go @@ -319,7 +319,7 @@ func (e SmsPhone) WeakUp(c *gin.Context) { e.OK(nil, "唤醒长效号码成功") } -// cancelWeakUp 取消号码 +// cancelWeakUp 删除号码 func (e SmsPhone) DeleteMyNumber(c *gin.Context) { req := dto.DeleteMyNumberReq{} s := service.SmsPhone{} @@ -350,3 +350,30 @@ func (e SmsPhone) DeleteMyNumber(c *gin.Context) { e.OK(nil, "取消号码成功") } + +// 取消号码 +func (e SmsPhone) CancelNumber(c *gin.Context) { + req := dto.SmsPhoneCancelNumberReq{} + 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.CancelNumber(&req, userId) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.OK(nil, "取消号码成功") +} diff --git a/app/admin/models/sms_phone.go b/app/admin/models/sms_phone.go index 6767811..02ebafb 100644 --- a/app/admin/models/sms_phone.go +++ b/app/admin/models/sms_phone.go @@ -3,23 +3,27 @@ package models import ( "go-admin/common/models" "time" + + "github.com/shopspring/decimal" ) type SmsPhone struct { models.Model - UserId int `json:"userId" gorm:"type:bigint;comment:用户Id"` - Service string `json:"service" gorm:"type:varchar(50);comment:sms 服务"` - ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"` - Type int `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"` - Period int `json:"period" gorm:"type:int;comment:时长(月)"` - Phone string `json:"phone" gorm:"type:varchar(30);comment:号码"` - ActivationId int `json:"activationId" gorm:"type:int;comment:激活码id"` - NewActivationId int `json:"newActivationId" gorm:"type:int;comment:新激活码id 每次获取验证码会刷新"` - MessageId int `json:"messageId" gorm:"type:int;comment:短信模板id"` - Code string `json:"code" gorm:"type:varchar(10);comment:验证码"` - Status int `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"` - ExpireTime *time.Time `json:"expireTime" gorm:"type:datetime;comment:过期时间"` + UserId int `json:"userId" gorm:"type:bigint;comment:用户Id"` + Service string `json:"service" gorm:"type:varchar(50);comment:sms 服务"` + ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"` + Type int `json:"type" gorm:"type:tinyint;comment:类型 0-短效 1-长效"` + Period int `json:"period" gorm:"type:int;comment:时长(月)"` + Phone string `json:"phone" gorm:"type:varchar(30);comment:号码"` + ActivationId int `json:"activationId" gorm:"type:int;comment:激活码id"` + NewActivationId int `json:"newActivationId" gorm:"type:int;comment:新激活码id 每次获取验证码会刷新"` + MessageId int `json:"messageId" gorm:"type:int;comment:短信模板id"` + Code string `json:"code" gorm:"type:varchar(10);comment:验证码"` + Status int `json:"status" gorm:"type:tinyint;comment:状态 1-等待验证码 2-已获取"` + 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:价格"` models.ModelTime models.ControlBy } diff --git a/app/admin/models/sms_services.go b/app/admin/models/sms_services.go index c199984..2fa6bf1 100644 --- a/app/admin/models/sms_services.go +++ b/app/admin/models/sms_services.go @@ -1,22 +1,21 @@ package models import ( - "go-admin/common/models" - ) type SmsServices struct { - models.Model - - Name string `json:"name" gorm:"type:varchar(255);comment:服务名称"` - Code string `json:"code" gorm:"type:varchar(50);comment:编码"` - models.ModelTime - models.ControlBy + models.Model + + Name string `json:"name" gorm:"type:varchar(255);comment:服务名称"` + Code string `json:"code" gorm:"type:varchar(50);comment:编码"` + ExpirationMinutes int `json:"expirationMinutes" gorm:"type:int;comment:过期时间(分钟)"` + models.ModelTime + models.ControlBy } func (SmsServices) TableName() string { - return "sms_services" + return "sms_services" } func (e *SmsServices) Generate() models.ActiveRecord { @@ -26,4 +25,4 @@ func (e *SmsServices) Generate() models.ActiveRecord { func (e *SmsServices) GetId() interface{} { return e.Id -} \ No newline at end of file +} diff --git a/app/admin/router/sms_phone.go b/app/admin/router/sms_phone.go index fd16f68..e51484d 100644 --- a/app/admin/router/sms_phone.go +++ b/app/admin/router/sms_phone.go @@ -24,6 +24,7 @@ func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl r1.GET("list", api.GetMyPage) // 获取自己的号码列表 r1.POST("weakUp", api.WeakUp) // 新增号码 r1.DELETE("my-number", api.DeleteMyNumber) // 删除自己的号码 + r1.PUT("cancel", api.CancelNumber) //取消号码 } r := v1.Group("/sms-phone").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) diff --git a/app/admin/service/cliproxy_server.go b/app/admin/service/cliproxy_server.go index 9fc6736..fb3b3d0 100644 --- a/app/admin/service/cliproxy_server.go +++ b/app/admin/service/cliproxy_server.go @@ -66,7 +66,7 @@ func (e *CliProxyService) GetTrafficInfo() ([]dto.CliProxyTraffics, error) { // 提取长效ip func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal.Decimal, error) { - api := "/v2/static/use" + api := "/api/static/buy" balanceService := MemberBalance{Service: e.Service} balance := balanceService.GetBalance(userId) @@ -91,10 +91,10 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. return balance, errors.New("余额不足") } - token, err := e.GetToken() + key, err := e.GetKey() if err != nil { - e.Log.Errorf("获取token失败 %v", err) + e.Log.Errorf("获取key失败 %v", err) return balance, err } @@ -103,13 +103,12 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. form.Set("day", strconv.Itoa(day)) form.Set("id", strconv.Itoa(req.Id)) form.Set("code", req.Code) - form.Set("lang", "en") - form.Set("token", token) + form.Set("lang", "zh") + form.Set("key", key) body := strings.NewReader(form.Encode()) - useTime := time.Now().Format("01-02 15") - resp := dto.CliProxyResultResp[interface{}]{} + resp := dto.CliProxyResultResp[dto.CliProxyBuyResp]{} client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyApiUrl, nil) err = client.PostWithContentType(api, body, "application/x-www-form-urlencoded;charset=UTF-8", nil, &resp) @@ -120,22 +119,14 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. if resp.Code != 0 { if resp.Code == 300 { - e.resetToken() - return e.UseIp(req, userId) + e.Log.Errorf("key 失效 code:%d", resp.Code) + return balance, errors.New("key 失效") } e.Log.Errorf("请求失败 params:%v err: %v", req, resp.Msg) return balance, errors.New(resp.Msg) } - //查询获取到的ip - userList, err := e.GetUserIpList(1, 10, req.Lang, req.Code) - - if err != nil { - e.Log.Errorf("获取用户ip列表失败 %v", err) - return balance, err - } - memberProxy := models.MemberProxy{} memberProxy.Area = req.Code memberProxy.Day = day @@ -143,40 +134,28 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. memberProxy.Type = 1 memberProxy.UserId = userId memberProxy.Status = 1 + memberProxy.State = resp.Data.City + memberProxy.Ip = resp.Data.Ip + memberProxy.Port = strconv.Itoa(resp.Data.Port) + memberProxy.Password = resp.Data.Password + memberProxy.UserName = resp.Data.UserName + memberProxy.Expired = time.Unix(int64(resp.Data.Expired), 0) - var count int64 - - for _, v := range userList.List { - //用提取时间来区分 - if strings.Contains(v.UseTime, useTime) && v.Code == req.Code { - err := e.Orm.Transaction(func(tx *gorm.DB) error { - if err1 := tx.Model(&models.MemberProxy{}).Where("ip =?", v.IP).Count(&count).Error; err1 != nil { - return err1 - } - - if count > 0 { - return errors.New("该ip已被使用") - } - - if err1 := tx.Exec("UPDATE member_balance SET balance = balance - ? WHERE user_id =?", price, userId).Error; err1 != nil { - return err1 - } - - memberProxy.Ip = v.IP - memberProxy.StaticId = v.ID - memberProxy.Port = strconv.Itoa(v.Port) - memberProxy.Password = v.Password - memberProxy.State = v.City - memberProxy.UserName = v.Username - memberProxy.Expired, err = time.Parse("2006-01-02 15:04", v.Expired) - - return tx.Create(&memberProxy).Error - }) - - if err == nil { - return balance.Sub(price), nil - } + err = e.Orm.Transaction(func(tx *gorm.DB) error { + if err1 := tx.Exec("UPDATE member_balance SET balance = balance - ? WHERE user_id =?", price, userId).Error; err1 != nil { + e.Log.Errorf("扣款失败 %v", err1) + return err1 } + + if err1 := tx.Create(&memberProxy).Error; err1 != nil { + e.Log.Errorf("创建ip记录失败 %v", err1) + } + return nil + }) + + if err != nil { + e.Log.Errorf("创建ip记录失败 %v", err) + return balance, err } return balance, nil @@ -184,11 +163,11 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. // 获取长效ip列表 func (e *CliProxyService) GetIps(req *dto.CliProxyIPListGetReq, userId int) ([]dto.CliProxyStaticListResp, error) { - api := "/v1/static/list" - token, err := e.GetToken() + api := "/api/static/list" + key, err := e.GetKey() if err != nil { - e.Log.Errorf("获取token失败 %v", err) + e.Log.Errorf("获取key失败 %v", err) return nil, err } @@ -208,7 +187,7 @@ func (e *CliProxyService) GetIps(req *dto.CliProxyIPListGetReq, userId int) ([]d writer := multipart.NewWriter(&buf) // 添加字段 _ = writer.WriteField("lang", req.Lang) - _ = writer.WriteField("token", token) + _ = writer.WriteField("key", key) _ = writer.WriteField("code", configResp.ConfigValue) writer.Close() @@ -224,8 +203,10 @@ func (e *CliProxyService) GetIps(req *dto.CliProxyIPListGetReq, userId int) ([]d if resp.Code == 0 { if resp.Code == 300 { - e.resetToken() - return e.GetIps(req, userId) + // e.resetToken() + // return e.GetIps(req, userId) + e.Log.Errorf("key 失效 code:%d", resp.Code) + return nil, errors.New("key 失效") } return resp.Data, nil } else { @@ -235,12 +216,12 @@ func (e *CliProxyService) GetIps(req *dto.CliProxyIPListGetReq, userId int) ([]d // 获取总的用户ip列表 func (e *CliProxyService) GetUserIpList(page int, limit int, lang, code string) (dto.CliProxyUserListResp, error) { - api := "/v2/static/useList" - token, err := e.GetToken() + api := "/api/static/useList" + key, err := e.GetKey() respList := dto.CliProxyUserListResp{} if err != nil { - e.Log.Errorf("获取token失败 %v", err) + e.Log.Errorf("获取key失败 %v", err) return respList, err } @@ -248,7 +229,7 @@ func (e *CliProxyService) GetUserIpList(page int, limit int, lang, code string) writer := multipart.NewWriter(&buf) // 添加字段 _ = writer.WriteField("lang", lang) - _ = writer.WriteField("token", token) + _ = writer.WriteField("key", key) _ = writer.WriteField("code", code) _ = writer.WriteField("page", strconv.Itoa(page)) _ = writer.WriteField("limit", strconv.Itoa(limit)) @@ -268,25 +249,19 @@ func (e *CliProxyService) GetUserIpList(page int, limit int, lang, code string) return resp.Data, 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 - } +// 获取私钥 +func (e *CliProxyService) GetKey() (string, error) { + sysConfigService := SysConfig{Service: e.Service} + configResp := dto.GetSysConfigByKEYForServiceResp{} + if err := sysConfigService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "cli_proxy_key"}, &configResp); err != nil { + return "", err } - resp, err := e.resetToken() - if err != nil { - return resp.Token, err + if configResp.ConfigValue == "" { + return "", errors.New("未配置私钥") } - return resp.Token, nil + return configResp.ConfigValue, nil } // 获取线路 @@ -296,10 +271,10 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.CliproxyApiUrl, headers) resp := dto.CliProxyResultResp[[]dto.CliProxyTrafficServerResp]{} - token, err := e.GetToken() + key, err := e.GetKey() if err != nil { - e.Log.Errorf("获取token失败 %v", err) + e.Log.Errorf("获取key失败 %v", err) return nil, err } @@ -307,7 +282,7 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR writer := multipart.NewWriter(&buf) // 添加字段 _ = writer.WriteField("lang", "en") - _ = writer.WriteField("token", token) + _ = writer.WriteField("key", key) writer.Close() @@ -320,8 +295,10 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR if resp.Code == 0 { if resp.Code == 300 { - e.resetToken() - return e.GetTrafficServer(req) + // e.resetToken() + // return e.GetTrafficServer(req) + e.Log.Errorf("key 失效 code:%d", resp.Code) + return nil, errors.New("key 失效") } return resp.Data, nil } else { @@ -329,64 +306,64 @@ func (e *CliProxyService) GetTrafficServer(req *dto.MemberProxyGetTrafficServerR } } -// 充值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 - } +// // 充值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) +// 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) +// var buf bytes.Buffer +// writer := multipart.NewWriter(&buf) +// // 添加字段 +// _ = writer.WriteField("lang", "en") +// _ = writer.WriteField("email", configResp.ConfigValue) +// _ = writer.WriteField("pwd", passwordConfigResp.ConfigValue) - writer.Close() +// writer.Close() - contentType := writer.FormDataContentType() - err := client.PostWithContentType(api, &buf, contentType, nil, &resp) +// contentType := writer.FormDataContentType() +// err := client.PostWithContentType(api, &buf, contentType, nil, &resp) - if err != nil { - return dto.CliProxySigninResp{}, err - } +// if err != nil { +// return dto.CliProxySigninResp{}, err +// } - if resp.Code != 0 || resp.Data.Token == "" { - return dto.CliProxySigninResp{}, errors.New(resp.Msg) - } +// if resp.Code != 0 || resp.Data.Token == "" { +// return dto.CliProxySigninResp{}, errors.New(resp.Msg) +// } - val, err := sonic.MarshalString(resp.Data) +// val, err := sonic.MarshalString(resp.Data) - if err != nil { - return dto.CliProxySigninResp{}, err - } +// 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 -} +// if val != "" { +// if err := redishelper.DefaultRedis.SetString(rediskey.CliProxyTokenPrefix, val); err != nil { +// return dto.CliProxySigninResp{}, err +// } +// } +// return resp.Data, nil +// } // 获取自定义header func GetHeaders() (map[string]string, error) { diff --git a/app/admin/service/cliproxy_server_test.go b/app/admin/service/cliproxy_server_test.go new file mode 100644 index 0000000..d4b788e --- /dev/null +++ b/app/admin/service/cliproxy_server_test.go @@ -0,0 +1,57 @@ +package service + +import ( + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "strconv" + "testing" + "time" + + "github.com/bytedance/sonic" + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk" + "gorm.io/gorm" +) + +func TestCliProxyServer(t *testing.T) { + initSetting() + val := `{"code":0,"data":{"ip":"38.75.201.117","port":443,"days":30,"username":"H18LP70091","password":"QjFZCG6k","country":"United States","city":"Seattle","expired":1756022300},"msg":"success"}` + resp := dto.CliProxyResultResp[dto.CliProxyBuyResp]{} + sonic.UnmarshalString(val, &resp) + + if resp.Code == 0 { + memberProxy := models.MemberProxy{} + memberProxy.Area = "US" + memberProxy.Day = 30 + memberProxy.StaticId = 10000 + memberProxy.Type = 1 + memberProxy.UserId = 1 + memberProxy.Status = 1 + memberProxy.State = resp.Data.City + memberProxy.Ip = resp.Data.Ip + memberProxy.Port = strconv.Itoa(resp.Data.Port) + memberProxy.Password = resp.Data.Password + memberProxy.UserName = resp.Data.UserName + memberProxy.Expired = time.Unix(int64(resp.Data.Expired), 0) + + db := sdk.Runtime.GetDbByKey("default") + err := db.Transaction(func(tx *gorm.DB) error { + if err1 := tx.Exec("UPDATE member_balance SET balance = balance - ? WHERE user_id =?", 5.7, 1).Error; err1 != nil { + logger.Errorf("扣款失败 %v", err1) + return err1 + } + + if err1 := tx.Create(&memberProxy).Error; err1 != nil { + logger.Errorf("创建ip记录失败 %v", err1) + } + return nil + }) + + if err != nil { + logger.Errorf("创建ip记录失败 %v", err) + + t.Fail() + } + } + +} diff --git a/app/admin/service/dto/cliproxy.go b/app/admin/service/dto/cliproxy.go index 8364838..9e59d28 100644 --- a/app/admin/service/dto/cliproxy.go +++ b/app/admin/service/dto/cliproxy.go @@ -43,6 +43,16 @@ type CliProxyResultResp[T any] struct { Msg string `json:"msg"` } +type CliProxyBuyResp struct { + Ip string `json:"ip"` + Port int `json:"port"` + Days int `json:"days"` + UserName string `json:"username"` + Password string `json:"password"` + City string `json:"city"` + Expired int `json:"expired" comment:"过期时间 秒 unix"` +} + type CliProxySigninResp struct { Token string `json:"token"` Balance decimal.Decimal `json:"balance"` @@ -50,11 +60,13 @@ type CliProxySigninResp struct { // 长效ip列表响应 type CliProxyStaticListResp struct { - City string `json:"city"` - Code string `json:"code"` - Country string `json:"country"` - Id int `json:"id"` - Ip string `json:"ip"` + City string `json:"city"` + CityZh string `json:"city_zh"` + Code string `json:"code"` + Country string `json:"country"` + CountryZh string `json:"country_zh"` + Id int `json:"id"` + Ip string `json:"ip"` } type CliProxyUserListResp struct { diff --git a/app/admin/service/dto/sms_phone.go b/app/admin/service/dto/sms_phone.go index 7ad9530..e7d6f39 100644 --- a/app/admin/service/dto/sms_phone.go +++ b/app/admin/service/dto/sms_phone.go @@ -163,3 +163,7 @@ func (s *DeleteMyNumberReq) Validate() error { return nil } + +type SmsPhoneCancelNumberReq struct { + Id int `json:"id" comment:"短信号码id"` +} diff --git a/app/admin/service/sms_phone.go b/app/admin/service/sms_phone.go index 97f80e7..124b485 100644 --- a/app/admin/service/sms_phone.go +++ b/app/admin/service/sms_phone.go @@ -24,8 +24,46 @@ type SmsPhone struct { service.Service } +// 取消等待 +func (e *SmsPhone) CancelNumber(req *dto.SmsPhoneCancelNumberReq, 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("获取短信号码失败, %s", err) + return statuscode.SmsNotExisted + } + + if data.Actived == 1 { + code := e.CancelRental(data.NewActivationId) + + if code == statuscode.Success { + err := e.Orm.Transaction(func(tx *gorm.DB) error { + if err1 := e.Orm.Model(data).Updates(map[string]interface{}{"status": 3, "code": "", "actived": "3"}).Error; err1 != nil { + e.Log.Errorf("更新短信号码失败, %s", err1) + return err1 + } + + if err1 := tx.Exec("UPDATE member_balance SET balance = balance +? WHERE user_id =?", data.Price, userId).Error; err1 != nil { + e.Log.Errorf("更新余额失败, %s", err1) + return err1 + } + + return nil + }) + + if err != nil { + e.Log.Errorf("取消租赁后修改失败, %s", err) + } + } else { + e.Log.Errorf("取消租赁失败, %s", code) + return code + } + } + + return statuscode.Success +} + // 删除断信号码 -func (e SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { +func (e *SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, 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 { @@ -33,7 +71,7 @@ func (e SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { return statuscode.AccountExisted } - if data.ExpireTime != nil && data.ExpireTime.After(time.Now()) && data.Type == 1 { + if data.ExpireTime != nil && data.ExpireTime.After(time.Now()) && data.Type == 1 && data.Actived == 2 { return statuscode.SmsNotExpired } @@ -52,7 +90,7 @@ func (e SmsPhone) DeleteMyNumber(req *dto.DeleteMyNumberReq, userId int) int { } // 唤醒长效号码 -func (e SmsPhone) WeakUp(req *dto.WeakUpReq, userId int) int { +func (e *SmsPhone) WeakUp(req *dto.WeakUpReq, userId int) int { smsPhone := models.SmsPhone{} if err := e.Orm.Model(smsPhone).Where("activation_id =? and user_id= ?", req.ActivationId, userId).First(&smsPhone).Error; err != nil { e.Log.Errorf("获取短信号码失败, %s", err) @@ -84,7 +122,7 @@ func (e SmsPhone) WeakUp(req *dto.WeakUpReq, userId int) int { } // 分页查询自己的号码列表 -func (e SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]models.SmsPhone, count *int64) int { +func (e *SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]models.SmsPhone, count *int64) int { var data models.SmsPhone if err := e.Orm.Model(data).Where("user_id =? and type =?", userId, req.Type).Scopes( @@ -102,7 +140,7 @@ func (e SmsPhone) GetMyPage(req *dto.SmsPhoneGetPageReq, userId int, phone *[]mo } // 租赁号码 -func (e SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, int) { +func (e *SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, int) { config := dto.GetSysConfigByKEYForServiceResp{} configReq := dto.SysConfigByKeyReq{} configService := SysConfig{Service: e.Service} @@ -166,10 +204,15 @@ func (e SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, smsPhone.ActivationId = activationId smsPhone.MessageId = activationId smsPhone.NewActivationId = activationId + smsPhone.Actived = 1 + smsPhone.Price = price if req.Type == 1 { now = now.AddDate(0, req.Period, 0) smsPhone.ExpireTime = &now + } else { + now = now.Add(time.Minute * time.Duration(serviceItem.ExpirationMinutes)) + smsPhone.ExpireTime = &now } smsPhone.Status = 1 @@ -179,28 +222,13 @@ func (e SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, return decimal.Zero, statuscode.ServerError } - // if req.Type == 0 { - // smsReceiveLog := models.SmsReceiveLog{ - // UserId: userId, - // MessageId: activationId, - // Service: serviceItem.Name, - // ServiceCode: serviceItem.Code, - // Phone: phone, - // Status: 1, - // } - // if err := e.Orm.Save(&smsReceiveLog).Error; err != nil { - // e.Log.Errorf("保存短信接收日志失败", err) - // return decimal.Zero, statuscode.ServerError - // } + // if req.Type == 1 { + // //保存长效号码 + // // if code := e.KeepLongTerm(smsPhone.ActivationId); code != statuscode.Success { + // // return decimal.Zero, code + // // } // } - if req.Type == 1 { - //保存长效号码 - if code := e.KeepLongTerm(smsPhone.ActivationId); code != statuscode.Success { - return decimal.Zero, code - } - } - if err := e.Orm.Exec("UPDATE member_balance SET balance = balance -? WHERE user_id =?", price, userId).Error; err != nil { e.Log.Errorf("更新余额失败", err) return decimal.Zero, statuscode.ServerError @@ -353,7 +381,7 @@ func (e *SmsPhone) SyncCodes() error { for _, item := range phones { code, codeStatus := e.GetCodeForApi(item.ActivationId) mapData := make(map[string]interface{}) - remark := "" + logMapData := make(map[string]interface{}) if codeStatus == statuscode.SmsWaitCode { continue @@ -361,10 +389,24 @@ func (e *SmsPhone) SyncCodes() error { mapData["code"] = code mapData["status"] = 2 + if item.Actived == 1 { + mapData["actived"] = 2 + } + + logMapData["code"] = code + logMapData["status"] = 2 + } else if codeStatus == statuscode.SmsNoActivation || codeStatus == statuscode.SmsCancel { mapData["code"] = code mapData["status"] = 3 - remark = statuscode.GetMsg(codeStatus, "zh") + + if item.Actived == 1 { + mapData["actived"] = 3 + } + + logMapData["code"] = code + logMapData["status"] = 3 + logMapData["remark"] = statuscode.GetMsg(codeStatus, "zh") } else { continue } @@ -374,11 +416,7 @@ func (e *SmsPhone) SyncCodes() error { continue } - if remark != "" { - mapData["remark"] = remark - } - - if err := e.Orm.Model(models.SmsReceiveLog{}).Where("message_id =?", item.MessageId).Updates(mapData).Error; err != nil { + if err := e.Orm.Model(models.SmsReceiveLog{}).Where("message_id =?", item.MessageId).Updates(logMapData).Error; err != nil { e.Log.Errorf("同步短信接收日志失败:%s", err.Error()) } } @@ -489,8 +527,33 @@ func (e *SmsPhone) KeepLongTerm(activationId int) int { e.Log.Errorf("租赁请求失败 %s", content) return statuscode.ServerError } +} - return statuscode.Success +// 取消租赁 +func (e *SmsPhone) CancelRental(activationId int) int { + key, code := GetApiKey(e) + + if code != statuscode.Success { + e.Log.Errorf("租赁api请求失败 %s") + return statuscode.ServerError + } + + url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=8", key.ConfigValue, activationId) + client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) + bytes, err := client.GetRaw(url, nil) + + if err != nil { + e.Log.Errorf("租赁请求失败 %s", err.Error()) + return statuscode.ServerError + } + content := string(bytes) + + if content == "ACCESS_CANCEL" { + return statuscode.Success + } else { + e.Log.Errorf("租赁请求失败 %s", content) + return statuscode.ServerError + } } // GetPage 获取SmsPhone列表 diff --git a/app/admin/service/sms_receive_log.go b/app/admin/service/sms_receive_log.go index f7d7687..0dce2d6 100644 --- a/app/admin/service/sms_receive_log.go +++ b/app/admin/service/sms_receive_log.go @@ -26,7 +26,7 @@ func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error { return nil } - if err := e.Orm.Model(&phoneLog).Where("new_activation_id =?", req.ActivationID).Updates(map[string]interface{}{"status": "2", "code": req.Code, "message_id": req.MessageID}).Error; err != nil { + if err := e.Orm.Model(&phoneLog).Where("new_activation_id =?", req.ActivationID).Updates(map[string]interface{}{"status": "2", "code": req.Code, "message_id": req.MessageID, "actived": 2}).Error; err != nil { e.Log.Errorf("webhook 修改phone code失败:%s \r\n", err) return err } diff --git a/app/admin/service/sys_config.go b/app/admin/service/sys_config.go index c21fe44..2323cbb 100644 --- a/app/admin/service/sys_config.go +++ b/app/admin/service/sys_config.go @@ -75,6 +75,14 @@ func (e *SysConfig) Update(c *dto.SysConfigControl) error { return errors.New("无权更新该数据") } + + // 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 } diff --git a/utils/utility/safe_go_helper.go b/utils/utility/safe_go_helper.go new file mode 100644 index 0000000..ddf7a1b --- /dev/null +++ b/utils/utility/safe_go_helper.go @@ -0,0 +1,44 @@ +package utility + +import ( + "fmt" + "runtime" + "runtime/debug" + "strings" + + "github.com/go-admin-team/go-admin-core/logger" +) + +// SafeGo 安全地启动一个 goroutine,捕获 panic +func SafeGo(fn func()) { + go func() { + defer func() { + if r := recover(); r != nil { + // 记录 Goroutine ID、panic 信息和堆栈 + logger.Error(fmt.Sprintf("Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", GetGoroutineID(), r, string(debug.Stack()))) + } + }() + fn() + }() +} + +// 获取 Goroutine ID +func GetGoroutineID() string { + buf := make([]byte, 64) + n := runtime.Stack(buf, false) + stack := string(buf[:n]) + // 提取 Goroutine ID + id := strings.Split(stack, " ")[1] + return id +} + +func SafeGoParam[T any](fn func(T), param T) { + go func() { + defer func() { + if r := recover(); r != nil { + logger.Error(fmt.Sprintf(" SafeGoParam Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", GetGoroutineID(), r, string(debug.Stack()))) + } + }() + fn(param) // 执行传入的函数 + }() +}