123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758 |
- package service
-
- import (
- "bytes"
- "encoding/base64"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "math"
- "math/rand"
- "net/http"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "SSO/models"
- "SSO/utils"
-
- "github.com/astaxie/beego"
- "github.com/jinzhu/gorm"
- )
-
- type SMSServiceError struct {
- Err string
- }
-
- func (e *SMSServiceError) Error() string {
- return e.Err
- }
-
- // 有如下两个外部可调用的接口:SendSMSUseTemplate、SendSMSWithCustomContent两个函数
- // 但是实际上我打算将 SendSMSUseTemplate 作为底层函数使用,不直接提供外界:
- // 这需要提供数个默认模板,通过为这几个模板创建独立便利函数,封装 SendSMSUseTemplate 的参数 defaultTemplateID 和 params
-
- // 发送验证码短信
- // 参数 aespass 是加密后的地址信息,用于限制频繁调用
- func SendVerificationCodeSMS(mobile string, aespass string) error {
- if len(mobile) == 0 {
- return &SMSServiceError{Err: "手机号为空"}
- }
- if err := checkVerificationCodeSMSLimit(aespass, mobile); err != nil {
- return err
- }
-
- var code_str string
- for i := 0; i < 6; i++ {
- rand.Seed(time.Now().UnixNano())
- code_str += strconv.Itoa(rand.Intn(10))
- }
- templateID, _ := beego.AppConfig.Int("sms_verification_code_templateid")
- utils.TraceLog("验证码为%v", code_str)
- _, _, err := batchSendMessage(templateID, []string{code_str}, []string{mobile})
- if err == nil {
- redisClient := RedisClient()
- defer redisClient.Close()
- cur_date := time.Now().Format("2006-01-02")
- redisClient.Set("code_msg_"+mobile, code_str, time.Minute*10)
- redisClient.Incr("code_msg_" + mobile + "_" + cur_date).Result()
- // 取出地址信息,因为上面已经验证过,这里就直接解密而不做错误判断了
- bytesPass, _ := base64.StdEncoding.DecodeString(aespass)
- tpass := utils.AESDecrypt(bytesPass)
- redisClient.Incr("ip:host_" + cur_date + "_" + tpass).Result()
- }
- return err
- }
-
- func checkVerificationCodeSMSLimit(aespass string, mobile string) error {
- redisClient := RedisClient()
- defer redisClient.Close()
- bytesPass, err := base64.StdEncoding.DecodeString(aespass)
- if err != nil {
- return &SMSServiceError{Err: "缺少关键参数"}
- }
- tpass := utils.AESDecrypt(bytesPass)
- if len(tpass) == 0 {
- return &SMSServiceError{Err: "缺少关键参数"}
- }
-
- cur_date := time.Now().Format("2006-01-02")
- add_redis, err := redisClient.Get("ip:host_" + cur_date + "_" + tpass).Result()
- if err != nil {
- return &SMSServiceError{Err: "缺少关键参数"}
- }
- ip_max_send_count, _ := beego.AppConfig.Int("ip_max_send_count")
- if add_count, _ := strconv.Atoi(add_redis); add_count >= ip_max_send_count {
- return &SMSServiceError{Err: "当前IP发送短信超过限制"}
- }
-
- moblie_count, _ := redisClient.Get("code_msg_" + mobile + "_" + cur_date).Result()
- moblie_count_int, _ := strconv.Atoi(moblie_count)
- if moblie_max, _ := beego.AppConfig.Int("moblie_max_send_count"); moblie_count_int >= moblie_max {
- return &SMSServiceError{Err: "当前手机号发送短信超过限制"}
- }
-
- return nil
- }
-
- // 指定模板群发短信
- func SendSMSUseTemplate(orgID int, orgShortName string, defaultTemplateID int, params []string, mobiles []string) error {
-
- if len(mobiles) == 0 {
- return nil
- }
-
- defaultTemplate, getDefaultTemplateErr := getDefaultTemplate(defaultTemplateID)
- if getDefaultTemplateErr != nil {
- utils.WarningLog("获取默认模板信息失败: %v", getDefaultTemplateErr)
- return getDefaultTemplateErr
- }
- smsContent, matchErr := getSMSFullContent(defaultTemplate, orgShortName, params)
- if matchErr != nil {
- return matchErr
- }
- if len(smsContent) > 500 {
- return &SMSServiceError{Err: "短信太长"}
- }
-
- // 若发送,是否会超过短信额度
- willConsumeLimit := getWillConsumeLimit(smsContent, len(mobiles))
- if isSMSCountOverLimit(orgID, willConsumeLimit) {
- utils.WarningLog("短信额度不足")
- return &SMSServiceError{"短信额度不足"}
- }
-
- sendTime := time.Now()
-
- batch := &models.SMSBatch{
- OrgId: orgID,
- Autograph: orgShortName,
- Params: strings.Join(params, ","),
- FullContent: smsContent,
- Mobiles: strings.Join(mobiles, ","),
- SendTime: sendTime.Unix(),
- Status: models.SMSBatchStatusUnsend,
- CreateTime: sendTime.Unix(),
- ModifyTime: sendTime.Unix(),
- }
-
- template, getTemplateErr := getSMSTemplateID(orgID, orgShortName, defaultTemplateID)
- if getTemplateErr != nil {
- return getTemplateErr
- }
- if template.TemplateId != 0 {
- if template.Status == 0 {
- return &SMSServiceError{Err: "短信模板无效"}
- }
- // 扣除短信额度
- if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
- return updateLimitErr
- }
- // 插入“未发送”状态的短信批次记录
- batch.TemplateId = template.TemplateId
- saveSMSBatch(batch, models.SMSBatchStatusUnsend)
- // 发送短信
- sendCount, report, sendErr := batchSendMessage(template.TemplateId, params, mobiles)
- // 根据发送结果,修改短信批次记录的状态,调整扣除额度
- if sendErr == nil { // 发送成功
- utils.InfoLog("发送了短信数量: %v", sendCount)
- // 修改短信批次记录的状态为“已发送”
- saveSMSBatch(batch, models.SMSBatchStatusDidSend)
- // 返回发送失败部分的额度
- if sendCount < willConsumeLimit {
- if updateLimitErr := updateLimit(orgID, sendTime, sendCount-willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- }
- // 插入发送状态
- if batch.Id != 0 {
- saveSMSSendReport(batch.Id, report)
- } else {
- utils.ErrorLog("因为短信批次插入失败,导致无法插入发送状态,report: %v", report)
- }
- return nil
-
- } else { // 发送失败
- utils.ErrorLog("发送短信失败: %v", sendErr)
- // 修改短信批次记录的状态为“发送失败”
- saveSMSBatch(batch, models.SMSBatchStatusSendFailed)
- // 返回扣除的额度
- if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- return sendErr
- }
-
- } else {
- // 扣除短信额度
- if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
- return updateLimitErr
- }
- // 创建模板 成功:保存模板ID; 失败:需要返回扣除的额度
- if newTemplateID, createErr := createTemplateToSMSPlatform(orgShortName, defaultTemplate.Content, defaultTemplate.Type); createErr == nil { // 创建成功
- // 插入“待审核”状态的短信批次记录
- batch.TemplateId = newTemplateID
- saveSMSBatch(batch, models.SMSBatchStatusInReview)
- // 保存模板ID
- if saveErr := setSMSTemplateIDFor(orgID, orgShortName, defaultTemplateID, newTemplateID); saveErr != nil {
- utils.ErrorLog("保存模板ID失败: %v", saveErr)
- return saveErr
- } else {
- // 等待短信平台对模板的审核回调
- utils.TraceLog("等待短信平台对模板的审核回调")
- return nil
- }
-
- } else { // 创建失败
- utils.ErrorLog("短信平台创建模板失败: %v", createErr)
- // 需要返回扣除的额度
- if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- return createErr
- }
- }
- }
-
- // 发送自定义内容短信,content 不需要包括“退订请回复TD”字样
- func SendSMSWithCustomContent(orgID int, orgShortName string, content string, mobiles []string) error {
- if len(mobiles) == 0 {
- return nil
- }
-
- content = fmt.Sprintf("%v退订请回复TD", content)
- fullContent := fmt.Sprintf("【%v】%v", orgShortName, content)
- if len(fullContent) > 500 {
- return &SMSServiceError{Err: "短信太长"}
- }
-
- // 若发送,是否会超过短信额度
- willConsumeLimit := getWillConsumeLimit(fullContent, len(mobiles))
- if isSMSCountOverLimit(orgID, willConsumeLimit) {
- utils.WarningLog("短信额度不足")
- return &SMSServiceError{"短信额度不足"}
- }
-
- sendTime := time.Now()
-
- batch := &models.SMSBatch{
- OrgId: orgID,
- Autograph: orgShortName,
- Params: "",
- FullContent: fullContent,
- Mobiles: strings.Join(mobiles, ","),
- SendTime: sendTime.Unix(),
- Status: models.SMSBatchStatusUnsend,
- CreateTime: sendTime.Unix(),
- ModifyTime: sendTime.Unix(),
- }
-
- // 扣除短信额度
- if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
- return updateLimitErr
- }
- // 创建模板 成功:保存模板ID; 失败:需要返回扣除的额度
- if newTemplateID, createErr := createTemplateToSMSPlatform(orgShortName, content, 5); createErr == nil { // 创建成功
- // 插入“待审核”状态的短信批次记录
- batch.TemplateId = newTemplateID
- saveSMSBatch(batch, models.SMSBatchStatusInReview)
- // 保存模板ID
- if saveErr := setSMSTemplateIDFor(orgID, orgShortName, 0, newTemplateID); saveErr != nil {
- utils.ErrorLog("保存模板ID失败: %v", saveErr)
- return saveErr
- } else {
- // 等待短信平台对模板的审核回调
- utils.TraceLog("等待短信平台对模板的审核回调")
- return nil
- }
-
- } else { // 创建失败
- utils.ErrorLog("短信平台创建模板失败: %v", createErr)
- // 需要返回扣除的额度
- if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- return createErr
- }
- }
-
- // 短信批次重发
- func ResendSMSBatch(templateID int) error {
- batchs, getBatchErr := getInReviewSMSBatchWithTemplateID(templateID)
- if getBatchErr != nil {
- if getBatchErr != gorm.ErrRecordNotFound {
- utils.ErrorLog("获取短信批次失败: %v", getBatchErr)
- return getBatchErr
- } else {
- return nil
- }
- }
-
- for _, batch := range batchs {
- params := strings.Split(batch.Params, ",")
- mobiles := strings.Split(batch.Mobiles, ",")
-
- // 若发送,是否会超过短信额度
- willConsumeLimit := getWillConsumeLimit(batch.FullContent, len(mobiles))
- // 更新批次状态为"未发送"
- saveSMSBatch(batch, models.SMSBatchStatusUnsend)
- // 发送短信
- sendCount, report, sendErr := batchSendMessage(templateID, params, mobiles)
- // 根据发送结果,修改短信批次记录的状态,调整扣除额度
- if sendErr == nil { // 发送成功
- utils.InfoLog("发送了短信数量: %v", sendCount)
- // 修改短信批次记录的状态为“已发送”
- saveSMSBatch(batch, models.SMSBatchStatusDidSend)
- // 返回发送失败部分的额度
- if sendCount < willConsumeLimit {
- if updateLimitErr := updateLimit(batch.OrgId, time.Unix(batch.SendTime, 0), sendCount-willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- }
- // 插入发送状态
- saveSMSSendReport(batch.Id, report)
- return nil
-
- } else { // 发送失败
- utils.ErrorLog("发送短信失败: %v", sendErr)
- // 修改短信批次记录的状态为“发送失败”
- saveSMSBatch(batch, models.SMSBatchStatusSendFailed)
- // 返回扣除的额度
- if updateLimitErr := updateLimit(batch.OrgId, time.Unix(batch.SendTime, 0), -willConsumeLimit); updateLimitErr != nil {
- // 记录更新失败的操作
- }
- }
- }
- return nil
- }
-
- func GetAllDefaultTemplates() []*models.SMSDefaultTemplate {
- var templates []*models.SMSDefaultTemplate
- err := readDb.Find(&templates).Error
- if err != nil {
- return make([]*models.SMSDefaultTemplate, 0)
- }
- return templates
- }
-
- // 更新"sgj_patient_sms_template_id"模板状态为无效
- func DisableTemplate(templateID int) error {
- tx := writeDb.Begin()
- if err := tx.Exec("UPDATE sgj_patient_sms_template_id SET status = ?, mtime = ? Where sms_template_id = ?;", 0, time.Now().Unix(), templateID).Error; err != nil {
- utils.ErrorLog("无效化模板失败: templateID: %v", templateID)
- tx.Rollback()
- // 记录更新失败的操作
- }
- return tx.Commit().Error
- }
-
- // 更新批次状态为”审核未通过“
- func SetSMSBatchUnapprovedWithTemplateID(templateID int) error {
- tx := writeDb.Begin()
- if err := tx.Exec("UPDATE sgj_patient_sms_batch SET status = ?, mtime = ? Where sms_template_id = ?;", models.SMSBatchStatusUnapproved, time.Now().Unix(), templateID).Error; err != nil {
- utils.ErrorLog("更新短信批次状态为”审核未通过“失败: templateID: %v", templateID)
- tx.Rollback()
- // 记录更新失败的操作
- }
- return tx.Commit().Error
- }
-
- // private methods
- // 获取数据库中 orgID & orgShortName & defaultTemplateID 对应的短信平台上的模板 ID,如果没有,会返回 0
- func getSMSTemplateID(orgID int, orgShortName string, defaultTemplateID int) (*models.SMSTemplateID, error) {
- var template models.SMSTemplateID
- err := readDb.Where("org_id = ? AND org_short_name = ? AND custom_template_id = ?", orgID, orgShortName, defaultTemplateID).First(&template).Error
- if err != nil {
- utils.WarningLog("getSMSTemplateID err: %v", err)
- return nil, err
- } else {
- return &template, nil
- }
- }
-
- // 将获取到的短信平台上的模板 ID插入数据库
- func setSMSTemplateIDFor(orgID int, orgShortName string, defaultTemplateID int, templateID int) error {
- tx := writeDb.Begin()
- now := time.Now().Unix()
- template := &models.SMSTemplateID{
- OrgId: orgID,
- OrgShortName: orgShortName,
- CustomTemplateId: defaultTemplateID,
- TemplateId: templateID,
- Status: 1,
- CreateTime: now,
- ModifyTime: now,
- }
- if err := tx.Create(template).Error; err != nil {
- tx.Rollback()
- }
- return tx.Commit().Error
- }
-
- // 获取默认模板
- func getDefaultTemplate(defaultTemplateID int) (*models.SMSDefaultTemplate, error) {
- var template models.SMSDefaultTemplate
- err := readDb.Where("id = ?", defaultTemplateID).First(&template).Error
- if err != nil {
- return nil, err
- } else {
- return &template, nil
- }
- }
-
- func getDefaultTemplateIDWithSMSTemplateID(smsTemplateID int) (int, error) {
- var template models.SMSTemplateID
- err := readDb.Where("sms_template_id = ?", smsTemplateID).First(template).Error
- if err != nil {
- return 0, err
- } else {
- return template.CustomTemplateId, nil
- }
- }
-
- // 判断短信参数是否含有非法参数
- func isSMSParamValid(params []string) bool {
- if params == nil {
- return true
- }
- for _, param := range params {
- if strings.Contains(param, ",") || strings.Contains(param, "】") || strings.Contains(param, "【") {
- return false
- }
- }
- return true
- }
-
- // 计算即将消耗的额度
- func getWillConsumeLimit(smsContent string, mobileCount int) int {
- limitEachMessage := math.Ceil(float64(len(smsContent)) / 67.0)
- return int(limitEachMessage) * mobileCount
- }
-
- // 拼接短信模板和参数为完整短信内容
- func getSMSFullContent(template *models.SMSDefaultTemplate, autograph string, params []string) (string, error) {
- reg, _ := regexp.Compile("{\\d}")
- placeholders := reg.FindAllString(template.Content, -1)
- if len(placeholders) != len(params) {
- utils.WarningLog("短信模板和参数不匹配")
- return template.Content, &SMSServiceError{"短信模板和参数不匹配"}
- }
- tplContent := template.Content
- for index, placeholder := range placeholders {
- tplContent = strings.Replace(tplContent, placeholder, params[index], 1)
- }
- smsContent := fmt.Sprintf("【%v】%v", autograph, tplContent)
- return smsContent, nil
- }
-
- // 向短信平台创建模板,返回新模板的 ID
- // autograph: 签名,即为机构简称
- // content: 模板内容
- // type_: 短信类型:0:通知短信、5:会员服务短信、4:验证码短信
- func createTemplateToSMSPlatform(autograph string, content string, type_ int) (int, error) {
- sms_api := beego.AppConfig.String("sms_baseUrl") + "addsmstemplate"
- appID, sid, token := getSMSConfig()
- params := make(map[string]interface{})
- params["appid"] = appID
- params["sid"] = sid
- params["token"] = token
- params["type"] = type_
- params["autograph"] = autograph
- params["content"] = content
-
- paramsBytes, _ := json.Marshal(params)
- resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
-
- if requestErr != nil {
- utils.ErrorLog("短信平台增加模板接口调用失败: %v", requestErr)
- return 0, requestErr
- }
- defer resp.Body.Close()
- body, ioErr := ioutil.ReadAll(resp.Body)
- if ioErr != nil {
- utils.ErrorLog("短信平台增加模板接口返回数据读取失败: %v", ioErr)
- return 0, ioErr
- }
- var respJSON map[string]interface{}
- utils.InfoLog(string(body))
- if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
- utils.ErrorLog("短信平台增加模板接口返回数据解析JSON失败: %v", err)
- return 0, err
- }
- if respJSON["code"].(string) != "000000" {
- msg := respJSON["msg"].(string)
- utils.ErrorLog("短信平台增加模板接口请求失败: %v", msg)
- return 0, &SMSServiceError{"短信平台增加模板接口请求失败"}
- }
-
- templateID, _ := strconv.Atoi(respJSON["templateid"].(string))
- utils.SuccessLog("新短信模板 ID: %v", templateID)
- return templateID, nil
- }
-
- // 指定模板群发短信
- // 返回值为发送了 n 条短信、短信平台返回的 report 数组[{"code":"0", "msg":"OK", "smsid":"f96f79240e372587e9284cd580d8f953", "mobile":"18011984299", "count":"1"}]
- func batchSendMessage(templateID int, params []string, mobiles []string) (int, []interface{}, error) {
- sms_api := beego.AppConfig.String("sms_baseUrl") + "sendsms"
- mobileStr := strings.Join(mobiles, ",")
- appID, sid, token := getSMSConfig()
- requestParams := make(map[string]interface{})
- requestParams["appid"] = appID
- requestParams["sid"] = sid
- requestParams["token"] = token
- requestParams["templateid"] = strconv.Itoa(templateID)
- requestParams["mobile"] = mobileStr
- if params != nil && len(params) != 0 {
- paramStr := strings.Join(params, ",")
- requestParams["param"] = paramStr
- }
-
- paramsBytes, _ := json.Marshal(requestParams)
- resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
-
- if requestErr != nil {
- utils.ErrorLog("短信平台模板群发接口调用失败: %v", requestErr)
- return 0, nil, requestErr
- }
- defer resp.Body.Close()
- body, ioErr := ioutil.ReadAll(resp.Body)
- if ioErr != nil {
- utils.ErrorLog("短信平台模板群发接口返回数据读取失败: %v", ioErr)
- return 0, nil, ioErr
- }
- var respJSON map[string]interface{}
- utils.InfoLog(string(body))
- if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
- utils.ErrorLog("短信平台模板群发接口返回数据解析JSON失败: %v", err)
- return 0, nil, err
- }
- if respJSON["code"].(string) != "000000" {
- msg := respJSON["msg"].(string)
- utils.ErrorLog("短信平台模板群发接口请求失败: %v", msg)
- return 0, nil, &SMSServiceError{"短信平台模板群发接口请求失败"}
-
- } else {
- utils.SuccessLog("短信发送成功 report: %v", respJSON["report"])
- if len(mobiles) > 1 {
- count, _ := strconv.Atoi(respJSON["count_sum"].(string))
- return count, respJSON["report"].([]interface{}), nil
- } else {
- return 1, nil, nil
- }
- }
- }
-
- // 判断短信额度是否充足
- func isSMSCountOverLimit(orgID int, willSendCount int) bool {
- freeLimit, getFreeErr := getUserSMSFreeLimit(orgID, time.Now())
- if getFreeErr != nil {
- return true
- }
- if willSendCount+freeLimit.UsedCount <= freeLimit.TotalCount {
- // 免费额度充足
- return false
- }
- // 免费额度不足时,需要加上已购额度来判断
- buyLimit, getBuyErr := getUserSMSBuyLimit(orgID)
- if getBuyErr != nil {
- return true
- }
- if buyLimit.UsedCount+freeLimit.UsedCount+willSendCount <= freeLimit.TotalCount+buyLimit.TotalCount {
- // 可用额度充足
- return false
- } else {
- return true
- }
- }
-
- // 获取短信平台信息
- func getSMSConfig() (string, string, string) {
- return beego.AppConfig.String("sms_appId"),
- beego.AppConfig.String("sms_sid"),
- beego.AppConfig.String("sms_token")
- }
-
- // 获取用户的免费短信额度
- func getUserSMSFreeLimit(orgID int, date time.Time) (*models.UserSMSFreeLimit, error) {
- month := date.Format("200601")
- var freeLimit models.UserSMSFreeLimit
- if readErr := readDb.Where("org_id = ? AND valid_month = ?", orgID, month).First(&freeLimit).Error; gorm.IsRecordNotFoundError(readErr) == true {
- // 创建
- now := time.Now().Unix()
- freeLimit = models.UserSMSFreeLimit{
- OrgId: orgID,
- TotalCount: 100,
- UsedCount: 0,
- ValidMonth: month,
- Status: 1,
- CreateTime: now,
- ModifyTime: now,
- }
- tx := writeDb.Begin()
- if createErr := tx.Create(&freeLimit).Error; createErr != nil {
- tx.Rollback()
- utils.ErrorLog("用户短信免费额度创建失败: %v", createErr)
- tx.Commit()
- return nil, createErr
- } else {
- tx.Commit()
- return &freeLimit, nil
- }
-
- } else if readErr != nil {
- utils.ErrorLog("获取用户短信免费额度信息失败: %v", readErr)
- return nil, readErr
-
- } else {
- return &freeLimit, nil
- }
- }
-
- // 获取用户的已购短信额度
- func getUserSMSBuyLimit(orgID int) (*models.UserSMSBuyLimit, error) {
- var buyLimit models.UserSMSBuyLimit
- if readErr := readDb.Where("org_id = ?", orgID).First(&buyLimit).Error; gorm.IsRecordNotFoundError(readErr) == true {
- // 创建
- now := time.Now().Unix()
- buyLimit = models.UserSMSBuyLimit{
- OrgId: orgID,
- TotalCount: 0,
- UsedCount: 0,
- Status: 1,
- CreateTime: now,
- ModifyTime: now,
- }
- tx := writeDb.Begin()
- if createErr := tx.Create(&buyLimit).Error; createErr != nil {
- tx.Rollback()
- utils.ErrorLog("用户短信已购额度创建失败: %v", createErr)
- tx.Commit()
- return nil, createErr
- } else {
- tx.Commit()
- return &buyLimit, nil
- }
-
- } else if readErr != nil {
- utils.ErrorLog("获取用户短信已购额度信息失败: %v", readErr)
- return nil, readErr
-
- } else {
- return &buyLimit, nil
- }
- }
-
- // 更新用户短信额度
- func updateLimit(orgID int, date time.Time, consumeLimit int) error {
- freeLimit, getFreeLimitErr := getUserSMSFreeLimit(orgID, date)
- if getFreeLimitErr != nil {
- return getFreeLimitErr
- }
- if freeLimit.UsedCount+consumeLimit <= freeLimit.TotalCount {
- freeLimit.UsedCount += consumeLimit
- freeLimit.ModifyTime = time.Now().Unix()
- tx := writeDb.Begin()
- if err := tx.Save(freeLimit).Error; err != nil {
- utils.ErrorLog("更新短信免费额度失败: %v\nOrgID: %v; 额度生效月份: %v; 总额度: %v; 已用额度: %v; 扣完后已用额度应当为: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.TotalCount, freeLimit.UsedCount+consumeLimit, freeLimit.UsedCount)
- tx.Rollback()
- }
- return tx.Commit().Error
-
- } else {
- buyLimit, getBuyLimitErr := getUserSMSBuyLimit(orgID)
- if getBuyLimitErr != nil {
- return getBuyLimitErr
- }
- consumeFreeLimit := freeLimit.TotalCount - freeLimit.UsedCount
- consumeBuyLimit := consumeLimit - consumeFreeLimit
- now := time.Now().Unix()
- tx := writeDb.Begin()
- freeLimit.UsedCount = freeLimit.TotalCount
- freeLimit.ModifyTime = now
- updateFreeLimitErr := tx.Save(freeLimit)
- if updateFreeLimitErr != nil {
- utils.ErrorLog("更新短信免费额度失败: %v\nOrgID: %v; 额度生效月份: %v; 当前剩余免费额度: %v; 当前剩余已购额度: %v; 当前应当扣去额度: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.UsedCount-consumeFreeLimit, buyLimit.TotalCount-buyLimit.UsedCount, consumeLimit)
- tx.Rollback()
-
- } else {
- buyLimit.UsedCount += consumeBuyLimit
- buyLimit.ModifyTime = now
- updateBuyLimitErr := tx.Save(buyLimit)
- if updateBuyLimitErr != nil {
- utils.ErrorLog("更新短信已购额度失败: %v\nOrgID: %v; 额度生效月份: %v; 当前剩余免费额度: %v; 当前剩余已购额度: %v; 当前应当扣去额度: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.UsedCount-consumeFreeLimit, buyLimit.TotalCount-buyLimit.UsedCount, consumeLimit)
- tx.Rollback()
- }
- }
- return tx.Commit().Error
- }
- }
-
- // 创建/更新 短信批次
- func saveSMSBatch(batch *models.SMSBatch, status int8) error {
- batch.Status = status
- batch.ModifyTime = time.Now().Unix()
- if err := _saveSMSBatch(batch); err != nil {
- // 记录更新失败的操作
-
- return err
- } else {
- return nil
- }
- }
-
- func _saveSMSBatch(batch *models.SMSBatch) error {
- tx := writeDb.Begin()
- if err := tx.Save(batch).Error; err != nil {
- tx.Rollback()
- }
- return tx.Commit().Error
- }
-
- // 获取短信批次
- func getInReviewSMSBatchWithTemplateID(templateID int) ([]*models.SMSBatch, error) {
- var batchs []*models.SMSBatch
- err := readDb.Where("sms_template_id = ? AND status = ?", templateID, models.SMSBatchStatusInReview).Find(&batchs).Error
- if err != nil {
- return nil, err
- } else {
- return batchs, nil
- }
- }
-
- // 插入短信发送状态
- // report: [{"code":"000000","count":"1","mobile":"13632250447","msg":"OK","smsid":"8a978fe2eb3e69c3e5983b6e1cd33427"}]
- func saveSMSSendReport(batchID int, report []interface{}) error {
- if report == nil || len(report) == 0 {
- return nil
- }
- now := time.Now().Unix()
- valueStrs := make([]string, 0, len(report))
- values := make([]interface{}, 0, len(report))
- for _, item := range report {
- json := item.(map[string]interface{})
- valueStrs = append(valueStrs, "(?, ?, ?, ?, ?, ?, ?)")
-
- values = append(values, batchID)
- values = append(values, json["mobile"])
- if json["code"].(string) == "000000" {
- values = append(values, 1)
- } else {
- values = append(values, 0)
- }
- values = append(values, json["code"])
- values = append(values, json["msg"])
- values = append(values, now)
- values = append(values, now)
- }
- sql := fmt.Sprintf("INSERT INTO sgj_patient_sms_send_status (batch_id, mobile, status, code, msg, ctime, mtime) VALUES %v;", strings.Join(valueStrs, ", "))
-
- tx := writeDb.Begin()
- if err := tx.Exec(sql, values...).Error; err != nil {
- utils.ErrorLog("插入短信发送状态失败: %v", err)
- tx.Rollback()
- // 记录插入失败的操作
- }
- return tx.Commit().Error
- }
|