Przeglądaj źródła

Merge branch 'master' of http://git.shengws.com/zhangbj/scrm-go

xiaoming_global 5 lat temu
rodzic
commit
ee54ba712c

+ 33 - 18
controllers/admin_user/admin_controller.go Wyświetl plik

@@ -6,6 +6,9 @@ import (
6 6
 	base_service "SCRM/service"
7 7
 	"SCRM/service/admin_service"
8 8
 	"SCRM/service/role_service"
9
+	"SCRM/service/sms_service"
10
+	"strconv"
11
+	"time"
9 12
 
10 13
 	"github.com/astaxie/beego"
11 14
 )
@@ -49,22 +52,34 @@ func (this *AdminController) EditAdminUserInfo() {
49 52
 
50 53
 // /api/password/code [post]
51 54
 func (this *AdminController) CodeOfModifyPwd() {
52
-	//////////////////////////////
53
-	// 待把 SMS 的 service 添加完再重新放开
54
-	//////////////////////////////
55
-	this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
56
-	return
57
-	// adminUserInfo := this.GetAdminUserInfo()
58
-	// mobile := adminUserInfo.AdminUser.Mobile
59
-	// if err := service.SMSSendVerificationCode(mobile); err != nil {
60
-	// 	utils.ErrorLog("修改密码发送验证码失败:%v", err)
61
-	// 	this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
62
-	// 	return
63
-	// } else {
64
-	// 	this.ServeSuccessJSON(map[string]interface{}{
65
-	// 		"msg": "短信发送成功,有效期为10分钟",
66
-	// 	})
67
-	// }
55
+	adminUserInfo := this.GetAdminUserInfo()
56
+	mobile := adminUserInfo.AdminUser.Mobile
57
+
58
+	redisClient := base_service.RedisClient()
59
+	defer redisClient.Close()
60
+
61
+	cur_date := time.Now().Format("2006-01-02")
62
+	moblie_count, _ := redisClient.Get("scrm_verification_code_" + mobile + "_" + cur_date).Result()
63
+	moblie_count_int, _ := strconv.Atoi(moblie_count)
64
+	if moblie_max := 5; moblie_count_int >= moblie_max {
65
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeVerificationCodeLimit)
66
+		return
67
+	}
68
+
69
+	if code, err := sms_service.SMSSendVerificationCode(mobile); err != nil {
70
+		this.ErrorLog("修改密码发送验证码失败:%v", err)
71
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
72
+		return
73
+
74
+	} else {
75
+		cur_date := time.Now().Format("2006-01-02")
76
+		redisClient.Set("scrm_verification_code_"+mobile, code, time.Minute*10)
77
+		redisClient.Incr("scrm_verification_code_" + mobile + "_" + cur_date).Result()
78
+
79
+		this.ServeSuccessJSON(map[string]interface{}{
80
+			"msg": "短信发送成功,有效期为10分钟",
81
+		})
82
+	}
68 83
 }
69 84
 
70 85
 // /api/password/modify [post]
@@ -83,7 +98,7 @@ func (this *AdminController) ModifyPwd() {
83 98
 
84 99
 	redisClient := base_service.RedisClient()
85 100
 	defer redisClient.Close()
86
-	cachedCode, err := redisClient.Get("xt_modify_pwd_" + mobile).Result()
101
+	cachedCode, err := redisClient.Get("scrm_verification_code_" + mobile).Result()
87 102
 	if err != nil {
88 103
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
89 104
 		return
@@ -100,7 +115,7 @@ func (this *AdminController) ModifyPwd() {
100 115
 	}
101 116
 
102 117
 	// 清除验证码
103
-	redisClient.Del("xt_modify_pwd_" + mobile)
118
+	redisClient.Del("scrm_verification_code_" + mobile)
104 119
 	this.ServeSuccessJSON(map[string]interface{}{
105 120
 		"msg": "密码已修改",
106 121
 	})

+ 1 - 0
controllers/global/router_collector.go Wyświetl plik

@@ -5,4 +5,5 @@ func RegisterRouters() {
5 5
 	DistrictCtlRegistRouters()
6 6
 	QiniuCtlRegistRouters()
7 7
 	OpenWechatCtlRegistRouters()
8
+	SMSCBAPICtlRegistRouters()
8 9
 }

+ 50 - 0
controllers/global/sms_controller.go Wyświetl plik

@@ -0,0 +1,50 @@
1
+package global
2
+
3
+import (
4
+	base_ctl "SCRM/controllers"
5
+	"SCRM/service/sms_service"
6
+	"encoding/json"
7
+	"strconv"
8
+
9
+	"github.com/astaxie/beego"
10
+)
11
+
12
+func SMSCBAPICtlRegistRouters() {
13
+	beego.Router("/sms/callback", &SMSTplCallBackAPIController{}, "post:CallBack")
14
+}
15
+
16
+type SMSTplCallBackAPIController struct {
17
+	base_ctl.BaseAPIController
18
+}
19
+
20
+// /sms/callback [post]
21
+func (this *SMSTplCallBackAPIController) CallBack() {
22
+	var callbackParams map[string]string
23
+	json.Unmarshal(this.Ctx.Input.RequestBody, &callbackParams)
24
+	this.TraceLog("短信平台回调: params: %v", callbackParams)
25
+
26
+	code := callbackParams["code"]
27
+	templateIDStr := callbackParams["templateid"]
28
+
29
+	if len(templateIDStr) != 0 {
30
+		templateID, _ := strconv.Atoi(templateIDStr)
31
+		if code == "2" {
32
+			err := sms_service.SMSUCPaasTemplateApproved(int64(templateID))
33
+			if err != nil {
34
+				this.ErrorLog("短信平台回调(成功)——数据库操作失败: err: %v; templateID: %v", err, templateID)
35
+			}
36
+		} else if code == "3" {
37
+			err := sms_service.SMSUCPaasTemplateUnapproved(int64(templateID))
38
+			if err != nil {
39
+				this.ErrorLog("短信平台回调(失败)——数据库操作失败: err: %v; templateID: %v", err, templateID)
40
+			}
41
+		}
42
+
43
+	} else {
44
+		this.TraceLog("短信平台回调: %+v", this.Ctx.Request)
45
+	}
46
+
47
+	this.ServeSuccessJSON(map[string]interface{}{
48
+		"result": true,
49
+	})
50
+}

+ 1 - 0
controllers/kefu/tencent_usersig_api_controller.go Wyświetl plik

@@ -62,3 +62,4 @@ func (c *TencentUsersigApiController) GetUsersig() {
62 62
 	c.ServeSuccessJSON(returnData)
63 63
 	return
64 64
 }
65
+

+ 20 - 3
controllers/members/members_controller.go Wyświetl plik

@@ -6,16 +6,17 @@ import (
6 6
 	"SCRM/models"
7 7
 	"SCRM/service/member_service"
8 8
 	"SCRM/utils"
9
+	"encoding/json"
10
+	"fmt"
9 11
 	"reflect"
10 12
 	"time"
11
-    "fmt"
12
-	"encoding/json"
13 13
 
14 14
 	"github.com/astaxie/beego"
15 15
 )
16 16
 
17 17
 func MemberCtlRegistRouters() {
18 18
 	beego.Router("/api/members", &MembersAPIController{}, "get:GetMembers")
19
+	beego.Router("/api/members/all", &MembersAPIController{}, "get:GetAllMembers")
19 20
 	beego.Router("/api/member/create", &MembersAPIController{}, "Post:CreateMember")
20 21
 	beego.Router("/api/member/edit", &MembersAPIController{}, "Put:EditMember")
21 22
 	beego.Router("/api/members/delete", &MembersAPIController{}, "Delete:DeleteMembers")
@@ -35,7 +36,7 @@ func (c *MembersAPIController) GetMembers() {
35 36
 	source, _ := c.GetInt64("source", 0)
36 37
 	tag, _ := c.GetInt64("tag", 0)
37 38
 	init, _ := c.GetInt64("init", 0)
38
-	fmt.Println("level",level,"source",source)
39
+	fmt.Println("level", level, "source", source)
39 40
 	if page <= 0 {
40 41
 		page = 1
41 42
 	}
@@ -107,6 +108,22 @@ func (c *MembersAPIController) GetMembers() {
107 108
 	return
108 109
 }
109 110
 
111
+func (c *MembersAPIController) GetAllMembers() {
112
+
113
+	adminUserInfo := c.GetAdminUserInfo()
114
+	members, err := member_service.GetAllMemberList(adminUserInfo.CurrentOrgId)
115
+	if err != nil {
116
+		c.ServeFailJsonSend(enums.ErrorCodeDataException, "获取列表失败")
117
+		return
118
+	}
119
+
120
+	returnData := make(map[string]interface{}, 0)
121
+	returnData["members"] = members
122
+
123
+	c.ServeSuccessJSON(returnData)
124
+	return
125
+}
126
+
110 127
 func (c *MembersAPIController) CreateMember() {
111 128
 
112 129
 	adminUserInfo := c.GetAdminUserInfo()

+ 18 - 18
controllers/role/admin_controller.go Wyświetl plik

@@ -7,6 +7,7 @@ import (
7 7
 	"SCRM/service"
8 8
 	base_service "SCRM/service"
9 9
 	"SCRM/service/role_service"
10
+	"SCRM/service/sms_service"
10 11
 	"time"
11 12
 
12 13
 	"github.com/astaxie/beego"
@@ -36,7 +37,7 @@ func (this *AdminAPIController) AdminMainView() {
36 37
 
37 38
 	viewModels, total, getAdminsErr := role_service.GetAdminUsersAndLoginInfo(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, 1, 10)
38 39
 	if getAdminsErr != nil {
39
-		//beego.Error("获取管理员列表失败:", getAdminsErr)
40
+		this.ErrorLog("获取管理员列表失败:", getAdminsErr)
40 41
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
41 42
 		return
42 43
 	}
@@ -62,7 +63,7 @@ func (this *AdminAPIController) Admins() {
62 63
 	page, _ := this.GetInt("page")
63 64
 	viewModels, total, getAdminsErr := role_service.GetAdminUsersAndLoginInfo(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, page, 10)
64 65
 	if getAdminsErr != nil {
65
-		//beego.Error("获取管理员列表失败:", getAdminsErr)
66
+		this.ErrorLog("获取管理员列表失败:", getAdminsErr)
66 67
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
67 68
 	} else {
68 69
 		this.ServeSuccessJSON(map[string]interface{}{
@@ -82,7 +83,7 @@ func (this *AdminAPIController) AddAdminInitData() {
82 83
 
83 84
 	roles, getRoleErr := role_service.GetAllValidRoles(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId)
84 85
 	if getRoleErr != nil {
85
-		//beego.Error("获取所有角色失败:", getRoleErr)
86
+		this.ErrorLog("获取所有角色失败:", getRoleErr)
86 87
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
87 88
 		return
88 89
 	}
@@ -126,7 +127,7 @@ func (this *AdminAPIController) AddAdmin() {
126 127
 
127 128
 	isRoleExist, getRoleErr := role_service.IsRoleExist(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, roleId)
128 129
 	if getRoleErr != nil {
129
-		//beego.Error("查询角色是否存在时失败:", getRoleErr)
130
+		this.ErrorLog("查询角色是否存在时失败:", getRoleErr)
130 131
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
131 132
 		return
132 133
 	}
@@ -137,7 +138,7 @@ func (this *AdminAPIController) AddAdmin() {
137 138
 
138 139
 	// 判断该应用是否已存在该手机号
139 140
 	if isMobileDidUsed, err := role_service.IsMobileDidUsedAtApp(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, mobile); err != nil {
140
-		//beego.Error("查询用户是否已被添加为管理员时失败:", err)
141
+		this.ErrorLog("查询用户是否已被添加为管理员时失败:", err)
141 142
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
142 143
 		return
143 144
 	} else {
@@ -159,18 +160,17 @@ func (this *AdminAPIController) AddAdmin() {
159 160
 
160 161
 	_, password, createErr := role_service.CreateGeneralAdminUser(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, mobile, name, userType, userTitle, intro, roleId)
161 162
 	if createErr != nil {
162
-		//beego.Error("创建管理员失败:", createErr)
163
+		this.ErrorLog("创建管理员失败:", createErr)
163 164
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDBCreate)
164 165
 		return
165 166
 
166 167
 	} else {
167 168
 		this.TraceLog("%v", password)
168
-		//beego.Trace("用户密码:", password)
169 169
 		// 发送短信通知这个手机号
170
-		// sendSMSErr := role_service.SMSSendInviteMobileToJoinOrgAdmin(name, mobile, password)
171
-		// if sendSMSErr != nil {
172
-		// 	//beego.Error("发送邀请短信失败:%v", sendSMSErr)
173
-		// }
170
+		sendSMSErr := sms_service.SMSSendInviteMobileToJoinOrgAdmin(name, mobile, password)
171
+		if sendSMSErr != nil {
172
+			this.ErrorLog("发送邀请短信失败:%v", sendSMSErr)
173
+		}
174 174
 
175 175
 		this.ServeSuccessJSON(nil)
176 176
 		return
@@ -194,7 +194,7 @@ func (this *AdminAPIController) EditAdminInitData() {
194 194
 
195 195
 	adminUserViewModel, getInfoErr := role_service.GetGeneralAdminUser(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, admin_user_id)
196 196
 	if getInfoErr != nil {
197
-		//beego.Error("获取管理员信息失败:", getInfoErr)
197
+		this.ErrorLog("获取管理员信息失败:", getInfoErr)
198 198
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
199 199
 		return
200 200
 	}
@@ -205,7 +205,7 @@ func (this *AdminAPIController) EditAdminInitData() {
205 205
 
206 206
 	roles, getRoleErr := role_service.GetAllValidRoles(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId)
207 207
 	if getRoleErr != nil {
208
-		//beego.Error("获取所有角色失败:", getRoleErr)
208
+		this.ErrorLog("获取所有角色失败:", getRoleErr)
209 209
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
210 210
 		return
211 211
 	}
@@ -250,7 +250,7 @@ func (this *AdminAPIController) EditAdmin() {
250 250
 
251 251
 	appRole, getAppRoleErr := role_service.GetAppRole(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, adminUserId)
252 252
 	if getAppRoleErr != nil {
253
-		//beego.Error("查询管理员信息时失败:", getAppRoleErr)
253
+		this.ErrorLog("查询管理员信息时失败:", getAppRoleErr)
254 254
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
255 255
 		return
256 256
 	}
@@ -261,7 +261,7 @@ func (this *AdminAPIController) EditAdmin() {
261 261
 
262 262
 	isRoleExist, getRoleErr := role_service.IsRoleExist(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, roleId)
263 263
 	if getRoleErr != nil {
264
-		//beego.Error("查询角色是否存在时失败:", getRoleErr)
264
+		this.ErrorLog("查询角色是否存在时失败:", getRoleErr)
265 265
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
266 266
 		return
267 267
 	}
@@ -278,7 +278,7 @@ func (this *AdminAPIController) EditAdmin() {
278 278
 	appRole.ModifyTime = time.Now().Unix()
279 279
 	saveErr := role_service.SaveAppRole(appRole)
280 280
 	if saveErr != nil {
281
-		//beego.Error("修改App_Role失败:", saveErr)
281
+		this.ErrorLog("修改App_Role失败:", saveErr)
282 282
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDBUpdate)
283 283
 
284 284
 	} else {
@@ -303,7 +303,7 @@ func (this *AdminAPIController) AdminSetStatus() {
303 303
 	}
304 304
 	appRole, getAppRoleErr := role_service.GetAppRole(adminUserInfo.CurrentOrgId, adminUserInfo.CurrentAppId, userID)
305 305
 	if getAppRoleErr != nil {
306
-		//beego.Error("查询管理员信息失败:", getAppRoleErr)
306
+		this.ErrorLog("查询管理员信息失败:", getAppRoleErr)
307 307
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
308 308
 		return
309 309
 	} else if appRole == nil {
@@ -327,7 +327,7 @@ func (this *AdminAPIController) AdminSetStatus() {
327 327
 	appRole.ModifyTime = time.Now().Unix()
328 328
 	saveErr := role_service.SaveAppRole(appRole)
329 329
 	if saveErr != nil {
330
-		//beego.Error("保存AppRole失败:", saveErr)
330
+		this.ErrorLog("保存AppRole失败:", saveErr)
331 331
 		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDBUpdate)
332 332
 
333 333
 	} else {

+ 5 - 0
controllers/sms/router_collector.go Wyświetl plik

@@ -0,0 +1,5 @@
1
+package sms
2
+
3
+func RegisterRouters() {
4
+	SMSCtlRegistRouters()
5
+}

+ 227 - 0
controllers/sms/sms_controller.go Wyświetl plik

@@ -0,0 +1,227 @@
1
+package sms
2
+
3
+import (
4
+	base_ctl "SCRM/controllers"
5
+	"SCRM/enums"
6
+	"SCRM/service/marketing_tool_service"
7
+	"SCRM/service/member_service"
8
+	"SCRM/service/sms_service"
9
+	"fmt"
10
+	"strings"
11
+
12
+	"github.com/astaxie/beego"
13
+)
14
+
15
+func SMSCtlRegistRouters() {
16
+	beego.Router("/api/sms/records", &SMSController{}, "get:GetSendRecords")
17
+	beego.Router("/api/sms/sendinit", &SMSController{}, "get:SendInitData")
18
+	beego.Router("/api/sms/tagfiltercount", &SMSController{}, "get:TagFilterCustomerCount")
19
+
20
+	beego.Router("/api/sms/send2all", &SMSController{}, "post:Send2AllCustomers")
21
+	beego.Router("/api/sms/send2tag", &SMSController{}, "post:Send2TagCustomers")
22
+	beego.Router("/api/sms/send2specific", &SMSController{}, "post:Send2SpecificCustomers")
23
+}
24
+
25
+type SMSController struct {
26
+	base_ctl.BaseAuthAPIController
27
+}
28
+
29
+// /api/sms/records [get]
30
+// @param page:int
31
+func (this *SMSController) GetSendRecords() {
32
+	page, _ := this.GetInt("page")
33
+	if page <= 0 {
34
+		page = 1
35
+	}
36
+
37
+	adminUserInfo := this.GetAdminUserInfo()
38
+	records, total, getRecordErr := sms_service.GetBatchSendRecords(adminUserInfo.CurrentOrgId, page, 10)
39
+	if getRecordErr != nil {
40
+		this.ErrorLog("获取短信批次记录失败:%v", getRecordErr)
41
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
42
+		return
43
+	}
44
+
45
+	this.ServeSuccessJSON(map[string]interface{}{
46
+		"records": records,
47
+		"total":   total,
48
+	})
49
+}
50
+
51
+// /api/sms/sendinit [get]
52
+// @param action?:int 事件类型 1.活动
53
+// @param id?:int 事件对应的id,如活动id
54
+func (this *SMSController) SendInitData() {
55
+	adminUserInfo := this.GetAdminUserInfo()
56
+	tags, getTagErr := member_service.GetTagList(adminUserInfo.CurrentOrgId)
57
+	if getTagErr != nil {
58
+		this.ErrorLog("获取标签失败:%v", getTagErr)
59
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
60
+		return
61
+	}
62
+
63
+	action, _ := this.GetInt("action")
64
+	id, _ := this.GetInt64("id")
65
+	defaultSMSContent := ""
66
+	if id > 0 {
67
+		if action == 1 {
68
+			activity, _ := marketing_tool_service.GetActivityWithID(adminUserInfo.CurrentOrgId, id)
69
+			if activity != nil { // 因为 defaultSMSContent 不是必须的,所以不理会出现的错误
70
+				shareObj, _ := marketing_tool_service.GetActivityWxShareByActivityID(id)
71
+				if shareObj != nil {
72
+					defaultSMSContent = fmt.Sprintf("给您分享一个活动,点击参与:%v", shareObj.ShortURL)
73
+				}
74
+			}
75
+		}
76
+	}
77
+
78
+	this.ServeSuccessJSON(map[string]interface{}{
79
+		"tags":            tags,
80
+		"default_content": defaultSMSContent,
81
+	})
82
+}
83
+
84
+// /api/sms/tagfiltercount [get]
85
+// @param tags:string tag_id 以逗号隔开 (1,2,4)
86
+func (this *SMSController) TagFilterCustomerCount() {
87
+	tagIDsStr := this.GetString("tags")
88
+	if len(tagIDsStr) == 0 {
89
+		this.ServeSuccessJSON(map[string]interface{}{
90
+			"count": 0,
91
+		})
92
+		return
93
+	}
94
+	tagIDs := strings.Split(tagIDsStr, ",")
95
+	count, getCountErr := member_service.GetTagUsersCountWithMobileByTagIDs(tagIDs)
96
+	if getCountErr != nil {
97
+		this.ErrorLog("获取标签人数失败:%v", getCountErr)
98
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
99
+		return
100
+	}
101
+	this.ServeSuccessJSON(map[string]interface{}{
102
+		"count": count,
103
+	})
104
+}
105
+
106
+// /api/sms/send2all [post]
107
+// @param content:string
108
+func (this *SMSController) Send2AllCustomers() {
109
+	content := this.GetString("content")
110
+	if len(content) == 0 || strings.Contains(content, "【") || strings.Contains(content, "】") {
111
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
112
+		return
113
+	}
114
+
115
+	adminUserInfo := this.GetAdminUserInfo()
116
+	customers, getCustomerErr := member_service.GetAllCustomersWithMobile(adminUserInfo.CurrentOrgId)
117
+	if getCustomerErr != nil {
118
+		this.ErrorLog("获取所有客户失败:%v", getCustomerErr)
119
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
120
+		return
121
+	} else if len(customers) == 0 {
122
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSNoCustomer)
123
+		return
124
+	}
125
+
126
+	mobiles := make([]string, 0, len(customers))
127
+	for _, customer := range customers {
128
+		mobiles = append(mobiles, customer.Mobile)
129
+	}
130
+
131
+	sendErr := sms_service.SMSOrgSendWithCustomContent(adminUserInfo.CurrentOrgId, content, mobiles)
132
+	if sendErr != nil {
133
+		if sendErr == sms_service.SMSErrorFreeLimitInsufficient {
134
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSLimitInsufficient)
135
+			return
136
+		} else {
137
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
138
+			return
139
+		}
140
+	} else {
141
+		this.ServeSuccessJSON(nil)
142
+	}
143
+}
144
+
145
+// /api/sms/send2tag [post]
146
+// @param content:string
147
+// @param tags:string 标签id以逗号隔开 (1,2,43)
148
+func (this *SMSController) Send2TagCustomers() {
149
+	content := this.GetString("content")
150
+	tagIDsStr := this.GetString("tags")
151
+	if len(content) == 0 || strings.Contains(content, "【") || strings.Contains(content, "】") || len(tagIDsStr) == 0 {
152
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
153
+		return
154
+	}
155
+
156
+	tagIDs := strings.Split(tagIDsStr, ",")
157
+	adminUserInfo := this.GetAdminUserInfo()
158
+	customers, getCustomerErr := member_service.GetTagCustomersWithMobileByTagIDs(adminUserInfo.CurrentOrgId, tagIDs)
159
+	if getCustomerErr != nil {
160
+		this.ErrorLog("获取标签指定客户失败:%v", getCustomerErr)
161
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
162
+		return
163
+	} else if len(customers) == 0 {
164
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSNoCustomer)
165
+		return
166
+	}
167
+
168
+	mobiles := make([]string, 0, len(customers))
169
+	for _, customer := range customers {
170
+		mobiles = append(mobiles, customer.Mobile)
171
+	}
172
+
173
+	sendErr := sms_service.SMSOrgSendWithCustomContent(adminUserInfo.CurrentOrgId, content, mobiles)
174
+	if sendErr != nil {
175
+		if sendErr == sms_service.SMSErrorFreeLimitInsufficient {
176
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSLimitInsufficient)
177
+			return
178
+		} else {
179
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
180
+			return
181
+		}
182
+	} else {
183
+		this.ServeSuccessJSON(nil)
184
+	}
185
+}
186
+
187
+// /api/sms/send2specific [post]
188
+// @param content:string
189
+// @param ids:string customer_id以逗号隔开 (4,11)
190
+func (this *SMSController) Send2SpecificCustomers() {
191
+	content := this.GetString("content")
192
+	customerIDsStr := this.GetString("ids")
193
+	if len(content) == 0 || strings.Contains(content, "【") || strings.Contains(content, "】") || len(customerIDsStr) == 0 {
194
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
195
+		return
196
+	}
197
+
198
+	customerIDs := strings.Split(customerIDsStr, ",")
199
+	adminUserInfo := this.GetAdminUserInfo()
200
+	customers, getCustomerErr := member_service.GetSpecificCustomersWithMobile(adminUserInfo.CurrentOrgId, customerIDs)
201
+	if getCustomerErr != nil {
202
+		this.ErrorLog("获取指定客户失败:%v", getCustomerErr)
203
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
204
+		return
205
+	} else if len(customers) == 0 {
206
+		this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSNoCustomer)
207
+		return
208
+	}
209
+
210
+	mobiles := make([]string, 0, len(customers))
211
+	for _, customer := range customers {
212
+		mobiles = append(mobiles, customer.Mobile)
213
+	}
214
+
215
+	sendErr := sms_service.SMSOrgSendWithCustomContent(adminUserInfo.CurrentOrgId, content, mobiles)
216
+	if sendErr != nil {
217
+		if sendErr == sms_service.SMSErrorFreeLimitInsufficient {
218
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSMSLimitInsufficient)
219
+			return
220
+		} else {
221
+			this.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
222
+			return
223
+		}
224
+	} else {
225
+		this.ServeSuccessJSON(nil)
226
+	}
227
+}

+ 0 - 2
controllers/短信模块临时备注 Wyświetl plik

@@ -1,2 +0,0 @@
1
-修改个人信息
2
-权限管理中添加管理员

+ 8 - 0
enums/error_code.go Wyświetl plik

@@ -21,6 +21,7 @@ const ( // ErrorCode
21 21
 	ErrorCodeUserWasForbidden               = 6015
22 22
 	ErrorCodeNeverCreateTypeApp             = 6016
23 23
 	ErrorCodeContactSuperAdminCreateTypeApp = 6017
24
+	ErrorCodeVerificationCodeLimit          = 6018
24 25
 
25 26
 	// 数据验证错误 7000+
26 27
 	ErrorCodeMobileFormat     = 7001
@@ -57,6 +58,9 @@ const ( // ErrorCode
57 58
 	ErrorCodeHetongHad         = 4007
58 59
 	ErrorCodeCreateHetongFail  = 4008
59 60
 	ErrorCodePatientReachLimit = 4009
61
+
62
+	ErrorCodeSMSNoCustomer        = 10001
63
+	ErrorCodeSMSLimitInsufficient = 10002
60 64
 )
61 65
 
62 66
 var ErrCodeMsgs = map[int]string{
@@ -78,6 +82,7 @@ var ErrCodeMsgs = map[int]string{
78 82
 	ErrorCodeUserWasForbidden:               "该用户被禁用",
79 83
 	ErrorCodeNeverCreateTypeApp:             "未创建此种应用",
80 84
 	ErrorCodeContactSuperAdminCreateTypeApp: "请联系超级管理员开通此种应用",
85
+	ErrorCodeVerificationCodeLimit:          "该号码今日验证码已超限",
81 86
 
82 87
 	// 数据验证错误
83 88
 	ErrorCodeMobileFormat:     "手机号格式错误",
@@ -114,6 +119,9 @@ var ErrCodeMsgs = map[int]string{
114 119
 	ErrorCodeHetongHad:         "合同已经存在!",
115 120
 	ErrorCodeCreateHetongFail:  "合同创建失败",
116 121
 	ErrorCodePatientReachLimit: "患者数已达到当前服务版本病人数,需要升级到更高的版本",
122
+
123
+	ErrorCodeSMSNoCustomer:        "没有目标客户",
124
+	ErrorCodeSMSLimitInsufficient: "当月可用短信额度不足",
117 125
 }
118 126
 
119 127
 type SGJError struct {

+ 128 - 0
jobcron/activity_short_url_job.go Wyświetl plik

@@ -0,0 +1,128 @@
1
+package jobcron
2
+
3
+import (
4
+	"SCRM/service"
5
+	"SCRM/utils"
6
+	"encoding/json"
7
+	"fmt"
8
+	"io/ioutil"
9
+	"net/http"
10
+	"net/url"
11
+	"strings"
12
+	"time"
13
+
14
+	"github.com/astaxie/beego"
15
+	"github.com/robfig/cron"
16
+)
17
+
18
+// cron表达式 https://www.cnblogs.com/zuxingyu/p/6023919.html
19
+var activityShortUrlCronJob *cron.Cron
20
+
21
+func init() {
22
+	activityShortUrlCronJob = cron.New()
23
+	spec := "0 0 0 * * ?" // 每天晚上12点整检查活动短链接库存充不充足
24
+	activityShortUrlCronJob.AddFunc(spec, func() {
25
+		CreateActivityShortURLIfNeeded()
26
+	})
27
+}
28
+
29
+type CreateShortURLServiceMaxId struct {
30
+	Value int
31
+}
32
+
33
+func CreateActivityShortURLIfNeeded() {
34
+	var maxActivityId CreateShortURLServiceMaxId
35
+	service.PatientReadDB().Raw("SELECT MAX(id) AS value FROM sgj_patient_activity").Scan(&maxActivityId)
36
+	utils.TraceLog("当前最大的活动 ID 为:%v", maxActivityId)
37
+	var maxShortURLId CreateShortURLServiceMaxId
38
+	service.PatientReadDB().Raw("SELECT MAX(id) AS value FROM sgj_patient_activity_wx_share").Scan(&maxShortURLId)
39
+	utils.TraceLog("当前最大的活动短链接 ID 为:%v", maxShortURLId)
40
+
41
+	if maxShortURLId.Value-maxActivityId.Value <= 300 {
42
+		utils.TraceLog("即将生成更多活动短链接")
43
+		go CreateMoreActivityShortURL(maxShortURLId.Value)
44
+	} else {
45
+		utils.TraceLog("活动短链接充足,无需生成")
46
+	}
47
+}
48
+
49
+// 新浪短链接服务示例 https://www.cnblogs.com/azure/archive/2012/08/29/WeiboAPI.html
50
+func CreateMoreActivityShortURL(lastShortURLID int) {
51
+	var maxUrlCountPerRequest int = 20
52
+	createCount, _ := beego.AppConfig.Int("create_short_url_count_every_time")
53
+	pageCount := createCount / maxUrlCountPerRequest
54
+	tx := service.PatientWriteDB().Begin()
55
+	for i := 0; i < pageCount; i++ {
56
+		longURLParams := make([]string, 0, maxUrlCountPerRequest)
57
+		for longURLIndex := 1; longURLIndex < maxUrlCountPerRequest+1; longURLIndex++ {
58
+			id := lastShortURLID + i*maxUrlCountPerRequest + longURLIndex
59
+			param := fmt.Sprintf("url_long=%v", fmt.Sprintf("%v/weixin/activity/%v", beego.AppConfig.String("weixinwebdomain"), id))
60
+			longURLParams = append(longURLParams, param)
61
+		}
62
+
63
+		urlStr := fmt.Sprintf("http://api.weibo.com/2/short_url/shorten.json?source=2849184197&%v", strings.Join(longURLParams, "&"))
64
+		u, _ := url.Parse(urlStr)
65
+		u.RawQuery = u.Query().Encode()
66
+		// fmt.Println(u.String())
67
+
68
+		resp, requestErr := http.Get(u.String())
69
+		if requestErr != nil {
70
+			utils.ErrorLog("http.Get error: %v", requestErr)
71
+			continue
72
+		}
73
+		result, readErr := ioutil.ReadAll(resp.Body)
74
+		resp.Body.Close()
75
+		if readErr != nil {
76
+			utils.ErrorLog("ioutil.ReadAll error: %v", readErr)
77
+			continue
78
+		}
79
+		// fmt.Println(string(result))
80
+		var respJSON map[string]interface{}
81
+		if err := json.Unmarshal([]byte(string(result)), &respJSON); err != nil {
82
+			utils.ErrorLog("json.Unmarshal error: %v\n", err)
83
+			continue
84
+		}
85
+		// fmt.Printf("resp: %v\n", respJSON)
86
+		if respJSON["error"] != nil {
87
+			utils.ErrorLog("request error: %v\n", respJSON)
88
+			continue
89
+		}
90
+
91
+		urlJSONs := respJSON["urls"].([]interface{})
92
+		now := time.Now().Unix()
93
+		valueStrs := make([]string, 0, len(urlJSONs))
94
+		values := make([]interface{}, 0, len(urlJSONs)*9)
95
+
96
+		for index, url := range urlJSONs {
97
+			urlJSON := url.(map[string]interface{})
98
+			shortURL := urlJSON["url_short"].(string)
99
+			id := lastShortURLID + i*maxUrlCountPerRequest + (index + 1)
100
+			// CreateActivityWxShareWithShortURL(id, shortURL)
101
+
102
+			// (`id`,`activity_id`,`title`,`subtitle`,`image`,`status`,`ctime`,`mtime`,`short_url`)
103
+			valueStrs = append(valueStrs, "(?, ?, ?, ?, ?, ?, ?, ?, ?)")
104
+			values = append(values, id)
105
+			values = append(values, id)
106
+			values = append(values, "")
107
+			values = append(values, "")
108
+			values = append(values, "")
109
+			values = append(values, 1)
110
+			values = append(values, now)
111
+			values = append(values, now)
112
+			values = append(values, shortURL)
113
+		}
114
+
115
+		sql := fmt.Sprintf("INSERT INTO sgj_patient_activity_wx_share (id, activity_id, title, subtitle, image, status, ctime, mtime, short_url) VALUES %v;", strings.Join(valueStrs, ", "))
116
+		if err := tx.Exec(sql, values...).Error; err != nil {
117
+			utils.ErrorLog("插入活动短链接失败: %v", err)
118
+			tx.Rollback()
119
+			break
120
+		}
121
+	}
122
+	tx.Commit()
123
+	utils.TraceLog("完成生成更多活动短链接")
124
+}
125
+
126
+func BeginCreateActivityShortURLCronJob() {
127
+	activityShortUrlCronJob.Start()
128
+}

+ 42 - 0
jobcron/qiniu_job.go Wyświetl plik

@@ -0,0 +1,42 @@
1
+package jobcron
2
+
3
+import (
4
+	"SCRM/service"
5
+	"fmt"
6
+
7
+	"github.com/astaxie/beego"
8
+	"github.com/qiniu/api.v7/auth/qbox"
9
+	"github.com/qiniu/api.v7/storage"
10
+	"github.com/robfig/cron"
11
+)
12
+
13
+var qnTokenCronJob *cron.Cron
14
+
15
+func init() {
16
+	qnTokenCronJob = cron.New()
17
+	qnTokenCronJob.AddFunc("@every 1h50m", func() {
18
+		go RequestQNToken()
19
+	})
20
+}
21
+
22
+func RequestQNToken() {
23
+	accessKey := beego.AppConfig.String("qiniu_accesskey")
24
+	secretKey := beego.AppConfig.String("qiniu_secretkey")
25
+	bucket := beego.AppConfig.String("qiniu_bucket")
26
+	putPolicy := storage.PutPolicy{
27
+		Scope:      bucket,
28
+		ReturnBody: `{"url":"$(key)","hash":"$(etag)","state":"SUCCESS"}`,
29
+	}
30
+	putPolicy.Expires = 7200
31
+	mac := qbox.NewMac(accessKey, secretKey)
32
+	upToken := putPolicy.UploadToken(mac)
33
+	fmt.Println("new qiniu token: ", upToken)
34
+
35
+	redisClient := service.RedisClient()
36
+	defer redisClient.Close()
37
+	redisClient.Set("qn_token", upToken, 0)
38
+}
39
+
40
+func BeginRerequestQNTokenCronJob() {
41
+	qnTokenCronJob.Start()
42
+}

+ 24 - 0
jobcron/sms_job.go Wyświetl plik

@@ -0,0 +1,24 @@
1
+package jobcron
2
+
3
+import (
4
+	"SCRM/service/sms_service"
5
+	"SCRM/utils"
6
+
7
+	"github.com/robfig/cron"
8
+)
9
+
10
+// cron表达式 https://www.cnblogs.com/zuxingyu/p/6023919.html
11
+var smsCronJob *cron.Cron
12
+
13
+func init() {
14
+	smsCronJob = cron.New()
15
+	spec := "0 */5 * * * ?" // 每五分钟检查一次
16
+	smsCronJob.AddFunc(spec, func() {
17
+		utils.TraceLog("定时发送未发送的短信")
18
+		sms_service.SMSSendUnsendBatchRealTime()
19
+	})
20
+}
21
+
22
+func BeginSendUnsendSMSCronJob() {
23
+	smsCronJob.Start()
24
+}

+ 8 - 2
main.go Wyświetl plik

@@ -5,8 +5,6 @@ import (
5 5
 	_ "SCRM/routers"
6 6
 	"SCRM/service"
7 7
 
8
-	// "SCRM/jobcron"
9
-
10 8
 	"github.com/astaxie/beego"
11 9
 )
12 10
 
@@ -22,7 +20,15 @@ func main() {
22 20
 		// jobcron.RequestMpWechatAccessToken()
23 21
 		// jobcron.ReqJsapiTicket()
24 22
 	}()
23
+
25 24
 	jobcron.StartOpenWechatCron()
25
+	jobcron.BeginSendUnsendSMSCronJob()
26
+
27
+	jobcron.CreateActivityShortURLIfNeeded()
28
+	jobcron.BeginCreateActivityShortURLCronJob()
29
+
30
+	go jobcron.RequestQNToken()
31
+	jobcron.BeginRerequestQNTokenCronJob()
26 32
 
27 33
 	beego.Run()
28 34
 }

+ 77 - 0
models/sms_models.go Wyświetl plik

@@ -0,0 +1,77 @@
1
+package models
2
+
3
+type SMSTemplate struct {
4
+	ID           int64  `gorm:"column:id"`
5
+	TemplateType int8   `gorm:"column:template_type"` // 模板类型 1.默认模板 2.自定义模板
6
+	TemplateID   int64  `gorm:"column:template_id"`   // 短信平台上的模板 id
7
+	MessageType  int8   `gorm:"column:message_type"`  // 短信类型 1.通知短信 2.服务短信
8
+	Content      string // 模板内容
9
+	// ParamTypes   string `gorm:"column:param_types"` // 字段因业务没有采用默认模板的方式而弃用了!!!! 模板内容里的参数类型:1,2,3 。参数类型以逗号隔开,位置代表了参数‘{n+1}’,类型暂时有:1.用户名 2.商家简称 3.链接 4.商品名称 5.活动名称 6.验证码。(自定义模板是没有值的,因为它没有参数)
10
+	Autograph  string // 短信签名
11
+	OrgID      int64  `gorm:"column:org_id"` // 模板所属机构(自定义模板才有值)
12
+	Status     int8   // 模板状态 0.无效 1.有效 2.审核中
13
+	CreateTime int64  `gorm:"column:ctime"` // 创建时间
14
+	ModifyTime int64  `gorm:"column:mtime"` // 修改时间
15
+}
16
+
17
+func (SMSTemplate) TableName() string {
18
+	return "sgj_patient_sms_template"
19
+}
20
+
21
+type UserSMSFreeLimit struct {
22
+	ID         int64  `gorm:"column:id"`
23
+	OrgID      int64  `gorm:"column:org_id"`      // 机构 ID
24
+	TotalCount int    `gorm:"column:total_count"` // 免费额度,单位:条,下同
25
+	UsedCount  int    `gorm:"column:used_count"`  // 已用额度
26
+	ValidMonth string `gorm:"column:valid_month"` // 生效月份,格式:201804
27
+	Status     int8   // 状态 0.无效 1.有效
28
+	CreateTime int64  `gorm:"column:ctime"` // 创建时间
29
+	ModifyTime int64  `gorm:"column:mtime"` // 修改时间
30
+}
31
+
32
+func (UserSMSFreeLimit) TableName() string {
33
+	return "sgj_patient_user_sms_free_limit"
34
+}
35
+
36
+type SMSBatch struct {
37
+	ID          int64  `gorm:"column:id"`
38
+	OrgID       int64  `gorm:"column:org_id"`          // 机构 ID
39
+	TemplateID  int64  `gorm:"column:sms_template_id"` // 对应短信平台上的模板 ID
40
+	Autograph   string // 短信签名
41
+	Params      string // 模板参数,以“,”隔开
42
+	FullContent string `gorm:"column:full_content"` // 完整短信内容
43
+	Mobiles     string // 接收手机号,以“,”隔开
44
+	SendTime    int64  `gorm:"column:send_time"` // 发送时间
45
+	Status      int8   // 状态: 1.待审核 2.审核未通过 3.未发送 4.已发送 5.发送失败
46
+	CreateTime  int64  `gorm:"column:ctime"` // 创建时间
47
+	ModifyTime  int64  `gorm:"column:mtime"` // 修改时间
48
+}
49
+
50
+func (SMSBatch) TableName() string {
51
+	return "sgj_patient_sms_batch"
52
+}
53
+
54
+type SMSSendStatus struct {
55
+	ID         int64  `gorm:"column:id"`
56
+	BatchID    int64  `gorm:"batch_id"` // 批次 ID,对应 SMSBatch.ID
57
+	Mobile     string // 接收手机号
58
+	Status     int8   // 发送结果状态: 0.失败 1.成功
59
+	Code       string // 短信平台返回的错误码
60
+	Msg        string // 短信平台返回的错误信息
61
+	CreateTime int64  `gorm:"column:ctime"` // 创建时间
62
+	ModifyTime int64  `gorm:"column:mtime"` // 修改时间
63
+}
64
+
65
+func (SMSSendStatus) TableName() string {
66
+	return "sgj_patient_sms_send_status"
67
+}
68
+
69
+// 1.待审核 2.审核未通过 3.未发送 4.已发送 5.发送失败
70
+const (
71
+	SMSBatchStatusInReview   = int8(1)
72
+	SMSBatchStatusUnapproved = int8(2)
73
+	SMSBatchStatusUnsend     = int8(3)
74
+	SMSBatchStatusDidSend    = int8(4)
75
+	SMSBatchStatusSendFailed = int8(5)
76
+	SMSBatchStatusSending    = int8(6)
77
+)

+ 4 - 3
routers/router.go Wyświetl plik

@@ -4,16 +4,17 @@ import (
4 4
 	"SCRM/controllers/admin_user"
5 5
 	"SCRM/controllers/article"
6 6
 	"SCRM/controllers/global"
7
+	"SCRM/controllers/kefu"
7 8
 	"SCRM/controllers/login"
8 9
 	"SCRM/controllers/marketing_tool"
9 10
 	"SCRM/controllers/members"
10 11
 	"SCRM/controllers/mpwechat"
11 12
 	"SCRM/controllers/role"
12
-	"SCRM/controllers/kefu"
13
+	"SCRM/controllers/sms"
14
+	"SCRM/controllers/staff"
13 15
 
14 16
 	"github.com/astaxie/beego"
15 17
 	"github.com/astaxie/beego/plugins/cors"
16
-	"SCRM/controllers/staff"
17 18
 )
18 19
 
19 20
 func init() {
@@ -33,7 +34,7 @@ func init() {
33 34
 	marketing_tool.RegisterRouters()
34 35
 	article.RegisterRouters()
35 36
 	mpwechat.RegisterRouters()
37
+	sms.RegisterRouters()
36 38
 	staff.RegisterRouters()
37 39
 	kefu.RegisterRouters()
38
-
39 40
 }

+ 51 - 0
service/member_service/member_service.go Wyświetl plik

@@ -146,3 +146,54 @@ func FindCustomerInfoByOpenid(orgID int64, openid string) (*models.UserCustomer,
146 146
 	}
147 147
 	return &cusotmer, nil
148 148
 }
149
+
150
+// 获取所有有手机号的客户(仅取部分信息!)
151
+func GetAllCustomersWithMobile(orgUserID int64) ([]*models.UserCustomer, error) {
152
+	var customer []*models.UserCustomer
153
+	err := service.UserReadDB().Table("sgj_user_customer AS uc").
154
+		Where("uc.user_org_id = ? AND uc.status=1 and uc.mobile <>'' ", orgUserID).
155
+		Select("uc.id, uc.name, uc.mobile").
156
+		Scan(&customer).
157
+		Error
158
+	return customer, err
159
+}
160
+
161
+// 获取指定标签下有手机号的客户(仅取部分信息!)
162
+func GetTagCustomersWithMobileByTagIDs(orgID int64, tagIDs []string) ([]*models.UserCustomer, error) {
163
+	var customer []*models.UserCustomer
164
+	err := service.UserReadDB().Table("sgj_user_customer AS uc").
165
+		Joins("JOIN sgj_user_tag_links AS tl on uc.id = tl.user_link_id").
166
+		Where("tl.tag_id IN (?) and tl.status = 1 and uc.user_org_id = ? and uc.status=1 and uc.mobile <>'' ", orgID, tagIDs).
167
+		Group("uc.id").
168
+		Select("uc.id, uc.name, uc.mobile").
169
+		Scan(&customer).
170
+		Error
171
+	return customer, err
172
+}
173
+
174
+// 获取指定客户ID的且有手机号的客户(仅取部分信息!)
175
+func GetSpecificCustomersWithMobile(orgID int64, customerIDs []string) ([]*models.UserCustomer, error) {
176
+	var customer []*models.UserCustomer
177
+	err := service.UserReadDB().Table("sgj_user_customer AS uc").
178
+		Where("uc.user_org_id = ? AND uc.status=1 and uc.mobile <>'' and id in (?)", orgID, customerIDs).
179
+		Select("uc.id, uc.name, uc.mobile").
180
+		Scan(&customer).
181
+		Error
182
+	return customer, err
183
+}
184
+
185
+func GetAllMemberList(orgID int64) (members []*Members, err error) {
186
+	db := service.UserReadDB().Table("sgj_user_customer as c").Where("c.user_org_id=? and c.status=1", orgID)
187
+
188
+	err = db.Order("c.created_time desc").
189
+		Select("c.id, c.user_org_id, c.user_id, c.mobile, c.name, c.gender, c.province_id, c.city_id, c.address, c.birthday, c.treat_type, c.relationship, c.illness_id, c.wechat_openid, c.membership, c.sources, c.status, c.created_time, c.updated_time, c.avatar, c.wechat_unionid, c.remark, c.medical_diagnose, c.yz_uid, c.ill_date, c.district_id").Find(&members).Error
190
+	if err != nil {
191
+		return
192
+	}
193
+	if len(members) > 0 {
194
+		for _, member := range members {
195
+			member.Avatar = strings.Replace(member.Avatar, "http://7xkofe.com1.z0.glb.clouddn.com", "https://images.shengws.com", 1)
196
+		}
197
+	}
198
+	return
199
+}

+ 12 - 0
service/member_service/tags_service.go Wyświetl plik

@@ -170,3 +170,15 @@ func DeleteTags(orgID int64, ids []int64) (err error) {
170 170
 	}
171 171
 	return
172 172
 }
173
+
174
+// 获取 tag ids 下有手机号的客户的数量
175
+func GetTagUsersCountWithMobileByTagIDs(tagIDs []string) (int, error) {
176
+	var count int
177
+	err := service.UserReadDB().Table("sgj_user_customer AS uc").
178
+		Joins("JOIN sgj_user_tag_links AS tl on uc.id = tl.user_link_id").
179
+		Where("tl.tag_id IN (?) and tl.status = 1 and uc.status=1 and uc.mobile <>'' ", tagIDs).
180
+		Group("uc.id").
181
+		Count(&count).
182
+		Error
183
+	return count, err
184
+}

+ 116 - 0
service/sms_service/sms_db_service.go Wyświetl plik

@@ -0,0 +1,116 @@
1
+package sms_service
2
+
3
+import (
4
+	"SCRM/models"
5
+	"SCRM/service"
6
+	"SCRM/utils"
7
+	"time"
8
+
9
+	"github.com/jinzhu/gorm"
10
+)
11
+
12
+func GetBatchSendRecords(orgID int64, page int, count int) ([]*SMSBatchListViewModel, int, error) {
13
+	if count <= 0 {
14
+		return []*SMSBatchListViewModel{}, 0, nil
15
+	}
16
+	var viewModels []*SMSBatchListViewModel = make([]*SMSBatchListViewModel, 0)
17
+	readDB := service.PatientReadDB()
18
+	var total int
19
+	getTotalErr := readDB.Model(&models.SMSBatch{}).Where("org_id = ?", orgID).Count(&total).Error
20
+	if getTotalErr != nil {
21
+		return nil, 0, getTotalErr
22
+	}
23
+
24
+	rows, err := readDB.Raw("SELECT b.full_content, b.status, (SELECT COUNT(id) AS c FROM sgj_patient_sms_send_status WHERE batch_id = b.id) AS total_count, (SELECT COUNT(id) AS c FROM sgj_patient_sms_send_status WHERE batch_id = b.id AND STATUS = '1') AS success_count FROM sgj_patient_sms_batch AS b WHERE b.org_id = ? ORDER BY b.id LIMIT ? OFFSET ?;", orgID, count, (page-1)*count).Rows()
25
+	defer rows.Close()
26
+	if err != nil {
27
+		return nil, 0, err
28
+	}
29
+	for rows.Next() {
30
+		var viewModel SMSBatchListViewModel
31
+		readDB.ScanRows(rows, &viewModel)
32
+		viewModels = append(viewModels, &viewModel)
33
+	}
34
+	return viewModels, total, nil
35
+}
36
+
37
+// 获取指定模板id的模板
38
+func GetSMSTemplateWithTemplateID(templateID int64) (*models.SMSTemplate, error) {
39
+	var template models.SMSTemplate
40
+	err := service.PatientReadDB().Where("template_id = ? AND (status = 1 OR status = 2)", templateID).First(&template).Error
41
+	if err != nil {
42
+		if err == gorm.ErrRecordNotFound {
43
+			return nil, nil
44
+		} else {
45
+			return nil, err
46
+		}
47
+	}
48
+	return &template, nil
49
+}
50
+
51
+// 获取审核中的短信批次
52
+func getInReviewSMSBatchWithTemplateID(templateID int) ([]*models.SMSBatch, error) {
53
+	var batchs []*models.SMSBatch
54
+	err := service.PatientReadDB().Where("sms_template_id = ? AND status = ?", templateID, models.SMSBatchStatusInReview).Find(&batchs).Error
55
+	if err != nil {
56
+		return nil, err
57
+	} else {
58
+		return batchs, nil
59
+	}
60
+}
61
+
62
+// 获取用户的免费短信额度
63
+func GetUserSMSFreeLimit(orgID int64, date time.Time) (*models.UserSMSFreeLimit, error) {
64
+	month := date.Format("200601")
65
+	var freeLimit models.UserSMSFreeLimit
66
+	if readErr := service.PatientReadDB().Where("org_id = ? AND valid_month = ?", orgID, month).First(&freeLimit).Error; readErr == gorm.ErrRecordNotFound {
67
+		// 创建
68
+		now := time.Now().Unix()
69
+		freeLimit = models.UserSMSFreeLimit{
70
+			OrgID:      orgID,
71
+			TotalCount: 200,
72
+			UsedCount:  0,
73
+			ValidMonth: month,
74
+			Status:     1,
75
+			CreateTime: now,
76
+			ModifyTime: now,
77
+		}
78
+
79
+		if createErr := service.PatientWriteDB().Create(&freeLimit).Error; createErr != nil {
80
+			utils.ErrorLog("用户短信免费额度创建失败: %v", createErr)
81
+			return nil, createErr
82
+		} else {
83
+			return &freeLimit, nil
84
+		}
85
+
86
+	} else if readErr != nil {
87
+		utils.ErrorLog("获取用户短信免费额度信息失败: %v", readErr)
88
+		return nil, readErr
89
+
90
+	} else {
91
+		return &freeLimit, nil
92
+	}
93
+}
94
+
95
+// 获取未发送的短信批次
96
+func GetUnsendSMSBatch() ([]*models.SMSBatch, error) {
97
+	var batchs []*models.SMSBatch
98
+	err := service.PatientReadDB().Where("status = ?", models.SMSBatchStatusUnsend).Find(&batchs).Error
99
+	if err != nil {
100
+		if err == gorm.ErrRecordNotFound {
101
+			return nil, nil
102
+		} else {
103
+			return nil, err
104
+		}
105
+	} else {
106
+		return batchs, nil
107
+	}
108
+}
109
+
110
+func DisableTemplate(templateID int64) error {
111
+	err := service.PatientWriteDB().Model(&models.SMSTemplate{}).Where("template_id = ?", templateID).Updates(map[string]interface{}{
112
+		"status": 0,
113
+		"mtime":  time.Now().Unix(),
114
+	}).Error
115
+	return err
116
+}

+ 325 - 0
service/sms_service/sms_service.go Wyświetl plik

@@ -0,0 +1,325 @@
1
+package sms_service
2
+
3
+import (
4
+	"SCRM/models"
5
+	base "SCRM/service"
6
+	"SCRM/utils"
7
+	"errors"
8
+	"fmt"
9
+	"math"
10
+	"strings"
11
+	"time"
12
+
13
+	"github.com/astaxie/beego"
14
+	"github.com/jinzhu/gorm"
15
+)
16
+
17
+// 发送验证码短信
18
+func SMSSendVerificationCode(mobile string) (string, error) {
19
+	if len(mobile) == 0 {
20
+		return "", errors.New("手机号为空")
21
+	}
22
+
23
+	code_str := utils.RandomNumberString(6)
24
+	templateID, _ := beego.AppConfig.Int("sms_verification_code_templateid")
25
+	_, _, _, err := singleSendMessageUseUCPaas(templateID, []string{code_str}, mobile)
26
+	return code_str, err
27
+}
28
+
29
+// 成为机构管理员的邀请短信
30
+func SMSSendInviteMobileToJoinOrgAdmin(name string, mobile string, password string) error {
31
+	if len(mobile) == 0 {
32
+		return errors.New("手机号为空")
33
+	}
34
+	if len(password) == 0 {
35
+		_, _, _, err := singleSendMessageUseUCPaas(332784, []string{name, mobile}, mobile)
36
+		return err
37
+	} else {
38
+		_, _, _, err := singleSendMessageUseUCPaas(332783, []string{name, mobile, password}, mobile)
39
+		return err
40
+	}
41
+}
42
+
43
+var (
44
+	SMSErrorFreeLimitInsufficient = errors.New("短信额度不足")
45
+)
46
+
47
+// 发送自定义内容短信,content 不需要包括签名及“退订请回复TD”字样
48
+/* 流程说明:
49
+检查额度 -> 创建短信平台模板 -> 创建数据库短信模板(待审核状态) -> 创建发送批次(待审核状态) -> 扣除额度 -> finish
50
+*/
51
+func SMSOrgSendWithCustomContent(orgID int64, originContent string, mobiles []string) error {
52
+	if len(mobiles) == 0 {
53
+		return nil
54
+	}
55
+
56
+	autograph := "酷医聚客"
57
+	content := fmt.Sprintf("%v退订请回复TD", originContent)
58
+	fullContent := fmt.Sprintf("【%v】%v", autograph, content)
59
+	if len(fullContent) > 500 {
60
+		return errors.New("短信太长")
61
+	}
62
+
63
+	willConsumeLimit := GetWillConsumeLimit(fullContent, len(mobiles))
64
+	freeLimit, getFreeLimitErr := GetUserSMSFreeLimit(orgID, time.Now())
65
+	if getFreeLimitErr != nil {
66
+		return getFreeLimitErr
67
+	}
68
+	if willConsumeLimit+freeLimit.UsedCount > freeLimit.TotalCount {
69
+		return SMSErrorFreeLimitInsufficient
70
+	}
71
+
72
+	smsTplID, createSMSTplErr := createTemplate2UCPaas(autograph, content, 5)
73
+	if createSMSTplErr != nil {
74
+		return createSMSTplErr
75
+	}
76
+
77
+	template := models.SMSTemplate{
78
+		TemplateType: 2,
79
+		TemplateID:   int64(smsTplID),
80
+		MessageType:  2,
81
+		Content:      content,
82
+		Autograph:    autograph,
83
+		OrgID:        orgID,
84
+		Status:       2,
85
+		CreateTime:   time.Now().Unix(),
86
+		ModifyTime:   time.Now().Unix(),
87
+	}
88
+
89
+	tx := base.PatientWriteDB().Begin()
90
+	if createTplErr := tx.Save(&template).Error; createTplErr != nil {
91
+		tx.Rollback()
92
+		return createTplErr
93
+	}
94
+
95
+	sendTime := time.Now()
96
+	batch := &models.SMSBatch{
97
+		OrgID:       orgID,
98
+		Autograph:   autograph,
99
+		FullContent: fullContent,
100
+		Mobiles:     strings.Join(mobiles, ","),
101
+		TemplateID:  int64(smsTplID),
102
+		SendTime:    sendTime.Unix(),
103
+		Status:      models.SMSBatchStatusInReview,
104
+		CreateTime:  time.Now().Unix(),
105
+		ModifyTime:  time.Now().Unix(),
106
+	}
107
+	if createBatchErr := tx.Save(batch).Error; createBatchErr != nil {
108
+		tx.Rollback()
109
+		return createBatchErr
110
+	}
111
+
112
+	freeLimit.UsedCount += willConsumeLimit
113
+	freeLimit.ModifyTime = time.Now().Unix()
114
+	updateLimitErr := tx.Save(freeLimit).Error
115
+	if updateLimitErr != nil {
116
+		tx.Rollback()
117
+		return updateLimitErr
118
+	}
119
+
120
+	tx.Commit()
121
+	return nil
122
+}
123
+
124
+// 计算即将消耗的额度 (smsFullContent 包含签名在内)
125
+func GetWillConsumeLimit(smsFullContent string, mobileCount int) int {
126
+	limitEachMessage := math.Ceil(float64(len(smsFullContent)) / 67.0)
127
+	return int(limitEachMessage) * mobileCount
128
+}
129
+
130
+// 短信平台模板审核成功
131
+/* 流程说明
132
+更新数据库模板状态(有效) -> 更新对应发送批次状态(待发送) -> finish
133
+*/
134
+func SMSUCPaasTemplateApproved(templateID int64) error {
135
+	tx := base.PatientWriteDB().Begin()
136
+
137
+	updateTplErr := tx.Model(&models.SMSTemplate{}).Where("template_id = ?", templateID).Updates(map[string]interface{}{
138
+		"status": 1,
139
+		"mtime":  time.Now().Unix(),
140
+	}).Error
141
+	if updateTplErr != nil {
142
+		tx.Rollback()
143
+		return updateTplErr
144
+	}
145
+
146
+	updateBatchErr := tx.Model(&models.SMSBatch{}).Where("sms_template_id = ?", templateID).Updates(map[string]interface{}{
147
+		"status": models.SMSBatchStatusUnsend,
148
+		"mtime":  time.Now().Unix(),
149
+	}).Error
150
+	if updateBatchErr != nil {
151
+		tx.Rollback()
152
+		return updateBatchErr
153
+	}
154
+
155
+	tx.Commit()
156
+	return nil
157
+}
158
+
159
+// 短信平台模板审核失败
160
+/* 流程说明
161
+更新数据库模板状态(无效) -> 更新对应发送批次状态(审核未通过) -> 返回发送当月的额度 -> finish
162
+*/
163
+func SMSUCPaasTemplateUnapproved(templateID int64) error {
164
+	var batch models.SMSBatch
165
+	getBatchErr := base.PatientReadDB().Model(&models.SMSBatch{}).Where("sms_template_id = ?", templateID).First(&batch).Error
166
+	if getBatchErr != nil {
167
+		if getBatchErr != gorm.ErrRecordNotFound {
168
+			return getBatchErr
169
+		}
170
+	}
171
+
172
+	tx := base.PatientWriteDB().Begin()
173
+
174
+	updateTplErr := tx.Model(&models.SMSTemplate{}).Where("template_id = ?", templateID).Updates(map[string]interface{}{
175
+		"status": 0,
176
+		"mtime":  time.Now().Unix(),
177
+	}).Error
178
+	if updateTplErr != nil {
179
+		tx.Rollback()
180
+		return updateTplErr
181
+	}
182
+
183
+	if batch.ID > 0 {
184
+		batch.Status = models.SMSBatchStatusUnapproved
185
+		batch.ModifyTime = time.Now().Unix()
186
+		updateBatchErr := tx.Save(&batch).Error
187
+		if updateBatchErr != nil {
188
+			tx.Rollback()
189
+			return updateBatchErr
190
+		}
191
+
192
+		freeLimit, getFreeLimitErr := GetUserSMSFreeLimit(batch.OrgID, time.Unix(batch.SendTime, 0))
193
+		if getFreeLimitErr != nil {
194
+			tx.Rollback()
195
+			return getFreeLimitErr
196
+		}
197
+
198
+		mobiles := strings.Split(batch.Mobiles, ",")
199
+		consumedLimit := GetWillConsumeLimit(batch.FullContent, len(mobiles))
200
+		freeLimit.UsedCount -= consumedLimit
201
+		freeLimit.ModifyTime = time.Now().Unix()
202
+		updateFreeLimitErr := tx.Save(freeLimit).Error
203
+		if updateFreeLimitErr != nil {
204
+			tx.Rollback()
205
+			return updateFreeLimitErr
206
+		}
207
+	}
208
+
209
+	tx.Commit()
210
+	return nil
211
+}
212
+
213
+// 发送“未发送”批次的短信
214
+// 备注:因短信平台每次群发限制 100 个号码,所以同一个批次可能需要分成多次发送
215
+func SMSSendUnsendBatchRealTime() {
216
+	batchs, getBatchErr := GetUnsendSMSBatch()
217
+	if getBatchErr != nil {
218
+		return
219
+	}
220
+
221
+	for _, batch := range batchs {
222
+		tx := base.PatientWriteDB().Begin()
223
+
224
+		isSendSuccess := false
225
+		failMobileCount := 0
226
+		reports := []interface{}{}
227
+
228
+		mobiles := strings.Split(batch.Mobiles, ",")
229
+		subbatchCount := int(math.Ceil(float64(len(mobiles)) / 100.0))
230
+		for index := 0; index < subbatchCount; index++ {
231
+			var subMobiles []string
232
+			if len(mobiles)-index*100 >= 100 {
233
+				subMobiles = mobiles[index*100 : 100]
234
+			} else {
235
+				subMobiles = mobiles[index*100:]
236
+			}
237
+
238
+			sendCount, report, sendErr := batchSendMessageUseUCPaas(int(batch.TemplateID), nil, subMobiles)
239
+			if sendErr != nil {
240
+				utils.ErrorLog("%v", sendErr)
241
+				failMobileCount += len(subMobiles)
242
+
243
+			} else {
244
+				isSendSuccess = true // 只要子批次有一次发送成功,就认为这个批次发送成功
245
+				reports = append(reports, report)
246
+				failMobileCount += len(subMobiles) - sendCount
247
+			}
248
+		}
249
+
250
+		if isSendSuccess {
251
+			batch.Status = models.SMSBatchStatusDidSend
252
+			batch.ModifyTime = time.Now().Unix()
253
+			updateBatchErr := tx.Save(batch).Error
254
+			if updateBatchErr != nil {
255
+				utils.ErrorLog("群发短信成功后 更新批次状态失败:%v", updateBatchErr)
256
+				tx.Rollback()
257
+				continue
258
+			}
259
+
260
+			valueStrs := make([]string, 0, len(reports))
261
+			values := make([]interface{}, 0, len(reports))
262
+			for _, item := range reports {
263
+				json := item.(map[string]interface{})
264
+				valueStrs = append(valueStrs, "(?, ?, ?, ?, ?, ?, ?)")
265
+
266
+				values = append(values, batch.ID)
267
+				values = append(values, json["mobile"])
268
+				if json["code"].(string) == "000000" {
269
+					values = append(values, 1)
270
+				} else {
271
+					values = append(values, 0)
272
+				}
273
+				values = append(values, json["code"])
274
+				values = append(values, json["msg"])
275
+				values = append(values, time.Now().Unix())
276
+				values = append(values, time.Now().Unix())
277
+			}
278
+			sql := fmt.Sprintf("INSERT INTO sgj_patient_sms_send_status (batch_id, mobile, status, code, msg, ctime, mtime) VALUES %v;", strings.Join(valueStrs, ", "))
279
+			if insertReportErr := tx.Exec(sql, values...).Error; insertReportErr != nil {
280
+				utils.ErrorLog("群发短信成功后 插入短信发送状态失败: %v", insertReportErr)
281
+				tx.Rollback()
282
+				continue
283
+			}
284
+
285
+			if failMobileCount > 0 {
286
+				freeLimit, getFreeLimitErr := GetUserSMSFreeLimit(batch.OrgID, time.Unix(batch.SendTime, 0))
287
+				if getFreeLimitErr == nil {
288
+					freeLimit.UsedCount -= GetWillConsumeLimit(batch.FullContent, failMobileCount)
289
+					freeLimit.ModifyTime = time.Now().Unix()
290
+					updateLimitErr := tx.Save(freeLimit).Error
291
+					if updateLimitErr != nil {
292
+						utils.ErrorLog("群发短信成功后 返回额度失败: %v", updateLimitErr)
293
+						tx.Rollback()
294
+						continue
295
+					}
296
+				}
297
+			}
298
+			tx.Commit()
299
+
300
+		} else {
301
+			batch.Status = models.SMSBatchStatusSendFailed
302
+			batch.ModifyTime = time.Now().Unix()
303
+			updateBatchErr := tx.Save(batch).Error
304
+			if updateBatchErr != nil {
305
+				utils.ErrorLog("群发短信成功后 更新批次状态失败:%v", updateBatchErr)
306
+				tx.Rollback()
307
+				continue
308
+			}
309
+
310
+			freeLimit, getFreeLimitErr := GetUserSMSFreeLimit(batch.OrgID, time.Unix(batch.SendTime, 0))
311
+			if getFreeLimitErr == nil {
312
+				freeLimit.UsedCount -= GetWillConsumeLimit(batch.FullContent, len(mobiles))
313
+				freeLimit.ModifyTime = time.Now().Unix()
314
+				updateLimitErr := tx.Save(freeLimit).Error
315
+				if updateLimitErr != nil {
316
+					utils.ErrorLog("群发短信成功后 返回额度失败: %v", updateLimitErr)
317
+					tx.Rollback()
318
+					continue
319
+				}
320
+			}
321
+
322
+			tx.Commit()
323
+		}
324
+	}
325
+}

+ 8 - 0
service/sms_service/sms_vms.go Wyświetl plik

@@ -0,0 +1,8 @@
1
+package sms_service
2
+
3
+type SMSBatchListViewModel struct {
4
+	FullContent  string `json:"full_content"`  // 发送内容
5
+	Status       int8   `json:"status"`        // 批次状态 (值同 sms_models 中的 SMSBatchStatuss)
6
+	TotalCount   int    `json:"total_count"`   // 发送人数
7
+	SuccessCount int    `json:"success_count"` // 到达条数
8
+}

+ 164 - 0
service/sms_service/ucpaas.go Wyświetl plik

@@ -0,0 +1,164 @@
1
+package sms_service
2
+
3
+import (
4
+	"SCRM/utils"
5
+	"bytes"
6
+	"encoding/json"
7
+	"errors"
8
+	"io/ioutil"
9
+	"net/http"
10
+	"strconv"
11
+	"strings"
12
+
13
+	"github.com/astaxie/beego"
14
+)
15
+
16
+// 获取短信平台信息
17
+func getUCPaasConfig() (string, string, string) {
18
+	return beego.AppConfig.String("sms_appId"),
19
+		beego.AppConfig.String("sms_sid"),
20
+		beego.AppConfig.String("sms_token")
21
+}
22
+
23
+// 向云之讯短信平台创建模板,返回新模板的 ID
24
+// autograph: 签名,即为机构简称
25
+// content: 模板内容
26
+// type_: 短信类型:0:通知短信 5:会员服务短信
27
+func createTemplate2UCPaas(autograph string, content string, type_ int) (int, error) {
28
+	sms_api := beego.AppConfig.String("sms_baseUrl") + "addsmstemplate"
29
+	appID, sid, token := getUCPaasConfig()
30
+	params := make(map[string]interface{})
31
+	params["appid"] = appID
32
+	params["sid"] = sid
33
+	params["token"] = token
34
+	params["type"] = type_
35
+	params["autograph"] = autograph
36
+	params["content"] = content
37
+
38
+	paramsBytes, _ := json.Marshal(params)
39
+	resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
40
+
41
+	if requestErr != nil {
42
+		utils.ErrorLog("短信平台增加模板接口调用失败: %v", requestErr)
43
+		return 0, requestErr
44
+	}
45
+	defer resp.Body.Close()
46
+	body, ioErr := ioutil.ReadAll(resp.Body)
47
+	if ioErr != nil {
48
+		utils.ErrorLog("短信平台增加模板接口返回数据读取失败: %v", ioErr)
49
+		return 0, ioErr
50
+	}
51
+	var respJSON map[string]interface{}
52
+	utils.InfoLog(string(body))
53
+	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
54
+		utils.ErrorLog("短信平台增加模板接口返回数据解析JSON失败: %v", err)
55
+		return 0, err
56
+	}
57
+	if respJSON["code"].(string) != "000000" {
58
+		msg := respJSON["msg"].(string)
59
+		utils.ErrorLog("短信平台增加模板接口请求失败: %v", msg)
60
+		return 0, errors.New("短信平台增加模板接口请求失败")
61
+	}
62
+
63
+	templateID, _ := strconv.Atoi(respJSON["templateid"].(string))
64
+	utils.SuccessLog("新短信模板 ID: %v", templateID)
65
+	return templateID, nil
66
+}
67
+
68
+// 用云之讯群发模板短信
69
+// 返回值为发送了 n 条短信
70
+func batchSendMessageUseUCPaas(templateID int, params []string, mobiles []string) (int, []interface{}, error) {
71
+	sms_api := beego.AppConfig.String("sms_baseUrl") + "sendsms_batch"
72
+	mobileStr := strings.Join(mobiles, ",")
73
+	appID, sid, token := getUCPaasConfig()
74
+	requestParams := make(map[string]interface{})
75
+	requestParams["appid"] = appID
76
+	requestParams["sid"] = sid
77
+	requestParams["token"] = token
78
+	requestParams["templateid"] = strconv.Itoa(templateID)
79
+	requestParams["mobile"] = mobileStr
80
+	if params != nil && len(params) != 0 {
81
+		paramStr := strings.Join(params, ",")
82
+		requestParams["param"] = paramStr
83
+	}
84
+
85
+	paramsBytes, _ := json.Marshal(requestParams)
86
+	resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
87
+
88
+	if requestErr != nil {
89
+		utils.ErrorLog("短信平台模板群发接口调用失败: %v", requestErr)
90
+		return 0, nil, requestErr
91
+	}
92
+	defer resp.Body.Close()
93
+	body, ioErr := ioutil.ReadAll(resp.Body)
94
+	if ioErr != nil {
95
+		utils.ErrorLog("短信平台模板群发接口返回数据读取失败: %v", ioErr)
96
+		return 0, nil, ioErr
97
+	}
98
+	var respJSON map[string]interface{}
99
+	utils.InfoLog(string(body))
100
+	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
101
+		utils.ErrorLog("短信平台模板群发接口返回数据解析JSON失败: %v", err)
102
+		return 0, nil, err
103
+	}
104
+	if respJSON["code"].(string) != "000000" {
105
+		msg := respJSON["msg"].(string)
106
+		utils.ErrorLog("短信平台模板群发接口请求失败: %v", msg)
107
+		return 0, nil, errors.New("短信平台模板群发接口请求失败")
108
+
109
+	} else {
110
+		utils.SuccessLog("短信发送成功 report: %v", respJSON["report"])
111
+		if len(mobiles) > 1 {
112
+			count, _ := strconv.Atoi(respJSON["count_sum"].(string))
113
+			return count, respJSON["report"].([]interface{}), nil
114
+		} else {
115
+			return 1, nil, nil
116
+		}
117
+	}
118
+}
119
+
120
+// 用云之讯单发模板短信
121
+// 返回值分别是 是否成功,code,msg,error
122
+func singleSendMessageUseUCPaas(templateID int, params []string, mobile string) (bool, string, string, error) {
123
+	sms_api := beego.AppConfig.String("sms_baseUrl") + "sendsms"
124
+	appID, sid, token := getUCPaasConfig()
125
+	requestParams := make(map[string]interface{})
126
+	requestParams["appid"] = appID
127
+	requestParams["sid"] = sid
128
+	requestParams["token"] = token
129
+	requestParams["templateid"] = strconv.Itoa(templateID)
130
+	requestParams["mobile"] = mobile
131
+	if params != nil && len(params) != 0 {
132
+		paramStr := strings.Join(params, ",")
133
+		requestParams["param"] = paramStr
134
+	}
135
+
136
+	paramsBytes, _ := json.Marshal(requestParams)
137
+	resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
138
+
139
+	if requestErr != nil {
140
+		utils.ErrorLog("短信平台模板群发接口调用失败: %v", requestErr)
141
+		return false, "", "", requestErr
142
+	}
143
+	defer resp.Body.Close()
144
+	body, ioErr := ioutil.ReadAll(resp.Body)
145
+	if ioErr != nil {
146
+		utils.ErrorLog("短信平台模板群发接口返回数据读取失败: %v", ioErr)
147
+		return false, "", "", ioErr
148
+	}
149
+	var respJSON map[string]interface{}
150
+	utils.InfoLog(string(body))
151
+	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
152
+		utils.ErrorLog("短信平台模板群发接口返回数据解析JSON失败: %v", err)
153
+		return false, "", "", err
154
+	}
155
+	if respJSON["code"].(string) != "000000" {
156
+		msg := respJSON["msg"].(string)
157
+		utils.ErrorLog("短信平台模板群发接口请求失败: %v", msg)
158
+		return false, "", "", errors.New("短信平台模板群发接口请求失败")
159
+
160
+	} else {
161
+		utils.SuccessLog("短信发送成功")
162
+		return true, respJSON["code"].(string), respJSON["msg"].(string), nil
163
+	}
164
+}

+ 25 - 0
service/tencentim_service/user_service.go Wyświetl plik

@@ -0,0 +1,25 @@
1
+package tencentim_service
2
+
3
+import (
4
+	"SCRM/service"
5
+)
6
+
7
+type InfoMap struct {
8
+	Cname      string
9
+	Name       string
10
+	Image      string
11
+	Uid        int64
12
+	Identifier string
13
+}
14
+
15
+func GetImUserMap(accountKey []string, userOrgID int64) (infoMap []InfoMap) {
16
+	service.UserReadDB().Table("sgj_user_user_im AS ui").
17
+		Joins("JOIN sgj_user_user AS u ON u.id = ui.user_id").
18
+		Joins("LEFT JOIN sgj_user_customer AS c ON c.user_id = ui.user_id AND c.user_org_id=? and c.status=1 and c.user_id>0 ", userOrgID).
19
+		Where("ui.identifier IN (?)", accountKey).
20
+		Select("ui.identifier, u.username as name, u.avatar as image, c.name as cname, u.id as uid").
21
+		Scan(&infoMap)
22
+
23
+	return
24
+
25
+}