zjxpcyc 6 years ago
parent
commit
d719cbd4d8

+ 131
- 105
controllers/auth.go View File

@@ -3,7 +3,7 @@ package controllers
3 3
 import (
4 4
 	"errors"
5 5
 	"net/http"
6
-	"spaceofcheng/services/models/model"
6
+	"spaceofcheng/services/models"
7 7
 	"spaceofcheng/services/service"
8 8
 	"spaceofcheng/services/utils"
9 9
 	"strings"
@@ -19,14 +19,70 @@ func (c *BaseController) authenticate() {
19 19
 	// 客户端类型
20 20
 	clientType := utils.GetClientType(c.Ctx.Request)
21 21
 
22
+	switch clientType {
23
+	case utils.ClientAdmin:
24
+		c.authPCAdmin(serv)
25
+	case utils.ClientWechat:
26
+		c.authWechat(serv)
27
+	default:
28
+		c.ResponseError(
29
+			errors.New("暂不支持的 API 场景"),
30
+			http.StatusBadRequest,
31
+		)
32
+	}
33
+}
34
+
35
+// authPCAdmin PC管理端
36
+func (c *BaseController) authPCAdmin(serv *service.SysServ) {
37
+	if !c.needAuth() {
38
+		return
39
+	}
40
+
41
+	// 用户ID
42
+	userID := ""
43
+	userIDRaw := c.GetSession(SNUserID)
44
+	if userIDRaw != nil {
45
+		userID = userIDRaw.(string)
46
+	}
47
+
48
+	if userID == "" {
49
+		c.ResponseError(
50
+			errors.New("用户未登录"),
51
+			http.StatusUnauthorized,
52
+		)
53
+	}
54
+
55
+	if err := serv.SetUserProfile(userID); err != nil {
56
+		utils.LogError(err.Error())
57
+
58
+		c.ResponseError(
59
+			errors.New("内部错误, 请重试"),
60
+			http.StatusInternalServerError,
61
+		)
62
+	}
63
+
64
+	// 设置 Session
65
+	c.SetSession(SNUserID, userID)
66
+	// c.SetSession(SNUserType, userType)
67
+}
68
+
69
+func (c *BaseController) authWechat(serv *service.SysServ) {
22 70
 	// 用户类型, 此处与数据库定义的不是一个概念
23 71
 	// 此处主要用来区分是 系统用户, 还是 系统客户
24
-	userType := UserGuest
72
+	userType := ""
25 73
 	userTypeRaw := c.GetSession(SNUserType)
26 74
 	if userTypeRaw != nil {
27 75
 		userType = userTypeRaw.(string)
28 76
 	}
29 77
 
78
+	if userType == "" {
79
+		userType = models.USERMAP_CUSTOMER
80
+		identify := c.GetString("identify_type")
81
+		if identify == "admin" {
82
+			userType = models.USERMAP_USER
83
+		}
84
+	}
85
+
30 86
 	// 用户类型不同, 此处ID代表的内容也是不一样的
31 87
 	userID := ""
32 88
 	userIDRaw := c.GetSession(SNUserID)
@@ -34,52 +90,45 @@ func (c *BaseController) authenticate() {
34 90
 		userID = userIDRaw.(string)
35 91
 	}
36 92
 
37
-	// 用户映射表
38
-	var userMap *model.TaUserMapping
39
-
40
-	if clientType == utils.ClientWechat && userID == "" {
41
-		var err error
42
-		userMap, err = c.checkWechat(serv)
43
-		if err != nil {
44
-			utils.LogError("获取并校验微信信息失败: " + err.Error())
45
-		}
46
-
47
-		c.SetSession("openid", userMap.Openid)
93
+	// 用户微信信息
94
+	var wxDetail map[string]interface{}
95
+	detailRaw := c.GetSession("wechat_user")
96
+	if detailRaw != nil {
97
+		wxDetail = detailRaw.(map[string]interface{})
48 98
 	}
49 99
 
50
-	if !c.needAuth() {
51
-		return
100
+	if userID == "" && wxDetail == nil {
101
+		wxDetail = c.getWechatDetail()
102
+		c.SetSession("wechat_user", wxDetail)
52 103
 	}
53 104
 
54
-	// 如果是普通游客, 暂时没有任何设置
55
-	if userType == UserGuest {
56
-		return
105
+	// 用户映射
106
+	// 如果映射不存在, 则新建映射
107
+	userMap, err := serv.CheckWechatUserMapping(wxDetail, userType)
108
+	if err != nil {
109
+		c.ResponseError(
110
+			utils.LogError("获取人员映射账户失败: "+err.Error()),
111
+			http.StatusInternalServerError,
112
+		)
113
+
114
+		c.Context.Set("userMap", *userMap)
57 115
 	}
58 116
 
59
-	// 如果存在用户映射
60
-	mapUserID := c.getUserIDByUserMap(userMap, userID, userType)
61
-	if mapUserID != "" {
62
-		userID = mapUserID
117
+	if !c.needAuth() {
118
+		return
63 119
 	}
64 120
 
65
-	// 未登录状态
66
-	if userID == "" {
67
-		if clientType == utils.ClientWechat {
68
-			wxConf, ok := c.Configer[WeChatConf]
69
-			if ok {
70
-				c.ResponseData(
71
-					map[string]interface{}{
72
-						"appid": wxConf.String("appid"),
73
-					},
74
-					errors.New("用户未登录"),
75
-					http.StatusUnauthorized,
76
-				)
77
-			} else {
78
-				c.ResponseError(
79
-					errors.New("用户未登录"),
80
-					http.StatusUnauthorized,
81
-				)
82
-			}
121
+	// 未登录或者绑定, 返回 401
122
+	if userID == "" && userMap.UserId == "" {
123
+		wxConf, ok := c.Configer[WeChatConf]
124
+		if ok {
125
+			c.ResponseData(
126
+				map[string]interface{}{
127
+					"appid": wxConf.String("appid"),
128
+				},
129
+				errors.New("用户未登录"),
130
+				http.StatusUnauthorized,
131
+			)
83 132
 		} else {
84 133
 			c.ResponseError(
85 134
 				errors.New("用户未登录"),
@@ -88,26 +137,42 @@ func (c *BaseController) authenticate() {
88 137
 		}
89 138
 	}
90 139
 
91
-	// 设置 Session
92
-	c.SetSession(SNUserID, userID)
93
-	c.SetSession(SNUserType, userType)
140
+	// 人员微信账号绑定错误
141
+	if userID != "" && userMap.UserId != "" &&
142
+		(userID != userMap.UserId || userType != userMap.UserType) {
143
+		c.ResponseError(
144
+			errors.New("系统内部数据错误, 人员绑定不正确"),
145
+			http.StatusInternalServerError,
146
+		)
147
+	}
94 148
 
95
-	var err error
149
+	if userID == "" {
150
+		userID = userMap.UserId
151
+	}
96 152
 
97
-	// 设置 Context
98
-	// 放到 serv 的方法里面了
99
-	if userType == UserAdmin {
100
-		// 普通管理人员
101
-		err = serv.SetUserProfile(userID)
153
+	if userType == models.USERMAP_USER {
154
+		if err := serv.SetUserProfile(userID); err != nil {
155
+			utils.LogError(err.Error())
102 156
 
157
+			c.ResponseError(
158
+				errors.New("内部错误, 请重试"),
159
+				http.StatusInternalServerError,
160
+			)
161
+		}
103 162
 	} else {
104
-		// 系统客户
105
-		err = serv.SetCustomer(userID)
106
-	}
163
+		if err := serv.SetCustomer(userID); err != nil {
164
+			utils.LogError(err.Error())
107 165
 
108
-	if err != nil {
109
-		// 暂不处理
166
+			c.ResponseError(
167
+				errors.New("内部错误, 请重试"),
168
+				http.StatusInternalServerError,
169
+			)
170
+		}
110 171
 	}
172
+
173
+	// 设置 Session
174
+	c.SetSession(SNUserID, userID)
175
+	c.SetSession(SNUserType, userType)
111 176
 }
112 177
 
113 178
 func (c *BaseController) needAuth() bool {
@@ -122,63 +187,24 @@ func (c *BaseController) needAuth() bool {
122 187
 	return true
123 188
 }
124 189
 
125
-func (c *BaseController) checkWechat(serv *service.SysServ) (*model.TaUserMapping, error) {
126
-	// 确定客户端类型
127
-	adminClient := c.GetString("admin")
128
-	userType := service.USERTYPE_CUSTOMER
129
-	if adminClient != "" {
130
-		userType = service.USERTYPE_USER
131
-	}
132
-
133
-	openIDRaw := c.GetSession("openid")
134
-	if openIDRaw != nil {
135
-		openID := openIDRaw.(string)
136
-		wxUser := map[string]interface{}{
137
-			"openid": openID,
138
-		}
139
-		return serv.CheckWechatUserMapping(wxUser, userType)
140
-	}
141
-
190
+// getWechatDetail 获取微信个人信息详情
191
+func (c *BaseController) getWechatDetail() map[string]interface{} {
142 192
 	// 微信 code
143 193
 	code := c.GetString("code")
144 194
 	if code == "" {
145
-		return nil, errors.New("没有微信 code")
195
+		c.ResponseError(
196
+			errors.New("没有接收到微信 code"),
197
+			http.StatusBadRequest,
198
+		)
146 199
 	}
147 200
 
148
-	// 获取微信信息
149
-	wxUser, err := utils.GetWxUserInfo(code)
201
+	usr, err := utils.GetWxUserInfo(code)
150 202
 	if err != nil {
151
-		return nil, err
152
-	}
153
-
154
-	return serv.CheckWechatUserMapping(wxUser, userType)
155
-}
156
-
157
-// getUserIDByUserMap 校验用户映射是否与 Session 一致
158
-func (c *BaseController) getUserIDByUserMap(user *model.TaUserMapping, userID, userType string) string {
159
-	// 如果映射信息不存在
160
-	if user == nil {
161
-		return ""
162
-	}
163
-
164
-	// 如果 session 数据存在, 以 session 为准
165
-	if userID != "" {
166
-		return ""
167
-	}
168
-
169
-	// 如果是系统内置用户
170
-	if user.UserType == service.USERTYPE_USER {
171
-		if userType != UserAdmin {
172
-			return ""
173
-		}
174
-	}
175
-
176
-	// 如果是普通客户
177
-	if user.UserType == service.USERTYPE_CUSTOMER {
178
-		if userType != UserCustomer {
179
-			return ""
180
-		}
203
+		c.ResponseError(
204
+			utils.LogError("获取微信个人信息失败: "+err.Error()),
205
+			http.StatusInternalServerError,
206
+		)
181 207
 	}
182 208
 
183
-	return user.UserId
209
+	return usr
184 210
 }

+ 1
- 0
controllers/context.go View File

@@ -11,6 +11,7 @@ import (
11 11
 * - 包含内容如下
12 12
 * user					用户基本信息
13 13
 * customer			会员基本信息
14
+* userMap				用户/会员 映射第三方账户
14 15
 * cases					用户所有案场信息
15 16
 * currentCase 	当前案场
16 17
 * org						用户当前组织

+ 82
- 0
controllers/customer/customer.go View File

@@ -0,0 +1,82 @@
1
+package customer
2
+
3
+import (
4
+	"errors"
5
+	"net/http"
6
+	"spaceofcheng/services/controllers"
7
+	"spaceofcheng/services/models"
8
+	"spaceofcheng/services/models/model"
9
+	"spaceofcheng/services/service/customer"
10
+	"spaceofcheng/services/utils"
11
+)
12
+
13
+// CustomerController 用户
14
+type CustomerController struct {
15
+	serv *customer.CustomerServ
16
+	controllers.BaseController
17
+}
18
+
19
+// Constructor 初始化 Controller
20
+// @Title Constructor
21
+// @Description 初始化 Controller, 系统自动调用
22
+func (c *CustomerController) Constructor() {
23
+	c.serv = customer.NewCustomerServ(c.Context)
24
+}
25
+
26
+// CustWXList 获取客户列表
27
+func (c *CustomerController) CustWXList() {
28
+	phone := c.GetString("phone")
29
+	page, _ := c.GetInt("page")
30
+
31
+	custList, total, err := c.serv.GetCustWithWXList(phone, page)
32
+	if err != nil {
33
+		utils.LogError("获取客户列表失败: " + err.Error())
34
+		c.ResponseError(
35
+			errors.New("获取客户列表失败"),
36
+			http.StatusInternalServerError,
37
+		)
38
+	}
39
+
40
+	c.ResponseJSON(map[string]interface{}{
41
+		"list":  custList,
42
+		"total": total,
43
+	})
44
+}
45
+
46
+// GetCustOrUserByID 获取用户信息
47
+func (c *CustomerController) GetCustOrUserByID() {
48
+	userMapRaw := c.Context.Get("userMap")
49
+	if userMapRaw == nil {
50
+		c.ResponseError(errors.New("获取用户信息失败, 请确认使用微信浏览"))
51
+	}
52
+
53
+	userMap := userMapRaw.(model.TaUserMapping)
54
+
55
+	if userMap.UserType == models.USERMAP_USER {
56
+		// 如果是用户
57
+		userID := userMap.UserId
58
+		user, err := c.serv.GetCustUserByID(userID)
59
+		if err != nil {
60
+			utils.LogError(err)
61
+			c.ResponseError(errors.New("获取用户信息失败, 请重试"))
62
+		}
63
+
64
+		c.ResponseJSON(map[string]interface{}{
65
+			"user":     user,
66
+			"userType": userMap.UserType,
67
+		})
68
+	} else {
69
+		// 如果是客户
70
+		custID := userMap.UserId
71
+		cust, err := c.serv.GetCustWithWXByID(custID)
72
+		if err != nil {
73
+			utils.LogError(err)
74
+			c.ResponseError(errors.New("获取用户信息失败, 请重试"))
75
+		}
76
+
77
+		c.ResponseJSON(map[string]interface{}{
78
+			"customer": cust,
79
+			"userType": userMap.UserType,
80
+		})
81
+	}
82
+}

+ 42
- 0
controllers/message.go View File

@@ -0,0 +1,42 @@
1
+package controllers
2
+
3
+import (
4
+	"errors"
5
+	"spaceofcheng/services/utils"
6
+)
7
+
8
+// SendCaptcha 发送验证码
9
+func (c *BaseController) SendCaptcha() {
10
+	phone := c.GetString("phone")
11
+
12
+	if phone == "" {
13
+		c.ResponseError(errors.New("请确定手机号码"))
14
+	}
15
+
16
+	if err := utils.SendCaptcha(phone); err != nil {
17
+		utils.LogError(err)
18
+		c.ResponseError(errors.New("验证码发送失败, 请重试"))
19
+	}
20
+
21
+	c.ResponseJSON("ok")
22
+}
23
+
24
+// ValidCaptcha 发送验证码
25
+func (c *BaseController) ValidCaptcha() {
26
+	phone := c.GetString("phone")
27
+
28
+	if phone == "" {
29
+		c.ResponseError(errors.New("请确定手机号码"))
30
+	}
31
+
32
+	captcha := c.GetString("captcha")
33
+	if captcha == "" {
34
+		c.ResponseError(errors.New("验证码不能为空"))
35
+	}
36
+
37
+	if err := utils.ValidCaptcha(phone, captcha); err != nil {
38
+		c.ResponseError(err)
39
+	}
40
+
41
+	c.ResponseJSON("ok")
42
+}

+ 2
- 2
controllers/session.go View File

@@ -7,6 +7,6 @@ const (
7 7
 	// 用户类型
8 8
 	SNUserType = "userType"
9 9
 
10
-	// 内置 openid
11
-	// 请勿使用 openid 作为 session 的key
10
+	// 内置 wechat_user
11
+	// 请勿使用 wechat_user 作为 session 的key
12 12
 )

+ 0
- 7
controllers/types.go View File

@@ -1,12 +1,5 @@
1 1
 package controllers
2 2
 
3
-// 用户类型列表
4
-const (
5
-	UserGuest    = "guest"
6
-	UserAdmin    = "admin"
7
-	UserCustomer = "customer"
8
-)
9
-
10 3
 // JSONMessage 主要用于 Response 返回
11 4
 type JSONMessage struct {
12 5
 	Code    int         `json:"code"`

+ 86
- 0
controllers/user/user.go View File

@@ -1,10 +1,15 @@
1 1
 package user
2 2
 
3 3
 import (
4
+	"errors"
5
+	"net/http"
4 6
 	"spaceofcheng/services/controllers"
7
+	"spaceofcheng/services/models"
5 8
 	"spaceofcheng/services/models/model"
6 9
 	"spaceofcheng/services/models/system"
7 10
 	"spaceofcheng/services/service"
11
+	"spaceofcheng/services/utils"
12
+	"time"
8 13
 )
9 14
 
10 15
 // UserController 用户
@@ -127,3 +132,84 @@ func (c *UserController) GetUserCustomer() {
127 132
 	}
128 133
 	c.ResponseJSON(customers)
129 134
 }
135
+
136
+// SignIn 用户登录
137
+func (c *UserController) SignIn() {
138
+	userName := c.GetString("username")
139
+	userPassword := c.GetString("userpassword")
140
+	token := c.GetString("token")
141
+	doRemember, _ := c.GetInt("doremember")
142
+
143
+	if token != "" {
144
+		tokenMap, err := utils.PareseToken(token)
145
+		if err != nil {
146
+			utils.LogError("解析用户 token 失败: " + err.Error())
147
+
148
+			c.ResponseError(
149
+				errors.New("登录失败, 用户名或者密码不正确"),
150
+				http.StatusBadRequest,
151
+			)
152
+		}
153
+
154
+		userName = tokenMap["user"].(string)
155
+		userPassword = tokenMap["password"].(string)
156
+	}
157
+
158
+	user, err := c.dao.CheckUserSignIn(userName, userPassword)
159
+	if err != nil {
160
+		utils.LogError("用户登录失败: " + err.Error())
161
+		c.ResponseError(
162
+			errors.New("登录失败, 用户名或者密码不正确"),
163
+			http.StatusBadRequest,
164
+		)
165
+	}
166
+
167
+	// 成功之后, 设置 session
168
+	c.SetSession(controllers.SNUserID, user.UserId)
169
+	c.SetSession(controllers.SNUserType, models.USERMAP_USER)
170
+
171
+	if token == "" && doRemember != 0 {
172
+		var err error
173
+		token, err = utils.CreateToken(map[string]interface{}{
174
+			"user":     userName,
175
+			"password": userPassword,
176
+			"exp":      time.Now().Local().Add(7 * 24 * time.Hour), // 有效期是 7 天
177
+		})
178
+
179
+		if err != nil {
180
+			utils.LogError("生成 用户登录 token 失败: " + err.Error())
181
+		}
182
+	}
183
+
184
+	c.ResponseJSON(map[string]interface{}{
185
+		token: token,
186
+	})
187
+}
188
+
189
+// SignOut 用户登出
190
+func (c *UserController) SignOut() {
191
+	c.DestroySession()
192
+	c.ResponseJSON("ok")
193
+}
194
+
195
+// WechatSignIn 微信登录
196
+func (c *UserController) WechatSignIn() {
197
+	phone := c.GetString("phone")
198
+	captcha := c.GetString("captcha")
199
+
200
+	if err := utils.ValidCaptcha(phone, captcha); err != nil {
201
+		c.ResponseError(err)
202
+	}
203
+
204
+	userMapping := c.Context.Get("userMap").(model.TaUserMapping)
205
+
206
+	user, err := c.dao.BindWechatUser(&userMapping, phone)
207
+	if err != nil {
208
+		c.ResponseError(err)
209
+	}
210
+
211
+	c.SetSession(controllers.SNUserID, user.UserId)
212
+	c.SetSession(controllers.SNUserType, models.USERMAP_USER)
213
+
214
+	c.ResponseJSON("ok")
215
+}

+ 6
- 0
models/constant.go View File

@@ -22,3 +22,9 @@ const (
22 22
 	IMAGETYPE_COVER  = "cover"
23 23
 	IMAGETYPE_DETAIL = "detail"
24 24
 )
25
+
26
+// 映射表用户类型
27
+const (
28
+	USERMAP_USER     = "user"
29
+	USERMAP_CUSTOMER = "customer"
30
+)

+ 177
- 0
models/customer/customer.go View File

@@ -0,0 +1,177 @@
1
+package customer
2
+
3
+import (
4
+	"errors"
5
+	"spaceofcheng/services/models"
6
+	"spaceofcheng/services/models/model"
7
+	"spaceofcheng/services/utils"
8
+	"time"
9
+
10
+	"github.com/yl10/kit/guid"
11
+
12
+	"github.com/go-xorm/xorm"
13
+)
14
+
15
+// CustomerDAO 当前数据库操作对象
16
+type CustomerDAO struct {
17
+	ctx *utils.Context
18
+	db  *xorm.Session
19
+}
20
+
21
+// NewCustomerDAO New Inst
22
+func NewCustomerDAO(ctx *utils.Context) *CustomerDAO {
23
+	return &CustomerDAO{
24
+		ctx: ctx,
25
+		db:  ctx.DB,
26
+	}
27
+}
28
+
29
+// CustWithWXList 客户列表
30
+func (m *CustomerDAO) CustWithWXList(phone string, page ...int) ([]CustWithWX, int64, error) {
31
+	if phone == "" {
32
+		phone = "%"
33
+	} else {
34
+		phone = "%" + phone + "%"
35
+	}
36
+
37
+	pageNum := 10
38
+	if m.ctx.Get("pageNum") != nil {
39
+		pageNum = m.ctx.Get("pageNum").(int)
40
+	}
41
+
42
+	pg := 1
43
+	if len(page) > 0 {
44
+		pg = page[0]
45
+	}
46
+	offset := (pg - 1) * pageNum
47
+	if offset < 0 {
48
+		offset = 0
49
+	}
50
+
51
+	query := `
52
+		SELECT *
53
+		FROM
54
+			ta_customer t
55
+		LEFT JOIN ta_user_mapping s ON t.customer_id = s.user_id
56
+			AND s.user_type = '` + models.USERMAP_CUSTOMER + `'
57
+			AND s.account_type = '` + models.ACCMAP_WECHAT + `'
58
+		WHERE
59
+			t.phone like ?
60
+		AND t.status > ?
61
+		ORDER BY
62
+			t.create_date DESC
63
+	`
64
+
65
+	var cust []CustWithWX
66
+
67
+	cnt, err := utils.GetPageList(m.db, &cust, []int{pageNum, offset}, query, phone, models.STATUS_DEL)
68
+	if err != nil {
69
+		return nil, 0, err
70
+	}
71
+
72
+	return cust, cnt, nil
73
+}
74
+
75
+// GetCustWithWXByID 获取客户(微信)信息
76
+func (m *CustomerDAO) GetCustWithWXByID(custID string) (*CustWithWX, error) {
77
+	if custID == "" {
78
+		return nil, errors.New("获取客户信息失败")
79
+	}
80
+
81
+	cust := CustWithWX{}
82
+	query := `
83
+		SELECT *
84
+		FROM
85
+			ta_customer t
86
+		LEFT JOIN ta_user_mapping s ON t.customer_id = s.user_id
87
+			AND s.user_type = '` + models.USERMAP_CUSTOMER + `'
88
+			AND s.account_type = '` + models.ACCMAP_WECHAT + `'
89
+		WHERE
90
+			t.customer_id = ?
91
+	`
92
+
93
+	if _, err := m.db.SQL(query, custID).Get(&cust); err != nil {
94
+		return nil, err
95
+	}
96
+
97
+	return &cust, nil
98
+}
99
+
100
+// GetCustomerByPhone 依据手机号获取客户信息
101
+func (m *CustomerDAO) GetCustomerByPhone(phone string) (*model.TaCustomer, error) {
102
+	custs := []model.TaCustomer{}
103
+
104
+	if err := m.db.Where("phone=?", phone).Find(&custs); err != nil {
105
+		return nil, err
106
+	}
107
+
108
+	if len(custs) == 0 {
109
+		return nil, nil
110
+	}
111
+
112
+	if len(custs) > 0 {
113
+		return nil, errors.New("数据异常, 一个手机绑定了多个用户")
114
+	}
115
+
116
+	cust := custs[0]
117
+
118
+	return &cust, nil
119
+}
120
+
121
+// UpdateUserMapping 更新用户映射
122
+func (m *CustomerDAO) UpdateUserMapping(userMap *model.TaUserMapping, cols []string) error {
123
+	if userMap.MappingId == "" {
124
+		return errors.New("用户映射信息不存在")
125
+	}
126
+
127
+	_, err := m.db.Where("mapping_id=?", userMap.MappingId).Cols(cols...).Update(&userMap)
128
+	return err
129
+}
130
+
131
+// GetCaseByID 获取案场
132
+func (m *CustomerDAO) GetCaseByID(caseID string) (*model.SysCase, error) {
133
+	if caseID == "" {
134
+		return nil, errors.New("获取案场信息失败")
135
+	}
136
+
137
+	cs := model.SysCase{}
138
+	has, err := m.db.Where("case_id=?", caseID).Get(&cs)
139
+	if err != nil {
140
+		return nil, err
141
+	}
142
+
143
+	if !has {
144
+		return nil, nil
145
+	}
146
+
147
+	return &cs, nil
148
+}
149
+
150
+// GetUserByID 获取系统用户
151
+func (m *CustomerDAO) GetUserByID(userID string) (*model.SysUser, error) {
152
+	if userID == "" {
153
+		return nil, errors.New("获取相关人员信息失败")
154
+	}
155
+
156
+	user := model.SysUser{}
157
+	has, err := m.db.Where("user_id=?", userID).Get(&user)
158
+	if err != nil {
159
+		return nil, err
160
+	}
161
+
162
+	if !has {
163
+		return nil, nil
164
+	}
165
+
166
+	return &user, nil
167
+}
168
+
169
+// SaveCustomer 新增客户
170
+func (m *CustomerDAO) SaveCustomer(cust *model.TaCustomer) error {
171
+	cust.CustomerId = guid.NewGUIDString()
172
+	cust.CreateDate = time.Now().Local()
173
+	cust.Status = models.STATUS_NORMAL
174
+
175
+	_, err := m.db.Insert(&cust)
176
+	return err
177
+}

+ 12
- 0
models/customer/types.go View File

@@ -0,0 +1,12 @@
1
+package customer
2
+
3
+import (
4
+	"spaceofcheng/services/models/model"
5
+)
6
+
7
+// CustWithWX 客户及微信相关信息
8
+type CustWithWX struct {
9
+	model.TaCustomer    `xorm:"extends"`
10
+	model.TaUserMapping `xorm:"extends"`
11
+	Points              int
12
+}

+ 43
- 0
models/system/user.go View File

@@ -1,6 +1,7 @@
1 1
 package system
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"spaceofcheng/services/models"
5 6
 	"spaceofcheng/services/models/model"
6 7
 	"spaceofcheng/services/utils"
@@ -328,3 +329,45 @@ func (m *UserDAO) GetUserCustomer(userid string, page int, pageSize int) ([]mode
328 329
 	err := m.db.Where("status>"+strconv.Itoa(models.STATUS_DEL)).And("recommend_id=?", userid).Limit(pageSize, page*pageSize).Desc("create_date").Find(&customers)
329 330
 	return customers, err
330 331
 }
332
+
333
+// GetUserByName 依据账户获取人员信息
334
+func (m *UserDAO) GetUserByName(userName string) (*model.SysUser, error) {
335
+	user := model.SysUser{}
336
+
337
+	has, err := m.db.Where("user_name=?", userName).Get(&user)
338
+	if err != nil {
339
+		return nil, err
340
+	}
341
+
342
+	if !has {
343
+		return nil, nil
344
+	}
345
+
346
+	return &user, nil
347
+}
348
+
349
+// GetUserByPhone 依据手机号获取人员信息
350
+func (m *UserDAO) GetUserByPhone(phone string) (*model.SysUser, error) {
351
+	user := model.SysUser{}
352
+
353
+	has, err := m.db.Where("phone=?", phone).Get(&user)
354
+	if err != nil {
355
+		return nil, err
356
+	}
357
+
358
+	if !has {
359
+		return nil, nil
360
+	}
361
+
362
+	return &user, nil
363
+}
364
+
365
+// UpdateUserMapping 更新用户映射
366
+func (m *UserDAO) UpdateUserMapping(userMap *model.TaUserMapping, cols []string) error {
367
+	if userMap.MappingId == "" {
368
+		return errors.New("用户映射信息不存在")
369
+	}
370
+
371
+	_, err := m.db.Where("mapping_id=?", userMap.MappingId).Cols(cols...).Update(&userMap)
372
+	return err
373
+}

+ 169
- 0
service/customer/customer.go View File

@@ -0,0 +1,169 @@
1
+package customer
2
+
3
+import (
4
+	"encoding/json"
5
+	"errors"
6
+	"spaceofcheng/services/models"
7
+	"spaceofcheng/services/models/customer"
8
+	"spaceofcheng/services/models/model"
9
+	"spaceofcheng/services/service/events"
10
+	"spaceofcheng/services/utils"
11
+	"strconv"
12
+)
13
+
14
+// CustomerServ 系统处理
15
+type CustomerServ struct {
16
+	ctx *utils.Context
17
+	dao *customer.CustomerDAO
18
+}
19
+
20
+// NewCustomerServ 初始化
21
+func NewCustomerServ(ctx *utils.Context) *CustomerServ {
22
+	return &CustomerServ{
23
+		ctx: ctx,
24
+		dao: customer.NewCustomerDAO(ctx),
25
+	}
26
+}
27
+
28
+// GetCustWithWXList 获取客户列表
29
+func (s *CustomerServ) GetCustWithWXList(phone string, page ...int) ([]customer.CustWithWX, int64, error) {
30
+	return s.dao.CustWithWXList(phone, page...)
31
+}
32
+
33
+// GetCustWithWXByID 获取客户列表
34
+func (s *CustomerServ) GetCustWithWXByID(custID string) (*customer.CustWithWX, error) {
35
+	cust, err := s.dao.GetCustWithWXByID(custID)
36
+	if err != nil {
37
+		return nil, err
38
+	}
39
+
40
+	if cust.Status != models.STATUS_NORMAL {
41
+		return nil, errors.New("用户状态异常")
42
+	}
43
+
44
+	return cust, nil
45
+}
46
+
47
+// GetCustUserByID 获取用户信息(自定义内容)
48
+func (s *CustomerServ) GetCustUserByID(userID string) (*UserProfile, error) {
49
+	user, err := s.dao.GetUserByID(userID)
50
+	if err != nil {
51
+		return nil, err
52
+	}
53
+
54
+	if user.Status != models.STATUS_NORMAL {
55
+		return nil, errors.New("用户状态异常")
56
+	}
57
+
58
+	userProfile := UserProfile{
59
+		UserId:     user.UserId,
60
+		UserName:   user.UserName,
61
+		RealName:   user.RealName,
62
+		Sex:        user.Sex,
63
+		WorkNo:     user.WorkNo,
64
+		Email:      user.Email,
65
+		Phone:      user.Phone,
66
+		Headimgurl: user.Headimgurl,
67
+	}
68
+
69
+	return &userProfile, nil
70
+}
71
+
72
+// BindWechat 绑定微信用户
73
+func (s *CustomerServ) BindWechat(userMap *model.TaUserMapping, phone, caseID, userID string) (*model.TaCustomer, error) {
74
+	cust, err := s.dao.GetCustomerByPhone(phone)
75
+	if err != nil {
76
+		return nil, err
77
+	}
78
+
79
+	// 用户不存在, 则新增
80
+	if cust == nil {
81
+		newCust, err := s.SaveNewCustomer(phone, caseID, userID)
82
+		if err != nil {
83
+			return nil, err
84
+		}
85
+
86
+		cust = newCust
87
+	}
88
+
89
+	if cust.Status != models.STATUS_NORMAL {
90
+		return nil, errors.New("用户状态不正确")
91
+	}
92
+
93
+	if cust.CustomerId == userMap.UserId {
94
+		return cust, nil
95
+	}
96
+
97
+	if userMap.UserId != "" {
98
+		return nil, errors.New("内部错误, 用户映射信息有误")
99
+	}
100
+
101
+	userMap.UserId = cust.CustomerId
102
+	if err := s.dao.UpdateUserMapping(userMap, []string{"user_id"}); err != nil {
103
+		utils.LogError("更新用户映射信息失败:" + err.Error())
104
+		return nil, errors.New("映射用户信息失败")
105
+	}
106
+
107
+	// triggerRegiteEvent 触发注册事件
108
+	utils.EmitEvent(events.EvtRegiste, *userMap)
109
+
110
+	return cust, nil
111
+}
112
+
113
+// SaveNewCustomer 新增客户
114
+func (s *CustomerServ) SaveNewCustomer(phone, caseID, userID string) (*model.TaCustomer, error) {
115
+	// 校验案场信息
116
+	cs, err := s.dao.GetCaseByID(caseID)
117
+	if err != nil {
118
+		return nil, err
119
+	}
120
+
121
+	if cs == nil || cs.Status == models.STATUS_DEL {
122
+		return nil, errors.New("系统无该推荐案场")
123
+	}
124
+
125
+	// 校验推荐人
126
+	user, err := s.dao.GetUserByID(userID)
127
+	if err != nil {
128
+		return nil, err
129
+	}
130
+
131
+	if user == nil || user.Status == models.STATUS_DEL {
132
+		return nil, errors.New("系统无该推荐人")
133
+	}
134
+
135
+	// 微信信息
136
+	userMap := s.ctx.Get("userMap").(model.TaUserMapping)
137
+	wxInfo := make(map[string]interface{})
138
+	if err := json.Unmarshal([]byte(userMap.AccountInfo), &wxInfo); err != nil {
139
+		utils.LogError(err.Error())
140
+		return nil, errors.New("校验用户微信信息失败")
141
+	}
142
+
143
+	nickyName := wxInfo["nickname"].(string)
144
+	sex := wxInfo["sex"].(string)
145
+	headimgurl := wxInfo["headimgurl"].(string)
146
+
147
+	sexInt, _ := strconv.Atoi(sex)
148
+	if sexInt == 0 {
149
+		sexInt = 1 // 默认男性
150
+	}
151
+
152
+	cust := model.TaCustomer{
153
+		OrgId:         cs.OrgId,
154
+		CustomerName:  nickyName,
155
+		Name:          nickyName,
156
+		Sex:           sexInt,
157
+		Headimgurl:    headimgurl,
158
+		RecommendId:   user.UserId,
159
+		RecommendName: user.RealName,
160
+		RecommendCase: cs.CaseId,
161
+	}
162
+
163
+	if err := s.dao.SaveCustomer(&cust); err != nil {
164
+		utils.LogError("更新客户信息失败: " + err.Error())
165
+		return nil, errors.New("更新客户信息失败")
166
+	}
167
+
168
+	return &cust, nil
169
+}

+ 16
- 0
service/customer/types.go View File

@@ -0,0 +1,16 @@
1
+package customer
2
+
3
+type UserProfile struct {
4
+	UserId     string
5
+	UserName   string
6
+	RealName   string
7
+	Sex        int
8
+	WorkNo     string
9
+	Email      string
10
+	Phone      string
11
+	Headimgurl string
12
+}
13
+
14
+func (t *UserProfile) TableName() string {
15
+	return "sys_user"
16
+}

+ 1
- 1
service/events/events.go View File

@@ -54,7 +54,7 @@ func getAllEvents() (map[string][]string, error) {
54 54
 }
55 55
 
56 56
 var tsAction = func(e tinyevent.Event) error {
57
-	str := string(e.Payload)
57
+	str := e.Payload.(string)
58 58
 	fmt.Println(str)
59 59
 	return nil
60 60
 }

+ 59
- 5
service/user.go View File

@@ -2,18 +2,15 @@ package service
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"spaceofcheng/services/models"
5 6
 	"spaceofcheng/services/models/model"
6 7
 	"spaceofcheng/services/models/system"
8
+	"spaceofcheng/services/service/events"
7 9
 	"spaceofcheng/services/utils"
8 10
 
9 11
 	"github.com/yl10/kit/encrypt"
10 12
 )
11 13
 
12
-const (
13
-	USERTYPE_USER     = "user"
14
-	USERTYPE_CUSTOMER = "customer"
15
-)
16
-
17 14
 // UserServ 系统处理
18 15
 type UserServ struct {
19 16
 	ctx     *utils.Context
@@ -244,3 +241,60 @@ func (s *UserServ) GetUserCustomer(userid string, page int, pageSize int) ([]mod
244 241
 	customers, err := s.dao.GetUserCustomer(userid, page, pageSize)
245 242
 	return customers, err
246 243
 }
244
+
245
+// CheckUserSignIn 校验用户登录
246
+func (s *UserServ) CheckUserSignIn(userName, userPassword string) (*model.SysUser, error) {
247
+	user, err := s.dao.GetUserByName(userName)
248
+	if err != nil {
249
+		return nil, err
250
+	}
251
+
252
+	if user == nil {
253
+		return nil, errors.New("账户不存在")
254
+	}
255
+
256
+	if user.Status != models.STATUS_NORMAL {
257
+		return nil, errors.New("用户账户异常")
258
+	}
259
+
260
+	if !s.dao.ValidatePassword(user, userPassword) {
261
+		return nil, errors.New("用户密码不正确")
262
+	}
263
+
264
+	return user, nil
265
+}
266
+
267
+// BindWechatUser 绑定微信用户
268
+func (s *UserServ) BindWechatUser(userMap *model.TaUserMapping, phone string) (*model.SysUser, error) {
269
+	user, err := s.dao.GetUserByPhone(phone)
270
+	if err != nil {
271
+		return nil, err
272
+	}
273
+
274
+	if user == nil {
275
+		return nil, errors.New("手机号对应用户不存在")
276
+	}
277
+
278
+	if user.Status != models.STATUS_NORMAL {
279
+		return nil, errors.New("用户状态不正确")
280
+	}
281
+
282
+	if user.UserId == userMap.UserId {
283
+		return user, nil
284
+	}
285
+
286
+	if userMap.UserId != "" {
287
+		return nil, errors.New("内部错误, 用户映射信息有误")
288
+	}
289
+
290
+	userMap.UserId = user.UserId
291
+	if err := s.dao.UpdateUserMapping(userMap, []string{"user_id"}); err != nil {
292
+		utils.LogError("更新用户映射信息失败:" + err.Error())
293
+		return nil, errors.New("映射用户信息失败")
294
+	}
295
+
296
+	// triggerRegiteEvent 触发注册事件
297
+	utils.EmitEvent(events.EvtRegiste, *userMap)
298
+
299
+	return user, nil
300
+}

BIN
swagger.zip View File


+ 99
- 0
utils/captcha.go View File

@@ -0,0 +1,99 @@
1
+package utils
2
+
3
+import (
4
+	"errors"
5
+	"math/rand"
6
+	"strconv"
7
+	"strings"
8
+	"time"
9
+)
10
+
11
+type cap struct {
12
+	Phone   string
13
+	Captcha string
14
+	Expire  time.Time
15
+}
16
+
17
+// CaptchaEngine 验证码引擎
18
+type CaptchaEngine struct {
19
+	capList map[string]cap
20
+}
21
+
22
+var capEngine *CaptchaEngine
23
+
24
+// NewCaptchaEngine 创建验证码引擎
25
+func NewCaptchaEngine() *CaptchaEngine {
26
+	return &CaptchaEngine{
27
+		capList: make(map[string]cap),
28
+	}
29
+}
30
+
31
+// SendCaptcha 发送验证码
32
+func SendCaptcha(phone string) error {
33
+	if phone == "" {
34
+		return errors.New("验证码发送错误: 没有手机号")
35
+	}
36
+
37
+	capEngine.SendCaptcha(phone)
38
+	return nil
39
+}
40
+
41
+// ValidCaptcha 校验 验证码
42
+func ValidCaptcha(phone, cap string) error {
43
+	if phone == "" {
44
+		return errors.New("验证码校验失败: 没有手机号")
45
+	}
46
+
47
+	return capEngine.ValidCaptcha(phone, cap)
48
+}
49
+
50
+// Random 获取随机数
51
+// 连续调用会有随机数不随机问题
52
+func (c *CaptchaEngine) Random(length int, phone string) string {
53
+	phoneInt, _ := strconv.ParseInt(phone, 10, 64)
54
+
55
+	r := rand.New(rand.NewSource(time.Now().UnixNano() + phoneInt))
56
+	dict := strings.Split("0123456789", "")
57
+
58
+	res := ""
59
+	for i := 0; i < length; i++ {
60
+		res = res + dict[r.Intn(10)]
61
+	}
62
+
63
+	return res
64
+}
65
+
66
+// SendCaptcha 发送验证码
67
+func (c *CaptchaEngine) SendCaptcha(phone string) {
68
+	theCap := cap{
69
+		Phone:   phone,
70
+		Captcha: capEngine.Random(6, phone),
71
+		Expire:  time.Now().Local().Add(1 * time.Minute),
72
+	}
73
+
74
+	c.capList[phone] = theCap
75
+
76
+	SendSMS(theCap.Captcha, theCap.Phone)
77
+}
78
+
79
+// ValidCaptcha 验证 验证码
80
+func (c *CaptchaEngine) ValidCaptcha(phone, cap string) error {
81
+	theCap, ok := c.capList[phone]
82
+	if !ok {
83
+		return errors.New("未发送过该号码的验证码")
84
+	}
85
+
86
+	if time.Now().Local().Before(theCap.Expire) {
87
+		return errors.New("验证码已过期")
88
+	}
89
+
90
+	if theCap.Captcha != cap {
91
+		return errors.New("验证码不正确")
92
+	}
93
+
94
+	return nil
95
+}
96
+
97
+func init() {
98
+	capEngine = NewCaptchaEngine()
99
+}

+ 18
- 0
utils/captcha_test.go View File

@@ -0,0 +1,18 @@
1
+package utils_test
2
+
3
+import (
4
+	"spaceofcheng/services/utils"
5
+	"testing"
6
+	"time"
7
+)
8
+
9
+func TestRandom(t *testing.T) {
10
+	capEngine := utils.NewCaptchaEngine()
11
+	r1 := capEngine.Random(6, "13823838438")
12
+	time.Sleep(1 * time.Microsecond)
13
+	r2 := capEngine.Random(6, "13823838438")
14
+
15
+	if r1 == r2 {
16
+		t.Fatalf("Test CaptchaEngine Random fail, %s", r2)
17
+	}
18
+}

+ 1
- 1
utils/event.go View File

@@ -7,7 +7,7 @@ import (
7 7
 var evtBus = &tinyevent.DefaultBus{}
8 8
 
9 9
 // EmitEvent 执行事件
10
-func EmitEvent(evt string, payload []byte) {
10
+func EmitEvent(evt string, payload interface{}) {
11 11
 	e := tinyevent.Event{
12 12
 		Name:    evt,
13 13
 		Payload: payload,

+ 34
- 0
utils/jwt.go View File

@@ -0,0 +1,34 @@
1
+package utils
2
+
3
+import (
4
+	"errors"
5
+
6
+	jwt "github.com/dgrijalva/jwt-go"
7
+)
8
+
9
+var tokenSignedKey = []byte(`Yansen is so handsome!`)
10
+
11
+// CreateToken 获取token
12
+func CreateToken(info map[string]interface{}) (string, error) {
13
+	claims := jwt.MapClaims(info)
14
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
15
+	return token.SignedString(tokenSignedKey)
16
+}
17
+
18
+// PareseToken 解析 token
19
+func PareseToken(token string) (map[string]interface{}, error) {
20
+	tk, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
21
+		return tokenSignedKey, nil
22
+	})
23
+
24
+	if err != nil {
25
+		return nil, err
26
+	}
27
+
28
+	if claims, ok := tk.Claims.(jwt.MapClaims); ok && tk.Valid {
29
+		info := map[string]interface{}(claims)
30
+		return info, nil
31
+	}
32
+
33
+	return nil, errors.New("Token 解析 未知错误")
34
+}

+ 5
- 0
utils/message.go View File

@@ -33,3 +33,8 @@ func SendMessage(message Message, msgType string) {
33 33
 	default:
34 34
 	}
35 35
 }
36
+
37
+// SendSMS 发送短信
38
+func SendSMS(message interface{}, to string) {
39
+
40
+}

+ 61
- 0
utils/pagenavi.go View File

@@ -0,0 +1,61 @@
1
+package utils
2
+
3
+import (
4
+	"errors"
5
+	"regexp"
6
+	"strconv"
7
+
8
+	"github.com/go-xorm/xorm"
9
+)
10
+
11
+// GetPageList 获取列表
12
+func GetPageList(db *xorm.Session, target interface{}, limit []int, sqlorArgs ...interface{}) (int64, error) {
13
+	if sqlorArgs == nil || len(sqlorArgs) == 0 {
14
+		return 0, errors.New("SQL 语句不存在")
15
+	}
16
+
17
+	countSQLorArgs := GetCountSQL(sqlorArgs...)
18
+	cntRes, err := db.Query(countSQLorArgs...)
19
+	if err != nil {
20
+		return 0, err
21
+	}
22
+
23
+	cntStr := string(cntRes[0]["cnt"])
24
+	cnt, _ := strconv.ParseInt(cntStr, 10, 64)
25
+
26
+	sql, args := PackLimitToSQL(limit, sqlorArgs...)
27
+
28
+	err = db.SQL(sql, args...).Find(target)
29
+	if err != nil {
30
+		return 0, err
31
+	}
32
+
33
+	return cnt, nil
34
+}
35
+
36
+// GetCountSQL 获取计算 Count 的相关 SQL 脚本
37
+func GetCountSQL(sqlorArgs ...interface{}) []interface{} {
38
+	sql := sqlorArgs[0].(string)
39
+
40
+	re := regexp.MustCompile("(?ims)(select .* from)")
41
+	cntSQL := re.ReplaceAllString(sql, "select count(*) as cnt from")
42
+
43
+	newSQLorArgs := []interface{}{cntSQL}
44
+	newSQLorArgs = append(newSQLorArgs, sqlorArgs[1:])
45
+	return newSQLorArgs
46
+}
47
+
48
+// PackLimitToSQL 添加 limit 语句到 sql 最后
49
+func PackLimitToSQL(limit []int, sqlorArgs ...interface{}) (string, []interface{}) {
50
+	sql := sqlorArgs[0].(string)
51
+	args := sqlorArgs[1:]
52
+
53
+	switch len(limit) {
54
+	case 1:
55
+		return sql + " LIMIT " + strconv.Itoa(limit[0]), args
56
+	case 2:
57
+		return sql + " LIMIT " + strconv.Itoa(limit[0]) + " OFFSET " + strconv.Itoa(limit[1]), args
58
+	default:
59
+		return sql, args
60
+	}
61
+}

+ 71
- 0
utils/pagenavi_test.go View File

@@ -0,0 +1,71 @@
1
+package utils_test
2
+
3
+import (
4
+	"spaceofcheng/services/utils"
5
+	"testing"
6
+)
7
+
8
+func TestGetCountSQL(t *testing.T) {
9
+	testCases := []map[string]string{
10
+		map[string]string{
11
+			"sql":      "Select field1, field2 From table",
12
+			"expected": "select count(*) as cnt from table",
13
+		},
14
+		map[string]string{
15
+			"sql": `Select 
16
+								field1, field2 From table where status = ?`,
17
+			"expected": "select count(*) as cnt from table where status = ?",
18
+		},
19
+	}
20
+
21
+	for inx, cs := range testCases {
22
+		sql := cs["sql"]
23
+		expected := cs["expected"]
24
+
25
+		var res []interface{}
26
+		if inx < 1 {
27
+			res = utils.GetCountSQL(sql)
28
+		} else {
29
+			res = utils.GetCountSQL(sql, inx)
30
+		}
31
+
32
+		if res == nil || len(res) == 0 {
33
+			t.Fatalf("Test GetCountSQL fail --1")
34
+		}
35
+
36
+		newSQL := res[0].(string)
37
+		if newSQL != expected {
38
+			t.Fatalf("Test GetCountSQL fail: %s", newSQL)
39
+		}
40
+	}
41
+}
42
+
43
+func TestPackLimitToSQL(t *testing.T) {
44
+	testCases := []map[string]string{
45
+		map[string]string{
46
+			"sql":      "Select * From table",
47
+			"expected": "Select * From table LIMIT 1 OFFSET 0",
48
+		},
49
+		map[string]string{
50
+			"sql":      "Select * From table",
51
+			"expected": "Select * From table LIMIT 1",
52
+		},
53
+	}
54
+
55
+	for inx, cs := range testCases {
56
+		sql := cs["sql"]
57
+		expected := cs["expected"]
58
+
59
+		var sqlRes string
60
+		if inx < 1 {
61
+			sqlRes, _ = utils.PackLimitToSQL([]int{inx + 1, inx}, sql)
62
+		} else {
63
+			sqlRes, _ = utils.PackLimitToSQL([]int{inx}, sql, inx)
64
+		}
65
+
66
+		if sqlRes != expected {
67
+			t.Fatalf("\n%v: Test PackLimitToSQL fail: %s", inx+1, sqlRes)
68
+		}
69
+	}
70
+
71
+}