From 854d7bee0c5e3fa7efa863d94096adfd75128395 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Thu, 24 Jul 2025 23:34:07 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=94=B6=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/sms_phone.go | 352 +++++++++++++ app/admin/apis/sms_receive_log.go | 219 ++++++++ app/admin/apis/sms_services.go | 236 +++++++++ app/admin/models/sms_phone.go | 38 ++ app/admin/models/sms_receive_log.go | 32 ++ app/admin/models/sms_services.go | 29 ++ app/admin/router/sms_phone.go | 38 ++ app/admin/router/sms_receive_log.go | 32 ++ app/admin/router/sms_services.go | 33 ++ app/admin/service/cliproxy_server.go | 8 +- app/admin/service/dto/sms_phone.go | 165 ++++++ app/admin/service/dto/sms_receive_log.go | 118 +++++ app/admin/service/dto/sms_services.go | 94 ++++ app/admin/service/sms_phone.go | 586 ++++++++++++++++++++++ app/admin/service/sms_receive_log.go | 150 ++++++ app/admin/service/sms_receive_log_test.go | 60 +++ app/admin/service/sms_services.go | 164 ++++++ app/jobs/examples.go | 1 + app/jobs/sms_job.go | 18 + app/jobs/sms_job_test.go | 16 + common/statuscode/status_code.go | 44 ++ config/extend.go | 1 + config/settings.yml | 2 + utils/httphelper/http_helper.go | 53 +- 24 files changed, 2479 insertions(+), 10 deletions(-) create mode 100644 app/admin/apis/sms_phone.go create mode 100644 app/admin/apis/sms_receive_log.go create mode 100644 app/admin/apis/sms_services.go create mode 100644 app/admin/models/sms_phone.go create mode 100644 app/admin/models/sms_receive_log.go create mode 100644 app/admin/models/sms_services.go create mode 100644 app/admin/router/sms_phone.go create mode 100644 app/admin/router/sms_receive_log.go create mode 100644 app/admin/router/sms_services.go create mode 100644 app/admin/service/dto/sms_phone.go create mode 100644 app/admin/service/dto/sms_receive_log.go create mode 100644 app/admin/service/dto/sms_services.go create mode 100644 app/admin/service/sms_phone.go create mode 100644 app/admin/service/sms_receive_log.go create mode 100644 app/admin/service/sms_receive_log_test.go create mode 100644 app/admin/service/sms_services.go create mode 100644 app/jobs/sms_job.go create mode 100644 app/jobs/sms_job_test.go diff --git a/app/admin/apis/sms_phone.go b/app/admin/apis/sms_phone.go new file mode 100644 index 0000000..14c319a --- /dev/null +++ b/app/admin/apis/sms_phone.go @@ -0,0 +1,352 @@ +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" + "go-admin/common/statuscode" +) + +type SmsPhone struct { + api.Api +} + +// GetPage 获取号码管理-号码列表列表 +// @Summary 获取号码管理-号码列表列表 +// @Description 获取号码管理-号码列表列表 +// @Tags 号码管理-号码列表 +// @Param service query string false "sms 服务" +// @Param serviceCode query string false "服务code" +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsPhone}} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-phone [get] +// @Security Bearer +func (e SmsPhone) GetPage(c *gin.Context) { + req := dto.SmsPhoneGetPageReq{} + 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 + } + + p := actions.GetPermissionFromContext(c) + list := make([]models.SmsPhone, 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.SmsPhone} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-phone/{id} [get] +// @Security Bearer +func (e SmsPhone) Get(c *gin.Context) { + req := dto.SmsPhoneGetReq{} + 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 + } + var object models.SmsPhone + + 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.SmsPhoneInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/sms-phone [post] +// @Security Bearer +func (e SmsPhone) Insert(c *gin.Context) { + req := dto.SmsPhoneInsertReq{} + 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 + } + // 设置创建人 + 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.SmsPhoneUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/sms-phone/{id} [put] +// @Security Bearer +func (e SmsPhone) Update(c *gin.Context) { + req := dto.SmsPhoneUpdateReq{} + 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 + } + 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.SmsPhoneDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/sms-phone [delete] +// @Security Bearer +func (e SmsPhone) Delete(c *gin.Context) { + s := service.SmsPhone{} + req := dto.SmsPhoneDeleteReq{} + 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(), "删除成功") +} + +// 租赁号码 +func (e SmsPhone) GetNumber(c *gin.Context) { + req := dto.GetNumberReq{} + 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 + } + + if err := req.Validate(); err != nil { + e.Logger.Errorf("租赁号码失败,%s", err.Error()) + e.Error(500, err, "服务器错误请联系管理员") + return + } + + userId := user.GetUserId(c) + balance, code := s.GetNumber(&req, userId) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.OK(balance, "租赁号码成功") +} + +// 获取验证码 +func (e SmsPhone) GetCodeByActivationId(c *gin.Context) { + req := dto.GetCodeReq{} + 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 + } + + if len(req.ActivationIds) == 0 { + e.OK([]interface{}{}, "获取验证码成功") + return + } + + userId := user.GetUserId(c) + codeData, code := s.GetCodeByActivationId(&req, userId) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.OK(codeData, "获取验证码成功") +} + +// 获取自己的号码的列表 +func (e SmsPhone) GetMyPage(c *gin.Context) { + req := dto.SmsPhoneGetPageReq{} + 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) + list := make([]models.SmsPhone, 0) + var count int64 + + code := s.GetMyPage(&req, userId, &list, &count) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") +} + +// 唤醒长效号码 +func (e SmsPhone) WeakUp(c *gin.Context) { + req := dto.WeakUpReq{} + 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 + } + + if err := req.Validate(); err != nil { + e.Logger.Errorf("唤醒长效号码失败,%s", err.Error()) + e.Error(500, err, "服务器错误请联系管理员") + return + } + + userId := user.GetUserId(c) + code := s.WeakUp(&req, userId) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.OK(nil, "唤醒长效号码成功") +} + +// cancelWeakUp 取消号码 +func (e SmsPhone) DeleteMyNumber(c *gin.Context) { + req := dto.DeleteMyNumberReq{} + 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 + } + + if err := req.Validate(); err != nil { + e.Logger.Errorf("取消号码失败,%s", err.Error()) + e.Error(500, err, "服务器错误请联系管理员") + return + } + + userId := user.GetUserId(c) + code := s.DeleteMyNumber(&req, userId) + + if code != statuscode.Success { + e.Error(code, nil, statuscode.GetMsg(code, "zh")) + return + } + + e.OK(nil, "取消号码成功") +} diff --git a/app/admin/apis/sms_receive_log.go b/app/admin/apis/sms_receive_log.go new file mode 100644 index 0000000..47988fd --- /dev/null +++ b/app/admin/apis/sms_receive_log.go @@ -0,0 +1,219 @@ +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 SmsReceiveLog struct { + api.Api +} + +// GetPage 获取验证码获取记录列表 +// @Summary 获取验证码获取记录列表 +// @Description 获取验证码获取记录列表 +// @Tags 验证码获取记录 +// @Param service query string false "服务" +// @Param serviceCode query string false "服务code" +// @Param status query int64 false "状态 0-等待验证码 1-成功 2-失败" +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsReceiveLog}} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-receive-log [get] +// @Security Bearer +func (e SmsReceiveLog) GetPage(c *gin.Context) { + req := dto.SmsReceiveLogGetPageReq{} + s := service.SmsReceiveLog{} + 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.SmsReceiveLog, 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.SmsReceiveLog} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-receive-log/{id} [get] +// @Security Bearer +func (e SmsReceiveLog) Get(c *gin.Context) { + req := dto.SmsReceiveLogGetReq{} + s := service.SmsReceiveLog{} + 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.SmsReceiveLog + + 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.SmsReceiveLogInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/sms-receive-log [post] +// @Security Bearer +func (e SmsReceiveLog) Insert(c *gin.Context) { + req := dto.SmsReceiveLogInsertReq{} + s := service.SmsReceiveLog{} + 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.SmsReceiveLogUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/sms-receive-log/{id} [put] +// @Security Bearer +func (e SmsReceiveLog) Update(c *gin.Context) { + req := dto.SmsReceiveLogUpdateReq{} + s := service.SmsReceiveLog{} + 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.SmsReceiveLogDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/sms-receive-log [delete] +// @Security Bearer +func (e SmsReceiveLog) Delete(c *gin.Context) { + s := service.SmsReceiveLog{} + req := dto.SmsReceiveLogDeleteReq{} + 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(), "删除成功") +} + +// WebHook 接收短信发送记录回调 +func (e SmsReceiveLog) WebHook(c *gin.Context) { + req := dto.SmsReceiveWebHookReq{} + s := service.SmsReceiveLog{} + + 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 + } + + err = s.WebHook(&req) + if err != nil { + c.JSON(500, gin.H{}) + return + } + + e.OK(nil, "接收短信发送记录回调成功") +} diff --git a/app/admin/apis/sms_services.go b/app/admin/apis/sms_services.go new file mode 100644 index 0000000..cfce3b8 --- /dev/null +++ b/app/admin/apis/sms_services.go @@ -0,0 +1,236 @@ +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 SmsServices struct { + api.Api +} + +// GetPage 获取SmsServices列表 +// @Summary 获取SmsServices列表 +// @Description 获取SmsServices列表 +// @Tags SmsServices +// @Param pageSize query int false "页条数" +// @Param pageIndex query int false "页码" +// @Success 200 {object} response.Response{data=response.Page{list=[]models.SmsServices}} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-services [get] +// @Security Bearer +func (e SmsServices) GetPage(c *gin.Context) { + req := dto.SmsServicesGetPageReq{} + s := service.SmsServices{} + 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.SmsServices, 0) + var count int64 + + err = s.GetPage(&req, p, &list, &count) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取SmsServices失败,\r\n失败信息 %s", err.Error())) + return + } + + e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功") +} + +// Get 获取SmsServices +// @Summary 获取SmsServices +// @Description 获取SmsServices +// @Tags SmsServices +// @Param id path int false "id" +// @Success 200 {object} response.Response{data=models.SmsServices} "{"code": 200, "data": [...]}" +// @Router /api/v1/sms-services/{id} [get] +// @Security Bearer +func (e SmsServices) Get(c *gin.Context) { + req := dto.SmsServicesGetReq{} + s := service.SmsServices{} + 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.SmsServices + + p := actions.GetPermissionFromContext(c) + err = s.Get(&req, p, &object) + if err != nil { + e.Error(500, err, fmt.Sprintf("获取SmsServices失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(object, "查询成功") +} + +// Insert 创建SmsServices +// @Summary 创建SmsServices +// @Description 创建SmsServices +// @Tags SmsServices +// @Accept application/json +// @Product application/json +// @Param data body dto.SmsServicesInsertReq true "data" +// @Success 200 {object} response.Response "{"code": 200, "message": "添加成功"}" +// @Router /api/v1/sms-services [post] +// @Security Bearer +func (e SmsServices) Insert(c *gin.Context) { + req := dto.SmsServicesInsertReq{} + s := service.SmsServices{} + 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("创建SmsServices失败,\r\n失败信息 %s", err.Error())) + return + } + + e.OK(req.GetId(), "创建成功") +} + +// Update 修改SmsServices +// @Summary 修改SmsServices +// @Description 修改SmsServices +// @Tags SmsServices +// @Accept application/json +// @Product application/json +// @Param id path int true "id" +// @Param data body dto.SmsServicesUpdateReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "修改成功"}" +// @Router /api/v1/sms-services/{id} [put] +// @Security Bearer +func (e SmsServices) Update(c *gin.Context) { + req := dto.SmsServicesUpdateReq{} + s := service.SmsServices{} + 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("修改SmsServices失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK(req.GetId(), "修改成功") +} + +// Delete 删除SmsServices +// @Summary 删除SmsServices +// @Description 删除SmsServices +// @Tags SmsServices +// @Param data body dto.SmsServicesDeleteReq true "body" +// @Success 200 {object} response.Response "{"code": 200, "message": "删除成功"}" +// @Router /api/v1/sms-services [delete] +// @Security Bearer +func (e SmsServices) Delete(c *gin.Context) { + s := service.SmsServices{} + req := dto.SmsServicesDeleteReq{} + 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("删除SmsServices失败,\r\n失败信息 %s", err.Error())) + return + } + e.OK(req.GetId(), "删除成功") +} + +// 获取选项列表 +func (e SmsServices) GetList(c *gin.Context) { + s := service.SmsServices{} + err := e.MakeContext(c). + MakeOrm(). + MakeService(&s.Service). + Errors + if err != nil { + e.Logger.Error(err) + e.Error(500, err, err.Error()) + return + } + list := make([]dto.SmsServicesGetListResp, 0) + err = s.GetList(&list) + if err != nil { + e.Logger.Errorf("获取选项列表失败,\r\n失败信息 %s", err.Error()) + e.Error(500, nil, "服务器错误请联系管理员") + return + } + e.OK(list, "获取选项列表成功") +} + +// 获取价格 +func (e SmsServices) GetPrice(c *gin.Context) { + req := dto.SmsGetPriceReq{} + s := service.SmsServices{} + 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 + } + price, err := s.GetPrice(&req) + if err != nil { + e.Logger.Errorf("获取价格失败,\r\n失败信息 %s", err.Error()) + e.Error(500, nil, "服务器错误请联系管理员") + return + } + e.OK(price, "获取价格成功") +} diff --git a/app/admin/models/sms_phone.go b/app/admin/models/sms_phone.go new file mode 100644 index 0000000..6767811 --- /dev/null +++ b/app/admin/models/sms_phone.go @@ -0,0 +1,38 @@ +package models + +import ( + "go-admin/common/models" + "time" +) + +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:过期时间"` + models.ModelTime + models.ControlBy +} + +func (SmsPhone) TableName() string { + return "sms_phone" +} + +func (e *SmsPhone) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *SmsPhone) GetId() interface{} { + return e.Id +} diff --git a/app/admin/models/sms_receive_log.go b/app/admin/models/sms_receive_log.go new file mode 100644 index 0000000..88f3c8d --- /dev/null +++ b/app/admin/models/sms_receive_log.go @@ -0,0 +1,32 @@ +package models + +import ( + "go-admin/common/models" +) + +type SmsReceiveLog struct { + models.Model + + UserId int `json:"userId" gorm:"type:bigint;comment:用户id"` + Service string `json:"service" gorm:"type:varchar(30);comment:服务"` + ServiceCode string `json:"serviceCode" gorm:"type:varchar(30);comment:服务code"` + MessageId int `json:"messageId" gorm:"type:int;comment:短信id"` + Phone string `json:"phone" gorm:"type:varchar(30);comment:号码"` + Code string `json:"code" gorm:"type:varchar(20);comment:验证码"` + Status int `json:"status" gorm:"type:tinyint;comment:状态 0-等待验证码 1-成功 2-失败"` + models.ModelTime + models.ControlBy +} + +func (SmsReceiveLog) TableName() string { + return "sms_receive_log" +} + +func (e *SmsReceiveLog) Generate() models.ActiveRecord { + o := *e + return &o +} + +func (e *SmsReceiveLog) GetId() interface{} { + return e.Id +} diff --git a/app/admin/models/sms_services.go b/app/admin/models/sms_services.go new file mode 100644 index 0000000..c199984 --- /dev/null +++ b/app/admin/models/sms_services.go @@ -0,0 +1,29 @@ +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 +} + +func (SmsServices) TableName() string { + return "sms_services" +} + +func (e *SmsServices) Generate() models.ActiveRecord { + o := *e + return &o +} + +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 new file mode 100644 index 0000000..fd16f68 --- /dev/null +++ b/app/admin/router/sms_phone.go @@ -0,0 +1,38 @@ +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/actions" + "go-admin/common/middleware" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerSmsPhoneRouter) +} + +// registerSmsPhoneRouter +func registerSmsPhoneRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.SmsPhone{} + + r1 := v1.Group("/sms-phone").Use(authMiddleware.MiddlewareFunc()) + { + r1.POST("getNumber", api.GetNumber) //租赁号码 + r1.GET("getCodeByActivationId", api.GetCodeByActivationId) //获取验证码 + r1.GET("list", api.GetMyPage) // 获取自己的号码列表 + r1.POST("weakUp", api.WeakUp) // 新增号码 + r1.DELETE("my-number", api.DeleteMyNumber) // 删除自己的号码 + } + + r := v1.Group("/sms-phone").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) + } + +} diff --git a/app/admin/router/sms_receive_log.go b/app/admin/router/sms_receive_log.go new file mode 100644 index 0000000..1980de2 --- /dev/null +++ b/app/admin/router/sms_receive_log.go @@ -0,0 +1,32 @@ +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/actions" + "go-admin/common/middleware" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerSmsReceiveLogRouter) +} + +// registerSmsReceiveLogRouter +func registerSmsReceiveLogRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.SmsReceiveLog{} + r := v1.Group("/sms-receive-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) + } + + r1 := v1.Group("sms-receive-log") + { + r1.POST("webhook", api.WebHook) //接收短信发送记录回调 + } +} diff --git a/app/admin/router/sms_services.go b/app/admin/router/sms_services.go new file mode 100644 index 0000000..08ff5fd --- /dev/null +++ b/app/admin/router/sms_services.go @@ -0,0 +1,33 @@ +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/actions" + "go-admin/common/middleware" +) + +func init() { + routerCheckRole = append(routerCheckRole, registerSmsServicesRouter) +} + +// registerSmsServicesRouter +func registerSmsServicesRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { + api := apis.SmsServices{} + r := v1.Group("/sms-services").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) + } + + r1 := v1.Group("/sms-services").Use(authMiddleware.MiddlewareFunc()) + { + r1.GET("list", api.GetList) //获取服务列表 + r1.GET("price", api.GetPrice) //获取服务价格 + } +} diff --git a/app/admin/service/cliproxy_server.go b/app/admin/service/cliproxy_server.go index 0a7fc85..9fc6736 100644 --- a/app/admin/service/cliproxy_server.go +++ b/app/admin/service/cliproxy_server.go @@ -73,7 +73,7 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. configService := SysConfig{Service: e.Service} deductStandard := dto.GetSysConfigByKEYForServiceResp{} - configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "deduction_standard"}, &deductStandard) + configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "ip_deduction_standard"}, &deductStandard) if deductStandard.ConfigValue == "" { return decimal.Decimal{}, errors.New("server error") @@ -81,6 +81,12 @@ func (e *CliProxyService) UseIp(req *dto.CliProxyIPUseReq, userId int) (decimal. price, _ := decimal.NewFromString(deductStandard.ConfigValue) + if price.Cmp(decimal.Zero) <= 0 { + e.Log.Errorf("ip提取费用配置错误,%s", deductStandard.ConfigValue) + + return decimal.Zero, errors.New("服务器错误,请联系管理员") + } + if balance.LessThan(price) { return balance, errors.New("余额不足") } diff --git a/app/admin/service/dto/sms_phone.go b/app/admin/service/dto/sms_phone.go new file mode 100644 index 0000000..7ad9530 --- /dev/null +++ b/app/admin/service/dto/sms_phone.go @@ -0,0 +1,165 @@ +package dto + +import ( + "errors" + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" +) + +type SmsPhoneGetPageReq struct { + dto.Pagination `search:"-"` + Service string `form:"service" search:"type:exact;column:service;table:sms_phone" comment:"sms 服务"` + ServiceCode string `form:"serviceCode" search:"type:exact;column:service_code;table:sms_phone" comment:"服务code"` + Type int `form:"type" search:"-" comment:"类型 0-短效 1-长效"` + SmsPhoneOrder +} + +type SmsPhoneOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:sms_phone"` + UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:sms_phone"` + Service string `form:"serviceOrder" search:"type:order;column:service;table:sms_phone"` + ServiceCode string `form:"serviceCodeOrder" search:"type:order;column:service_code;table:sms_phone"` + TypeOrder string `form:"typeOrder" search:"type:order;column:type;table:sms_phone"` + Period string `form:"periodOrder" search:"type:order;column:period;table:sms_phone"` + Phone string `form:"phoneOrder" search:"type:order;column:phone;table:sms_phone"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_phone"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_phone"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_phone"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_phone"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_phone"` +} + +func (m *SmsPhoneGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type SmsPhoneInsertReq struct { + Id int `json:"-" comment:"主键id"` // 主键id + UserId int `json:"userId" comment:"用户Id"` + Service string `json:"service" comment:"sms 服务"` + ServiceCode string `json:"serviceCode" comment:"服务code"` + Type int `json:"type" comment:"类型 0-短效 1-长效"` + Period int `json:"period" comment:"时长(月)"` + Phone string `json:"phone" comment:"号码"` + common.ControlBy +} + +func (s *SmsPhoneInsertReq) Generate(model *models.SmsPhone) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.Service = s.Service + model.ServiceCode = s.ServiceCode + model.Type = s.Type + model.Period = s.Period + model.Phone = s.Phone + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *SmsPhoneInsertReq) GetId() interface{} { + return s.Id +} + +type SmsPhoneUpdateReq struct { + Id int `uri:"id" comment:"主键id"` // 主键id + UserId int `json:"userId" comment:"用户Id"` + Service string `json:"service" comment:"sms 服务"` + ServiceCode string `json:"serviceCode" comment:"服务code"` + Type int `json:"type" comment:"类型 0-短效 1-长效"` + Period int `json:"period" comment:"时长(月)"` + Phone string `json:"phone" comment:"号码"` + common.ControlBy +} + +func (s *SmsPhoneUpdateReq) Generate(model *models.SmsPhone) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.Service = s.Service + model.ServiceCode = s.ServiceCode + model.Type = s.Type + model.Period = s.Period + model.Phone = s.Phone + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *SmsPhoneUpdateReq) GetId() interface{} { + return s.Id +} + +// SmsPhoneGetReq 功能获取请求参数 +type SmsPhoneGetReq struct { + Id int `uri:"id"` +} + +func (s *SmsPhoneGetReq) GetId() interface{} { + return s.Id +} + +// SmsPhoneDeleteReq 功能删除请求参数 +type SmsPhoneDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *SmsPhoneDeleteReq) GetId() interface{} { + return s.Ids +} + +type GetNumberReq struct { + Type int `json:"type" form:"type" comment:"类型 0-短效 1-长效"` + ServiceCode string `json:"serviceCode" form:"serviceCode" comment:"服务code"` + Period int `json:"period" form:"period" comment:"时长(月)"` +} + +func (s *GetNumberReq) Validate() error { + if s.Type > 1 || s.Type < 0 { + return errors.New("租赁类型错误") + } + + if s.ServiceCode == "" { + return errors.New("请先选择服务") + } + + if s.Type == 1 && s.Period <= 0 { + return errors.New("长租时长不能为空") + } + + return nil +} + +type GetCodeReq struct { + ActivationIds []int `form:"activationIds"` +} + +type GetCodeResp struct { + ActivationId int `json:"activationId" comment:"激活码id"` + Code string `json:"code" comment:"验证码"` + Status int `json:"status" comment:"状态 1-等待验证码 2-已获取"` +} + +type WeakUpReq struct { + ActivationId int `json:"activationId" comment:"激活码id"` +} + +func (s *WeakUpReq) Validate() error { + if s.ActivationId <= 0 { + return errors.New("激活码id不能为空") + } + + return nil +} + +type DeleteMyNumberReq struct { + Id int `json:"id" comment:"短信号码id"` +} + +func (s *DeleteMyNumberReq) Validate() error { + if s.Id <= 0 { + return errors.New("号码不能为空") + } + + return nil +} diff --git a/app/admin/service/dto/sms_receive_log.go b/app/admin/service/dto/sms_receive_log.go new file mode 100644 index 0000000..1929c47 --- /dev/null +++ b/app/admin/service/dto/sms_receive_log.go @@ -0,0 +1,118 @@ +package dto + +import ( + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" +) + +type SmsReceiveLogGetPageReq struct { + dto.Pagination `search:"-"` + Service string `form:"service" search:"type:exact;column:service;table:sms_receive_log" comment:"服务"` + ServiceCode string `form:"serviceCode" search:"type:exact;column:service_code;table:sms_receive_log" comment:"服务code"` + Status int64 `form:"status" search:"type:exact;column:status;table:sms_receive_log" comment:"状态 0-等待验证码 1-成功 2-失败"` + SmsReceiveLogOrder +} + +type SmsReceiveLogOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:sms_receive_log"` + UserId string `form:"userIdOrder" search:"type:order;column:user_id;table:sms_receive_log"` + Service string `form:"serviceOrder" search:"type:order;column:service;table:sms_receive_log"` + ServiceCode string `form:"serviceCodeOrder" search:"type:order;column:service_code;table:sms_receive_log"` + Phone string `form:"phoneOrder" search:"type:order;column:phone;table:sms_receive_log"` + Code string `form:"codeOrder" search:"type:order;column:code;table:sms_receive_log"` + Status string `form:"statusOrder" search:"type:order;column:status;table:sms_receive_log"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_receive_log"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_receive_log"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_receive_log"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_receive_log"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_receive_log"` +} + +func (m *SmsReceiveLogGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type SmsReceiveLogInsertReq struct { + Id int `json:"-" comment:"主键id"` // 主键id + UserId int `json:"userId" comment:"用户id"` + Service string `json:"service" comment:"服务"` + ServiceCode string `json:"serviceCode" comment:"服务code"` + Phone string `json:"phone" comment:"号码"` + Code string `json:"code" comment:"验证码"` + Status int `json:"status" comment:"状态 0-等待验证码 1-成功 2-失败"` + common.ControlBy +} + +func (s *SmsReceiveLogInsertReq) Generate(model *models.SmsReceiveLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.Service = s.Service + model.ServiceCode = s.ServiceCode + model.Phone = s.Phone + model.Code = s.Code + model.Status = s.Status + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *SmsReceiveLogInsertReq) GetId() interface{} { + return s.Id +} + +type SmsReceiveLogUpdateReq struct { + Id int `uri:"id" comment:"主键id"` // 主键id + UserId int `json:"userId" comment:"用户id"` + Service string `json:"service" comment:"服务"` + ServiceCode string `json:"serviceCode" comment:"服务code"` + Phone string `json:"phone" comment:"号码"` + Code string `json:"code" comment:"验证码"` + Status int `json:"status" comment:"状态 0-等待验证码 1-成功 2-失败"` + common.ControlBy +} + +func (s *SmsReceiveLogUpdateReq) Generate(model *models.SmsReceiveLog) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.UserId = s.UserId + model.Service = s.Service + model.ServiceCode = s.ServiceCode + model.Phone = s.Phone + model.Code = s.Code + model.Status = s.Status + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *SmsReceiveLogUpdateReq) GetId() interface{} { + return s.Id +} + +// SmsReceiveLogGetReq 功能获取请求参数 +type SmsReceiveLogGetReq struct { + Id int `uri:"id"` +} + +func (s *SmsReceiveLogGetReq) GetId() interface{} { + return s.Id +} + +// SmsReceiveLogDeleteReq 功能删除请求参数 +type SmsReceiveLogDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *SmsReceiveLogDeleteReq) GetId() interface{} { + return s.Ids +} + +type SmsReceiveWebHookReq struct { + ActivationID int `json:"activationId"` + MessageID int `json:"messageId"` + Service string `json:"service"` + Text string `json:"text"` + Code string `json:"code"` + Country int `json:"country"` + ReceivedAt string `json:"receivedAt"` +} diff --git a/app/admin/service/dto/sms_services.go b/app/admin/service/dto/sms_services.go new file mode 100644 index 0000000..0c3ef91 --- /dev/null +++ b/app/admin/service/dto/sms_services.go @@ -0,0 +1,94 @@ +package dto + +import ( + "go-admin/app/admin/models" + "go-admin/common/dto" + common "go-admin/common/models" +) + +type SmsServicesGetPageReq struct { + dto.Pagination `search:"-"` + SmsServicesOrder +} + +type SmsServicesOrder struct { + Id string `form:"idOrder" search:"type:order;column:id;table:sms_services"` + Name string `form:"nameOrder" search:"type:order;column:name;table:sms_services"` + Code string `form:"codeOrder" search:"type:order;column:code;table:sms_services"` + CreatedAt string `form:"createdAtOrder" search:"type:order;column:created_at;table:sms_services"` + UpdatedAt string `form:"updatedAtOrder" search:"type:order;column:updated_at;table:sms_services"` + DeletedAt string `form:"deletedAtOrder" search:"type:order;column:deleted_at;table:sms_services"` + CreateBy string `form:"createByOrder" search:"type:order;column:create_by;table:sms_services"` + UpdateBy string `form:"updateByOrder" search:"type:order;column:update_by;table:sms_services"` +} + +func (m *SmsServicesGetPageReq) GetNeedSearch() interface{} { + return *m +} + +type SmsServicesInsertReq struct { + Id int `json:"-" comment:""` // + Name string `json:"name" comment:"服务名称"` + Code string `json:"code" comment:"编码"` + common.ControlBy +} + +func (s *SmsServicesInsertReq) Generate(model *models.SmsServices) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.Name = s.Name + model.Code = s.Code + model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的 +} + +func (s *SmsServicesInsertReq) GetId() interface{} { + return s.Id +} + +type SmsServicesUpdateReq struct { + Id int `uri:"id" comment:""` // + Name string `json:"name" comment:"服务名称"` + Code string `json:"code" comment:"编码"` + common.ControlBy +} + +func (s *SmsServicesUpdateReq) Generate(model *models.SmsServices) { + if s.Id == 0 { + model.Model = common.Model{Id: s.Id} + } + model.Name = s.Name + model.Code = s.Code + model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的 +} + +func (s *SmsServicesUpdateReq) GetId() interface{} { + return s.Id +} + +// SmsServicesGetReq 功能获取请求参数 +type SmsServicesGetReq struct { + Id int `uri:"id"` +} + +func (s *SmsServicesGetReq) GetId() interface{} { + return s.Id +} + +// SmsServicesDeleteReq 功能删除请求参数 +type SmsServicesDeleteReq struct { + Ids []int `json:"ids"` +} + +func (s *SmsServicesDeleteReq) GetId() interface{} { + return s.Ids +} + +type SmsServicesGetListResp struct { + Name string `json:"name" comment:"服务名称"` + Code string `json:"code" comment:"编码"` +} + +type SmsGetPriceReq struct { + Type int `json:"type" form:"type" comment:"类型"` +} diff --git a/app/admin/service/sms_phone.go b/app/admin/service/sms_phone.go new file mode 100644 index 0000000..97f80e7 --- /dev/null +++ b/app/admin/service/sms_phone.go @@ -0,0 +1,586 @@ +package service + +import ( + "errors" + "fmt" + "strconv" + "strings" + "time" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/shopspring/decimal" + "gorm.io/gorm" + + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" + cDto "go-admin/common/dto" + "go-admin/common/statuscode" + "go-admin/config" + "go-admin/utils/httphelper" +) + +type SmsPhone struct { + service.Service +} + +// 删除断信号码 +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 { + e.Log.Errorf("删除短信号码失败, %s", err) + return statuscode.AccountExisted + } + + if data.ExpireTime != nil && data.ExpireTime.After(time.Now()) && data.Type == 1 { + return statuscode.SmsNotExpired + } + + db := e.Orm.Model(&data).Delete(&data, data.GetId()) + + if db.Error != nil { + e.Log.Errorf("删除短信号码失败 id:%d, %s", req.Id, db.Error.Error()) + return statuscode.ServerError + } + + if db.RowsAffected == 0 { + return statuscode.ServerError + } + + return statuscode.Success +} + +// 唤醒长效号码 +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) + return statuscode.ServerError + } + + if smsPhone.Status != 1 { + newActivationId, code := e.getExtraActivation(smsPhone.ActivationId) + + if code == statuscode.ServerError { + return code + } else if code == statuscode.Success { + if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"new_activation_id": newActivationId, "status": 1, "code": ""}).Error; err != nil { + e.Log.Errorf("更新短信号码失败, %s", err) + return statuscode.ServerError + } + } else if code == statuscode.SmsLongNumWaitCode { + e.Log.Info("无须唤醒") + if err := e.Orm.Model(smsPhone).Updates(map[string]interface{}{"status": 1, "code": ""}).Error; err != nil { + e.Log.Errorf("更新短信号码失败, %s", err) + return statuscode.ServerError + } + + return statuscode.Success + } + } + + return statuscode.Success +} + +// 分页查询自己的号码列表 +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( + cDto.MakeCondition(req.GetNeedSearch()), + cDto.Paginate(req.GetPageSize(), req.GetPageIndex()), + ). + Order("id desc"). + Find(phone).Limit(-1).Offset(-1). + Count(count).Error; err != nil { + e.Log.Errorf("获取数据失败, %s", err) + return statuscode.ServerError + } + + return statuscode.Success +} + +// 租赁号码 +func (e SmsPhone) GetNumber(req *dto.GetNumberReq, userId int) (decimal.Decimal, int) { + config := dto.GetSysConfigByKEYForServiceResp{} + configReq := dto.SysConfigByKeyReq{} + configService := SysConfig{Service: e.Service} + + if req.Type == 0 { + configReq.ConfigKey = "number_fee_short_term" + } else { + configReq.ConfigKey = "number_fee_long_term" + } + err := configService.GetWithKey(&configReq, &config) + + if err != nil { + return decimal.Zero, statuscode.ServerError + } + + if config.ConfigValue == "" { + e.Log.Errorf("短期或长期租赁费用不能为空") + return decimal.Zero, statuscode.ServerError + } + + price, err := decimal.NewFromString(config.ConfigValue) + if err != nil { + e.Log.Errorf("短期或长期租赁费用格式错误") + return decimal.Zero, statuscode.ServerError + } + + smsService := SmsServices{Service: e.Service} + balanceService := MemberBalance{Service: e.Service} + balance := balanceService.GetBalance(userId) + serviceItem, err := smsService.GetByCode(req.ServiceCode) + + if err != nil { + e.Log.Errorf("短信服务报错:%v", err) + return decimal.Zero, statuscode.ServerError + } + + if serviceItem.Code == "" { + e.Log.Error("短信服务不存在") + return decimal.Zero, statuscode.ServerError + } + + if balance.LessThan(price) { + e.Log.Error("余额不足") + return decimal.Zero, statuscode.BalanceNotEnough + } + + now := time.Now() + activationId, phone, code := e.GetNumberForApi(req.Type, req.ServiceCode, price, req.Period) + + if code != statuscode.Success { + return decimal.Zero, code + } + + smsPhone := models.SmsPhone{} + smsPhone.Phone = phone + smsPhone.UserId = userId + smsPhone.Service = serviceItem.Name + smsPhone.ServiceCode = req.ServiceCode + smsPhone.Type = req.Type + smsPhone.Period = req.Period + smsPhone.ActivationId = activationId + smsPhone.MessageId = activationId + smsPhone.NewActivationId = activationId + + if req.Type == 1 { + now = now.AddDate(0, req.Period, 0) + smsPhone.ExpireTime = &now + } + + smsPhone.Status = 1 + + if err := e.Orm.Save(&smsPhone).Error; err != nil { + e.Log.Errorf("获取手机号失败", err) + 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 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 + } + + balance = balanceService.GetBalance(userId) + + return balance, statuscode.Success +} + +// GetNumberForApi 获取短期或长期租赁号码 +// getType 0-短效 1-长效 +// service 服务code +// maxPrice 最大价格 +// period 时长(月) +func (e *SmsPhone) GetNumberForApi(getType int, serviceCode string, maxPrice decimal.Decimal, period int) (int, string, int) { + acitvationId := 0 + result := "" + resultCode := statuscode.Success + + configResp, code := GetApiKey(e) + if code != statuscode.Success { + return acitvationId, result, code + } + url := fmt.Sprintf("?api_key=%s&action=getNumber&service=%s", configResp.ConfigValue, serviceCode) + + if getType == 1 { + url = fmt.Sprintf("%s&duration=%dM", url, period) + } + + client := httphelper.NewHTTPClient(10*time.Second, config.ExtConfig.DaisysmsUrl, nil) + //ACCESS_NUMBER:999999:13476711222 + bytes, err := client.GetRaw(url, nil) + + if err != nil { + e.Log.Errorf("租赁请求失败 %s", err.Error()) + return acitvationId, result, statuscode.ServerError + } + content := string(bytes) + + if strings.Contains(content, "ACCESS_NUMBER:") { + if len(strings.Split(content, ":")) < 2 { + e.Log.Errorf("租赁请求失败 %s", content) + return acitvationId, result, statuscode.ServerError + } + + acitvationId, _ = strconv.Atoi(strings.Split(content, ":")[1]) + result = strings.Split(content, ":")[2] + } else if strings.Contains(content, "MAX_PRICE_EXCEEDED:") { + e.Log.Errorf("租赁价格超过最大价格") + resultCode = statuscode.NoNumbers + } else if strings.Contains(content, "NO_NUMBERS") { + e.Log.Error("平台号码不足") + resultCode = statuscode.NoNumbers + } else if strings.Contains(content, "TOO_MANY_ACTIVE_RENTALS") { + e.Log.Error("租赁数量超过限制") + resultCode = statuscode.ServerError + } else if strings.Contains(content, "NO_MONEY") { + e.Log.Error("余额不足") + resultCode = statuscode.BalanceNotEnough + } else { + e.Log.Errorf("租赁请求失败 %s", content) + resultCode = statuscode.ServerError + } + + return acitvationId, result, resultCode +} + +// 设置租赁结束 +func (e *SmsPhone) setStatus(id int) int { + configResp, code := GetApiKey(e) + if code != statuscode.Success { + return code + } + + url := fmt.Sprintf("?api_key=%s&action=setStatus&id=%d&status=6", configResp.ConfigValue, id) + 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_ACTIVATION" { + return statuscode.Success + } else { + e.Log.Errorf("租赁请求失败 %s", content) + return statuscode.ServerError + } +} + +func GetApiKey(e *SmsPhone) (dto.GetSysConfigByKEYForServiceResp, int) { + configService := SysConfig{Service: e.Service} + configResp := dto.GetSysConfigByKEYForServiceResp{} + err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: "sms_key"}, &configResp) + + if err != nil { + e.Log.Errorf("获取短信api失败, %s", err) + return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError + } + + if configResp.ConfigValue == "" { + e.Log.Error("短信api不能为空") + return dto.GetSysConfigByKEYForServiceResp{}, statuscode.ServerError + } + return configResp, statuscode.Success +} + +func (e *SmsPhone) GetCodeByActivationId(req *dto.GetCodeReq, userId int) ([]dto.GetCodeResp, int) { + var smsPhone []models.SmsPhone + result := []dto.GetCodeResp{} + + if err := e.Orm.Model(models.SmsPhone{}).Where("user_id =? and activation_id in ?", userId, req.ActivationIds).Find(&smsPhone).Error; err != nil { + return result, statuscode.ServerError + } + + for _, item := range smsPhone { + if item.Status == 1 { + continue + } + resultItem := dto.GetCodeResp{} + resultItem.ActivationId = item.ActivationId + resultItem.Code = item.Code + resultItem.Status = item.Status + + result = append(result, resultItem) + } + + return result, statuscode.Success +} + +// 获取验证码 +func (e *SmsPhone) GetCode(req *dto.GetCodeReq, userId int) (string, int) { + code := "" + status := statuscode.ServerError + + return code, status +} + +// SyncCodes 同步短信验证码 +func (e *SmsPhone) SyncCodes() error { + var phones []models.SmsPhone + + if err := e.Orm.Model(models.SmsPhone{}).Where("status =1").Find(&phones).Error; err != nil { + return err + } + + for _, item := range phones { + code, codeStatus := e.GetCodeForApi(item.ActivationId) + mapData := make(map[string]interface{}) + remark := "" + + if codeStatus == statuscode.SmsWaitCode { + continue + } else if codeStatus == statuscode.Success { + mapData["code"] = code + mapData["status"] = 2 + + } else if codeStatus == statuscode.SmsNoActivation || codeStatus == statuscode.SmsCancel { + mapData["code"] = code + mapData["status"] = 3 + remark = statuscode.GetMsg(codeStatus, "zh") + } else { + continue + } + + if err := e.Orm.Model(&item).Updates(mapData).Error; err != nil { + e.Log.Errorf("同步短信验证码失败:%s", err.Error()) + continue + } + + if remark != "" { + mapData["remark"] = remark + } + + if err := e.Orm.Model(models.SmsReceiveLog{}).Where("message_id =?", item.MessageId).Updates(mapData).Error; err != nil { + e.Log.Errorf("同步短信接收日志失败:%s", err.Error()) + } + } + + return nil +} + +// GetCodeForApi 获取验证码 +// messageId 短信id +// return 验证码, 状态码 +func (e *SmsPhone) GetCodeForApi(messageId int) (string, int) { + result := "" + key, code := GetApiKey(e) + + if code != statuscode.Success { + return result, code + } + url := fmt.Sprintf("?api_key=%s&action=getStatus&id=%d", key.ConfigValue, messageId) + 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 result, statuscode.ServerError + } + content := string(bytes) + + if strings.Contains(content, "STATUS_OK:") && content != "STATUS_OK:KEEP" { + result = strings.Split(content, ":")[1] + } else if content == "NO_ACTIVATION" { + code = statuscode.SmsNoActivation + } else if content == "STATUS_WAIT_CODE" || content == "STATUS_OK:KEEP" { + code = statuscode.SmsWaitCode + } else if content == "STATUS_CANCEL" { + code = statuscode.SmsCancel + } else { + e.Log.Errorf("租赁请求失败 %s", content) + return result, statuscode.ServerError + } + + return result, code +} + +// getExtraActivation 获取额外的激活 +// messageId 短信id +// return 验证码, 状态码 +func (e *SmsPhone) getExtraActivation(activationId int) (int, int) { + result := 0 + key, err := GetApiKey(e) + if err != statuscode.Success { + return 0, statuscode.ServerError + } + url := fmt.Sprintf("?api_key=%s&action=getExtraActivation&activationId=%d", key.ConfigValue, activationId) + 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 0, statuscode.ServerError + } + + content := string(bytes) + + if strings.Contains(content, "ASLEEP:") { + message := strings.Split(content, ":")[1] + result, _ = strconv.Atoi(message) + + if result > 0 { + return result, statuscode.Success + } + } else if strings.Contains(content, "STATUS_OK:") { + return 0, statuscode.SmsLongNumWaitCode + } else if strings.Contains(content, "ACCESS_NUMBER:") { + message := strings.Split(content, ":")[1] + result, _ = strconv.Atoi(message) + + if result > 0 { + return result, statuscode.Success + } + } + + e.Log.Errorf("激活长效号码失败,%s", content) + + return 0, statuscode.ServerError +} + +// KeepLongTerm 长期租赁 +func (e *SmsPhone) KeepLongTerm(activationId int) int { + key, code := GetApiKey(e) + + if code != statuscode.Success { + return statuscode.ServerError + } + + url := fmt.Sprintf("?api_key=%s&action=keep&id=%d", 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 == "OK" { + return statuscode.Success + } else { + e.Log.Errorf("租赁请求失败 %s", content) + return statuscode.ServerError + } + + return statuscode.Success +} + +// GetPage 获取SmsPhone列表 +func (e *SmsPhone) GetPage(c *dto.SmsPhoneGetPageReq, p *actions.DataPermission, list *[]models.SmsPhone, count *int64) error { + var err error + var data models.SmsPhone + + 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("SmsPhoneService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取SmsPhone对象 +func (e *SmsPhone) Get(d *dto.SmsPhoneGetReq, p *actions.DataPermission, model *models.SmsPhone) error { + var data models.SmsPhone + + 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 GetSmsPhone error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建SmsPhone对象 +func (e *SmsPhone) Insert(c *dto.SmsPhoneInsertReq) error { + var err error + var data models.SmsPhone + c.Generate(&data) + err = e.Orm.Create(&data).Error + if err != nil { + e.Log.Errorf("SmsPhoneService Insert error:%s \r\n", err) + return err + } + return nil +} + +// Update 修改SmsPhone对象 +func (e *SmsPhone) Update(c *dto.SmsPhoneUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.SmsPhone{} + 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("SmsPhoneService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除SmsPhone +func (e *SmsPhone) Remove(d *dto.SmsPhoneDeleteReq, p *actions.DataPermission) error { + var data models.SmsPhone + + 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 RemoveSmsPhone error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} diff --git a/app/admin/service/sms_receive_log.go b/app/admin/service/sms_receive_log.go new file mode 100644 index 0000000..f7d7687 --- /dev/null +++ b/app/admin/service/sms_receive_log.go @@ -0,0 +1,150 @@ +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" + "go-admin/common/statuscode" +) + +type SmsReceiveLog struct { + service.Service +} + +// 收到消息web hook +func (e SmsReceiveLog) WebHook(req *dto.SmsReceiveWebHookReq) error { + var data models.SmsReceiveLog + var phoneLog models.SmsPhone + + if req.Code == "KEEP" { + 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 { + e.Log.Errorf("webhook 修改phone code失败:%s \r\n", err) + return err + } + + e.Orm.Model(&phoneLog).Where("new_activation_id =?", req.ActivationID).First(&phoneLog) + + if phoneLog.Id > 0 { + data = models.SmsReceiveLog{ + UserId: phoneLog.UserId, + Service: phoneLog.Service, + ServiceCode: phoneLog.ServiceCode, + MessageId: req.MessageID, + Phone: phoneLog.Phone, + Code: req.Code, + Status: 2, + } + if err := e.Orm.Create(&data).Error; err != nil { + e.Log.Errorf("WebHook 创建receive_log失败:%s \r\n", err) + return err + } + + phoneService := SmsPhone{Service: e.Service} + if code := phoneService.setStatus(req.ActivationID); code != statuscode.Success { + e.Log.Errorf("接受验证码回调后修改状态失败 %d", code) + } + } + + return nil +} + +// GetPage 获取SmsReceiveLog列表 +func (e *SmsReceiveLog) GetPage(c *dto.SmsReceiveLogGetPageReq, p *actions.DataPermission, list *[]models.SmsReceiveLog, count *int64) error { + var err error + var data models.SmsReceiveLog + + 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("SmsReceiveLogService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取SmsReceiveLog对象 +func (e *SmsReceiveLog) Get(d *dto.SmsReceiveLogGetReq, p *actions.DataPermission, model *models.SmsReceiveLog) error { + var data models.SmsReceiveLog + + 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 GetSmsReceiveLog error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建SmsReceiveLog对象 +func (e *SmsReceiveLog) Insert(c *dto.SmsReceiveLogInsertReq) error { + var err error + var data models.SmsReceiveLog + c.Generate(&data) + err = e.Orm.Create(&data).Error + if err != nil { + e.Log.Errorf("SmsReceiveLogService Insert error:%s \r\n", err) + return err + } + return nil +} + +// Update 修改SmsReceiveLog对象 +func (e *SmsReceiveLog) Update(c *dto.SmsReceiveLogUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.SmsReceiveLog{} + 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("SmsReceiveLogService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除SmsReceiveLog +func (e *SmsReceiveLog) Remove(d *dto.SmsReceiveLogDeleteReq, p *actions.DataPermission) error { + var data models.SmsReceiveLog + + 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 RemoveSmsReceiveLog error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} diff --git a/app/admin/service/sms_receive_log_test.go b/app/admin/service/sms_receive_log_test.go new file mode 100644 index 0000000..5b4dc59 --- /dev/null +++ b/app/admin/service/sms_receive_log_test.go @@ -0,0 +1,60 @@ +package service + +import ( + "go-admin/app/admin/service/dto" + "go-admin/config" + "go-admin/utils/redishelper" + "testing" + + "github.com/go-admin-team/go-admin-core/logger" + "github.com/go-admin-team/go-admin-core/sdk" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func initSetting() { + dsn := "root:123456@tcp(127.0.0.1:3306)/proxy_server?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" + db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + sdk.Runtime.SetDb("default", db) + config.ExtConfig.TrxGridUrl = "https://api.trongrid.io" + config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php" + redishelper.InitDefaultRedis("127.0.0.1:6379", "", 4) + redishelper.InitLockRedisConn("127.0.0.1:6379", "", "4") +} + +func TestSmsReceiveLog(t *testing.T) { + initSetting() + receiveService := SmsReceiveLog{} + receiveService.Orm = sdk.Runtime.GetDbByKey("default") + receiveService.Log = logger.NewHelper(logger.DefaultLogger) + + // receiveService. +} + +func TestSmsPhone(t *testing.T) { + initSetting() + receiveService := SmsPhone{} + receiveService.Orm = sdk.Runtime.GetDbByKey("default") + receiveService.Log = logger.NewHelper(logger.DefaultLogger) + + req := dto.GetNumberReq{ + Type: 0, + ServiceCode: "lf", + } + receiveService.GetNumber(&req, 1) +} + +// 测试获取长效号码 +func TestPeriodPhone(t *testing.T) { + initSetting() + receiveService := SmsPhone{} + receiveService.Orm = sdk.Runtime.GetDbByKey("default") + receiveService.Log = logger.NewHelper(logger.DefaultLogger) + + req := dto.GetNumberReq{ + Type: 1, + ServiceCode: "lf", + Period: 1, + } + receiveService.GetNumber(&req, 1) +} diff --git a/app/admin/service/sms_services.go b/app/admin/service/sms_services.go new file mode 100644 index 0000000..aecf4e6 --- /dev/null +++ b/app/admin/service/sms_services.go @@ -0,0 +1,164 @@ +package service + +import ( + "errors" + + "github.com/go-admin-team/go-admin-core/sdk/service" + "github.com/shopspring/decimal" + "gorm.io/gorm" + + "go-admin/app/admin/models" + "go-admin/app/admin/service/dto" + "go-admin/common/actions" + cDto "go-admin/common/dto" +) + +type SmsServices struct { + service.Service +} + +// GetPrice 获取SmsServices价格 +func (e SmsServices) GetPrice(req *dto.SmsGetPriceReq) (decimal.Decimal, error) { + key := "number_fee_short_term" + + if req.Type == 1 { + key = "number_fee_long_term" + } + + var price decimal.Decimal + configService := SysConfig{Service: e.Service} + configResp := dto.GetSysConfigByKEYForServiceResp{} + err := configService.GetWithKey(&dto.SysConfigByKeyReq{ConfigKey: key}, &configResp) + + if err != nil { + return price, err + } + + price, err = decimal.NewFromString(configResp.ConfigValue) + + if err != nil { + return price, err + } + + return price, nil +} + +// GetList 获取SmsServices列表 +func (e SmsServices) GetList(resp *[]dto.SmsServicesGetListResp) error { + var data []models.SmsServices + err := e.Orm.Find(&data).Error + if err != nil { + e.Log.Errorf("SmsServicesService GetList error:%s \r\n", err) + return err + } + for _, item := range data { + respItem := dto.SmsServicesGetListResp{ + Name: item.Name, + Code: item.Code, + } + *resp = append(*resp, respItem) + } + return nil +} + +// GetPage 获取SmsServices列表 +func (e *SmsServices) GetPage(c *dto.SmsServicesGetPageReq, p *actions.DataPermission, list *[]models.SmsServices, count *int64) error { + var err error + var data models.SmsServices + + 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("SmsServicesService GetPage error:%s \r\n", err) + return err + } + return nil +} + +// Get 获取SmsServices对象 +func (e *SmsServices) Get(d *dto.SmsServicesGetReq, p *actions.DataPermission, model *models.SmsServices) error { + var data models.SmsServices + + 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 GetSmsServices error:%s \r\n", err) + return err + } + if err != nil { + e.Log.Errorf("db error:%s", err) + return err + } + return nil +} + +// Insert 创建SmsServices对象 +func (e *SmsServices) Insert(c *dto.SmsServicesInsertReq) error { + var err error + var data models.SmsServices + c.Generate(&data) + err = e.Orm.Create(&data).Error + if err != nil { + e.Log.Errorf("SmsServicesService Insert error:%s \r\n", err) + return err + } + return nil +} + +// Update 修改SmsServices对象 +func (e *SmsServices) Update(c *dto.SmsServicesUpdateReq, p *actions.DataPermission) error { + var err error + var data = models.SmsServices{} + 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("SmsServicesService Save error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权更新该数据") + } + return nil +} + +// Remove 删除SmsServices +func (e *SmsServices) Remove(d *dto.SmsServicesDeleteReq, p *actions.DataPermission) error { + var data models.SmsServices + + 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 RemoveSmsServices error:%s \r\n", err) + return err + } + if db.RowsAffected == 0 { + return errors.New("无权删除该数据") + } + return nil +} + +func (e *SmsServices) GetByCode(code string) (models.SmsServices, error) { + var data models.SmsServices + + if err := e.Orm.Model(data).Where("code =?", code).First(&data).Error; err != nil { + return data, err + } + + return data, nil +} diff --git a/app/jobs/examples.go b/app/jobs/examples.go index 12888b2..324ffce 100644 --- a/app/jobs/examples.go +++ b/app/jobs/examples.go @@ -16,6 +16,7 @@ func InitJob() { "RenewalJob": RenewalJob{}, "ExpireProxyJob": ExpireProxyJob{}, "CleanExpiredOrderJob": CleanExpiredOrderJob{}, + "SmsJob": SmsJob{}, // ... } } diff --git a/app/jobs/sms_job.go b/app/jobs/sms_job.go new file mode 100644 index 0000000..089fb02 --- /dev/null +++ b/app/jobs/sms_job.go @@ -0,0 +1,18 @@ +package jobs + +import ( + "go-admin/app/admin/service" + + "github.com/go-admin-team/go-admin-core/logger" +) + +type SmsJob struct{} + +// 定时查询结果 +func (j SmsJob) Exec(args interface{}) error { + phoneService := service.SmsPhone{} + phoneService.Orm = GetDb() + phoneService.Log = logger.NewHelper(logger.DefaultLogger) + + return phoneService.SyncCodes() +} diff --git a/app/jobs/sms_job_test.go b/app/jobs/sms_job_test.go new file mode 100644 index 0000000..4fabded --- /dev/null +++ b/app/jobs/sms_job_test.go @@ -0,0 +1,16 @@ +package jobs + +import ( + "go-admin/config" + "testing" +) + +func TestSendSMS(t *testing.T) { + initSetting() + config.ExtConfig.DaisysmsUrl = "https://daisysms.com/stubs/handler_api.php" + job := SmsJob{} + + if err := job.Exec(nil); err != nil { + t.Error(err) + } +} diff --git a/common/statuscode/status_code.go b/common/statuscode/status_code.go index 14e50bd..9a96195 100644 --- a/common/statuscode/status_code.go +++ b/common/statuscode/status_code.go @@ -11,6 +11,17 @@ var StatusCodeZh = map[int]string{ PasswordEmpty: "密码不能为空", ConfirmPasswordEmpty: "确认密码不能为空", VerifyCodeError: "验证码错误", + BalanceNotEnough: "余额不足", + MaxPriceExceeded: "超过最大接受单价", + NoNumbers: "号码不足", + RentalsNotFinished: "需要先完成部分租赁才能继续租赁", + + SmsCancel: "短信验证码_手机号过期", + SmsNoActivation: "短信验证码_手机号不存在", + SmsWaitCode: "短信验证码_等待验证码", + SmsLongNumWaitCode: "短信验证码_长效号码已唤醒", + SmsNotExisted: "号码不存在", + SmsNotExpired: "号码未过期无法删除", } var StatusCodeEn = map[int]string{ @@ -24,6 +35,17 @@ var StatusCodeEn = map[int]string{ PasswordEmpty: "password can not be empty", ConfirmPasswordEmpty: "confirm password can not be empty", VerifyCodeError: "verify code error", + BalanceNotEnough: "balance not enough", + MaxPriceExceeded: "max price exceeded", + NoNumbers: "no numbers", + RentalsNotFinished: "need to finish some rentals before renting more", + + SmsCancel: "sms code expired", + SmsNoActivation: "sms code not exist", + SmsWaitCode: "sms code wait for input", + SmsLongNumWaitCode: "sms code long num wake up", + SmsNotExisted: "number not exist", + SmsNotExpired: "number not expired, can not delete", } func GetMsg(code int, lang string) string { @@ -59,4 +81,26 @@ const ( ConfirmPasswordEmpty = 10008 //验证码错误 VerifyCodeError = 10009 + + //余额不足 + BalanceNotEnough = 10010 + //超过最大接受单价 + MaxPriceExceeded = 10011 + //号码不足 + NoNumbers = 10012 + // Need to finish some rentals before renting more + RentalsNotFinished = 10013 + + //短信验证码_手机号过期 + SmsCancel = 20014 + //短信验证码_手机号不存在 + SmsNoActivation = 20015 + //短信验证码_等待验证码 + SmsWaitCode = 20016 + //短信验证码_长效号码已唤醒 + SmsLongNumWaitCode = 20017 + //号码不存在 + SmsNotExisted = 20018 + //号码未过期无法删除 + SmsNotExpired = 20019 ) diff --git a/config/extend.go b/config/extend.go index 135869b..6c90ebf 100644 --- a/config/extend.go +++ b/config/extend.go @@ -14,6 +14,7 @@ type Extend struct { TrxGridUrl string CliproxyUrl string //cliproxy服务地址 CliproxyApiUrl string //cliproxy api地址 + DaisysmsUrl string //daisysms服务地址 } type AMap struct { diff --git a/config/settings.yml b/config/settings.yml index da2f813..7ffba14 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -55,6 +55,8 @@ settings: cliproxyUrl: "https://f.cliproxy.com" #cliproxy api url cliproxyApiUrl: "https://api.cliproxy.com" + #daisysms api url + daisysmsUrl: "https://daisysms.com/stubs/handler_api.php" cache: redis: addr: 127.0.0.1:6379 diff --git a/utils/httphelper/http_helper.go b/utils/httphelper/http_helper.go index 594fd06..e235d4c 100644 --- a/utils/httphelper/http_helper.go +++ b/utils/httphelper/http_helper.go @@ -48,12 +48,14 @@ func (c *HTTPClient) applyHeaders(req *http.Request, customHeaders map[string]st // path: 请求路径,将与 BaseURL 拼接 // requestBody: 请求体数据,如果为 GET/DELETE 请求则为 nil // customHeaders: 自定义请求头,将覆盖默认请求头 -// responseData: 用于存储响应数据的目标结构体(指针类型) +// responseData: 用于存储响应数据的目标结构体(指针类型),如果为 nil 则表示不需要 JSON 解码 +// rawResponse: 用于存储原始响应体字节切片(*[]byte),如果为 nil 则表示不需要原始响应 func (c *HTTPClient) doRequest( method, path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}, + rawResponse *[]byte, // 新增参数:指向字节切片的指针,用于存储原始响应 ) error { // 拼接完整的 URL url := c.BaseURL + path @@ -93,7 +95,7 @@ func (c *HTTPClient) doRequest( return fmt.Errorf("http request failed with status: %d, body: %s", resp.StatusCode, string(bodyBytes)) } - // 解码 JSON 响应(支持 gzip) + // 解码响应(支持 gzip) var reader io.Reader = resp.Body if resp.Header.Get("Content-Encoding") == "gzip" { gzipReader, err := gzip.NewReader(resp.Body) @@ -104,8 +106,20 @@ func (c *HTTPClient) doRequest( reader = gzipReader } + // 首先读取整个响应体,然后决定如何处理 + bodyBytes, err := io.ReadAll(reader) + if err != nil { + return fmt.Errorf("read response body failed: %w", err) + } + + // 如果提供了原始响应目标,则填充它 + if rawResponse != nil { + *rawResponse = bodyBytes + } + + // 如果提供了 JSON 解码目标,则尝试解码 if responseData != nil { - err = json.NewDecoder(reader).Decode(responseData) + err = json.Unmarshal(bodyBytes, responseData) // 直接对字节切片使用 Unmarshal if err != nil { return fmt.Errorf("json decode response body failed: %w", err) } @@ -119,7 +133,7 @@ func (c *HTTPClient) doRequest( // customHeaders: 自定义请求头 // responseData: 用于存储响应数据的目标结构体(指针类型) func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseData interface{}) error { - return c.doRequest(http.MethodGet, path, nil, customHeaders, responseData) + return c.doRequest(http.MethodGet, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil } // Post 发送 POST 请求 @@ -128,7 +142,7 @@ func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseD // customHeaders: 自定义请求头 // responseData: 用于存储响应数据的目标结构体(指针类型) func (c *HTTPClient) Post(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { - return c.doRequest(http.MethodPost, path, requestBody, customHeaders, responseData) + return c.doRequest(http.MethodPost, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil } // PostWithContentType 发送 POST 请求,支持自定义 Content-Type(如 application/json 或 multipart/form-data) @@ -196,8 +210,15 @@ func (c *HTTPClient) PostWithContentType(path string, requestBody interface{}, c reader = gzipReader } + // 首先读取整个响应体,然后决定如何处理 + bodyBytes, err := io.ReadAll(reader) + if err != nil { + return fmt.Errorf("read response body failed: %w", err) + } + + // 如果提供了 JSON 解码目标,则尝试解码 if responseData != nil { - err = json.NewDecoder(reader).Decode(responseData) + err = json.Unmarshal(bodyBytes, responseData) if err != nil { return fmt.Errorf("json decode response body failed: %w", err) } @@ -212,7 +233,7 @@ func (c *HTTPClient) PostWithContentType(path string, requestBody interface{}, c // customHeaders: 自定义请求头 // responseData: 用于存储响应数据的目标结构体(指针类型) func (c *HTTPClient) Put(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { - return c.doRequest(http.MethodPut, path, requestBody, customHeaders, responseData) + return c.doRequest(http.MethodPut, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil } // Delete 发送 DELETE 请求 @@ -221,7 +242,7 @@ func (c *HTTPClient) Put(path string, requestBody interface{}, customHeaders map // responseData: 用于存储响应数据的目标结构体(指针类型) func (c *HTTPClient) Delete(path string, customHeaders map[string]string, responseData interface{}) error { // DELETE 请求通常没有请求体,但某些 RESTful API 可能支持 - return c.doRequest(http.MethodDelete, path, nil, customHeaders, responseData) + return c.doRequest(http.MethodDelete, path, nil, customHeaders, responseData, nil) // rawResponse 传递 nil } // Patch 发送 PATCH 请求 @@ -230,5 +251,19 @@ func (c *HTTPClient) Delete(path string, customHeaders map[string]string, respon // customHeaders: 自定义请求头 // responseData: 用于存储响应数据的目标结构体(指针类型) func (c *HTTPClient) Patch(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error { - return c.doRequest(http.MethodPatch, path, requestBody, customHeaders, responseData) + return c.doRequest(http.MethodPatch, path, requestBody, customHeaders, responseData, nil) // rawResponse 传递 nil +} + +// GetRaw 发送 GET 请求并返回原始响应体 +// path: 请求路径 +// customHeaders: 自定义请求头 +// 返回值: 原始响应体字节切片或错误 +func (c *HTTPClient) GetRaw(path string, customHeaders map[string]string) ([]byte, error) { + var raw []byte + // responseData 传递 nil,rawResponse 传递 &raw + err := c.doRequest(http.MethodGet, path, nil, customHeaders, nil, &raw) + if err != nil { + return nil, err + } + return raw, nil }