浏览代码

merge yansen

zjxpcyc 6 年前
父节点
当前提交
5214ddee72
共有 14 个文件被更改,包括 3740 次插入242 次删除
  1. 1
    1
      conf/app.conf
  2. 36
    177
      controllers/auth.go
  3. 9
    1
      controllers/base.go
  4. 0
    1
      controllers/context.go
  5. 23
    0
      controllers/file.go
  6. 4
    3
      controllers/user/user.go
  7. 3077
    0
      log/common.log
  8. 10
    0
      models/model/sys_token_log.go
  9. 40
    0
      models/sys.go
  10. 11
    24
      service/customer/customer.go
  11. 357
    35
      service/sys.go
  12. 68
    0
      utils/excel.go
  13. 48
    0
      utils/jwt.go
  14. 56
    0
      utils/wechat.go

+ 1
- 1
conf/app.conf 查看文件

4
 autorender = false
4
 autorender = false
5
 copyrequestbody = true
5
 copyrequestbody = true
6
 EnableDocs = true
6
 EnableDocs = true
7
-sessionon = true
7
+sessionon = false
8
 excelpath = ./
8
 excelpath = ./
9
 clienturl = http://dev.ycjcjy.com/c-v2
9
 clienturl = http://dev.ycjcjy.com/c-v2
10
 
10
 

+ 36
- 177
controllers/auth.go 查看文件

1
 package controllers
1
 package controllers
2
 
2
 
3
 import (
3
 import (
4
-	"errors"
5
 	"net/http"
4
 	"net/http"
6
-	"spaceofcheng/services/models/model"
7
 	"spaceofcheng/services/service"
5
 	"spaceofcheng/services/service"
8
-	"spaceofcheng/services/utils"
9
-	"strings"
10
-
11
-	"github.com/astaxie/beego"
12
 )
6
 )
13
 
7
 
14
 // Authenticate 权限验证
8
 // Authenticate 权限验证
9
+// 其中 token 的处理方式是
10
+// 1、获取 request 中 token
11
+// 2、放入 Context 中
12
+// 3、校验 token
13
+// 4、结束后,设置过期并从 Context 中删除
14
+// 5、生成新的 token, 并放入 Context 中
15
 func (c *BaseController) authenticate() {
15
 func (c *BaseController) authenticate() {
16
-
17
-	// 默认 service
18
 	serv := service.NewSysServ(c.Context)
16
 	serv := service.NewSysServ(c.Context)
19
 
17
 
20
-	orgID := c.GetString(":org")
21
-	if orgID == "" {
22
-		c.ResponseError(errors.New("接口地址访问不正确"))
23
-	}
24
-	serv.SetOrgByID(orgID)
25
-
26
-	// 客户端类型
27
-	clientType := utils.GetClientType(c.Ctx.Request)
28
-
29
-	switch clientType {
30
-	case utils.ClientAdmin:
31
-		c.authPCAdmin(serv)
32
-	case utils.ClientWechat:
33
-		c.authWechat(serv)
34
-	default:
35
-		c.ResponseError(
36
-			errors.New("暂不支持的 API 场景"),
37
-			http.StatusBadRequest,
38
-		)
39
-	}
40
-}
41
-
42
-// authPCAdmin PC管理端
43
-func (c *BaseController) authPCAdmin(serv *service.SysServ) {
44
-	if !c.needAuth() {
45
-		return
46
-	}
47
-
48
-	// 用户ID
49
-	userID := ""
50
-	userIDRaw := c.GetSession(SNUserID)
51
-	if userIDRaw != nil {
52
-		userID = userIDRaw.(string)
53
-	}
54
-
55
-	if userID == "" {
56
-		c.ResponseError(
57
-			errors.New("用户未登录"),
58
-			http.StatusUnauthorized,
59
-		)
60
-	}
61
-
62
-	if err := serv.SetUserProfile(userID); err != nil {
63
-		// utils.LogError(err.Error())
64
-
65
-		c.ResponseError(
66
-			utils.LogError(err.Error()),
67
-			http.StatusInternalServerError,
68
-		)
69
-	}
70
-
71
-	// 设置 Session
72
-	c.SetSession(SNUserID, userID)
73
-}
74
-
75
-// authWechat 微信端
76
-func (c *BaseController) authWechat(serv *service.SysServ) {
77
-	custID := ""
78
-	custIDRaw := c.GetSession(SNCustID)
79
-	if custIDRaw != nil {
80
-		custID = custIDRaw.(string)
81
-	}
18
+	// 鉴权 - 并初始化上下文
19
+	res := serv.AuthAndInitCtx(c.Ctx)
82
 
20
 
83
-	// 机构
84
-	org := c.Context.Get("org").(model.SysOrg)
85
-
86
-	// 微信配置
87
-	wxConf, err := serv.GetWeChatConfig(org.OrgId)
88
-	if err != nil {
89
-		utils.LogError("查询微信配置失败: " + err.Error())
90
-		c.ResponseError(
91
-			errors.New("没有找到微信相关配置"),
92
-			http.StatusBadRequest,
93
-		)
94
-	}
95
-	utils.WxClientSingleton(org.OrgId, wxConf)
96
-
97
-	// 用户微信信息
98
-	var wxDetail map[string]interface{}
99
-
100
-	detailRaw := c.GetSession("wechat_user")
101
-	if detailRaw != nil {
102
-		wxDetail = detailRaw.(map[string]interface{})
103
-	} else {
104
-		// DEV MODE
105
-		if c.RunMode == "dev" {
106
-			wxDetail = map[string]interface{}{
107
-				"openid":     "OPENID",
108
-				"nickname":   "NICKNAME",
109
-				"sex":        "1",
110
-				"province":   "PROVINCE",
111
-				"city":       "CITY",
112
-				"country":    "COUNTRY",
113
-				"headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
114
-				"unionid":    "o6_bmasdasdsad6_2sgVt7hMZOPfL",
115
-			}
116
-		} else {
117
-			wxDetail = c.getWechatDetail(org.OrgId, serv)
21
+	if res != nil {
22
+		code := http.StatusOK
23
+		if res["code"] != nil {
24
+			code = res["code"].(int)
118
 		}
25
 		}
119
 
26
 
120
-		c.SetSession("wechat_user", wxDetail)
121
-	}
27
+		if code != http.StatusOK {
28
+			err := res["error"].(error)
29
+			data := map[string]interface{}{}
122
 
30
 
123
-	c.Context.Set("wxInfo", wxDetail)
31
+			if res["message"] != nil {
32
+				data = res["message"].(map[string]interface{})
33
+			}
124
 
34
 
125
-	// 用户映射检查
126
-	userMap, err := serv.CheckWechatUserMapping(wxDetail)
127
-	if err != nil {
128
-		c.ResponseError(
129
-			utils.LogError("获取人员映射账户失败: "+err.Error()),
130
-			http.StatusInternalServerError,
131
-		)
132
-	}
133
-	c.Context.Set("userMap", *userMap)
35
+			// 设置旧 token 过期
36
+			c.SetTokenExipre()
134
 
37
 
135
-	if !c.needAuth() {
136
-		return
137
-	}
138
-	// 未登录或者绑定, 返回 406
139
-	if custID == "" && userMap.UserId == "" {
140
-		// 新增用户信息
141
-		cust, err := serv.SaveNewCustomer(wxDetail, userMap)
142
-		if err != nil {
143
-			c.ResponseError(err)
38
+			c.ResponseData(data, err, code)
144
 		}
39
 		}
145
-		custID = cust.CustomerId
146
-		// c.ResponseError(
147
-		// 	errors.New("用户未登录或绑定"),
148
-		// 	http.StatusNotAcceptable,
149
-		// )
150
 	}
40
 	}
151
 
41
 
152
-	if custID == "" {
153
-		custID = userMap.UserId
154
-	}
155
-	if custID != "" {
156
-		if err := serv.SetCustomer(custID); err != nil {
157
-			utils.LogError(err.Error())
42
+	// 设置旧 token 过期
43
+	c.SetTokenExipre()
158
 
44
 
159
-			c.ResponseError(
160
-				errors.New("内部错误, 请重试"),
161
-				http.StatusInternalServerError,
162
-			)
163
-		}
164
-
165
-		// 设置 Session
166
-		c.SetSession(SNCustID, custID)
167
-	}
45
+	// 生成新 token
46
+	c.CreateNewToken()
168
 }
47
 }
169
 
48
 
170
-func (c *BaseController) needAuth() bool {
171
-	route := c.Ctx.Input.URL()
172
-	apiPrefix := beego.AppConfig.String("api::prefix")
173
-	guestAPI := beego.AppConfig.String("api::guest")
174
-
175
-	if strings.Index(route, apiPrefix+strings.Split(guestAPI, ":")[0]) > -1 {
176
-		return false
49
+// SetTokenExipre 设置 token 过期
50
+func (c *BaseController) SetTokenExipre() {
51
+	token := c.Context.Get("token")
52
+	if token != nil {
53
+		serv := service.NewSysServ(c.Context)
54
+		serv.UpdateTokenExpire(token.(string))
177
 	}
55
 	}
178
 
56
 
179
-	return true
57
+	c.Context.Set("token", "")
180
 }
58
 }
181
 
59
 
182
-// getWechatDetail 获取微信个人信息详情
183
-func (c *BaseController) getWechatDetail(org string, serv *service.SysServ) map[string]interface{} {
184
-	// 微信 code
185
-	code := c.GetString("code")
186
-	if code == "" {
187
-		c.ResponseData(
188
-			map[string]interface{}{
189
-				"appid": utils.GetWxAppID(org),
190
-			},
191
-			errors.New("获取微信信息失败"),
192
-			http.StatusUnauthorized,
193
-		)
194
-	}
195
-
196
-	usr, err := utils.WxClientFor(org).GetUserInfo(code)
197
-	if err != nil {
198
-		c.ResponseError(
199
-			utils.LogError("获取微信个人信息失败: "+err.Error()),
200
-			http.StatusInternalServerError,
201
-		)
202
-	}
203
-
204
-	return usr
60
+// CreateNewToken 新 token
61
+func (c *BaseController) CreateNewToken() {
62
+	serv := service.NewSysServ(c.Context)
63
+	c.Context.Set("token", serv.NewToken())
205
 }
64
 }

+ 9
- 1
controllers/base.go 查看文件

62
 	}
62
 	}
63
 
63
 
64
 	c.Data["json"] = JSONMessage{status, sendMessage, data}
64
 	c.Data["json"] = JSONMessage{status, sendMessage, data}
65
-	c.Ctx.Output.Header("Access-Control-Expose-Headers", "X-Token")
65
+	c.Ctx.Output.Header("Access-Control-Expose-Headers", utils.TokenHeader)
66
+
67
+	token := c.Context.Get("token")
68
+	if token != nil {
69
+		tk := token.(string)
70
+		if tk != "" {
71
+			c.Ctx.Output.Header(utils.TokenHeader, utils.TokenSchema+" "+tk)
72
+		}
73
+	}
66
 
74
 
67
 	c.crosPolicy()
75
 	c.crosPolicy()
68
 	c.ServeJSON()
76
 	c.ServeJSON()

+ 0
- 1
controllers/context.go 查看文件

12
 * user					SysUser 									用户基本信息
12
 * user					SysUser 									用户基本信息
13
 * customer			TaCustomer 								会员基本信息
13
 * customer			TaCustomer 								会员基本信息
14
 * userMap				TaUserMapping 						用户/会员 映射第三方账户
14
 * userMap				TaUserMapping 						用户/会员 映射第三方账户
15
-* wxInfo				map[string]interface{} 		微信信息
16
 * cases					[]SysUserCase 						用户所有案场信息
15
 * cases					[]SysUserCase 						用户所有案场信息
17
 * currentCase		SysUserCase 							当前案场
16
 * currentCase		SysUserCase 							当前案场
18
 * org						SysOrg 										用户当前组织
17
 * org						SysOrg 										用户当前组织

+ 23
- 0
controllers/file.go 查看文件

1
 package controllers
1
 package controllers
2
 
2
 
3
 import (
3
 import (
4
+	"bytes"
4
 	"errors"
5
 	"errors"
5
 	"spaceofcheng/services/utils"
6
 	"spaceofcheng/services/utils"
6
 	"strconv"
7
 	"strconv"
7
 	"time"
8
 	"time"
8
 
9
 
9
 	"net/http"
10
 	"net/http"
11
+	"net/url"
10
 )
12
 )
11
 
13
 
12
 // FileUpload 文件上传
14
 // FileUpload 文件上传
46
 
48
 
47
 	return fileURL, nil
49
 	return fileURL, nil
48
 }
50
 }
51
+
52
+// SaveToExcel 保存文件到 excel
53
+func (c *BaseController) SaveToExcel(fn string, data interface{}, f func(interface{}) []interface{}) {
54
+	var buf bytes.Buffer
55
+	if err := utils.NewTinyXLSXEngine().SetTransFunc(f).SetData(data).Write(&buf); err != nil {
56
+		utils.LogError("写 xlsx buffer 失败: " + err.Error())
57
+		c.ResponseError(errors.New("生成 excel 异常, 请重试"))
58
+	}
59
+
60
+	c.Ctx.Output.Header("Content-Disposition", "attachment; filename="+url.QueryEscape(fn))
61
+	c.Ctx.Output.Header("Content-Description", "File Transfer")
62
+	c.Ctx.Output.ContentType(".xlsx")
63
+	c.Ctx.Output.Header("Content-Transfer-Encoding", "binary")
64
+	c.Ctx.Output.Header("Expires", "0")
65
+	c.Ctx.Output.Header("Cache-Control", "must-revalidate")
66
+	c.Ctx.Output.Header("Pragma", "public")
67
+
68
+	r := bytes.NewReader(buf.Bytes())
69
+	http.ServeContent(c.Ctx.ResponseWriter, c.Ctx.Request, fn, time.Now().Local(), r)
70
+	c.StopRun()
71
+}

+ 4
- 3
controllers/user/user.go 查看文件

182
 		)
182
 		)
183
 	}
183
 	}
184
 
184
 
185
-	// 成功之后, 设置 session
186
-	c.SetSession(controllers.SNUserID, user.UserId)
185
+	// 成功之后, 生成新 token
186
+	c.Context.Set("user", *user)
187
+	c.CreateNewToken()
187
 
188
 
188
 	if token == "" && doRemember != 0 {
189
 	if token == "" && doRemember != 0 {
189
 		var err error
190
 		var err error
205
 
206
 
206
 // SignOut 用户登出
207
 // SignOut 用户登出
207
 func (c *UserController) SignOut() {
208
 func (c *UserController) SignOut() {
208
-	c.DestroySession()
209
+	c.SetTokenExipre()
209
 	c.ResponseJSON("ok")
210
 	c.ResponseJSON("ok")
210
 }
211
 }
211
 
212
 

+ 3077
- 0
log/common.log
文件差异内容过多而无法显示
查看文件


+ 10
- 0
models/model/sys_token_log.go 查看文件

1
+package model
2
+
3
+import "time"
4
+
5
+type SysTokenLog struct {
6
+	TokenId    int       `xorm:"not null pk autoincr INT(11)"`
7
+	Token      string    `xorm:"TEXT"`
8
+	Status     int       `xorm:"SMALLINT(6)"`
9
+	CreateDate time.Time `xorm:"DATETIME"`
10
+}

+ 40
- 0
models/sys.go 查看文件

4
 	"errors"
4
 	"errors"
5
 	"spaceofcheng/services/models/model"
5
 	"spaceofcheng/services/models/model"
6
 	"spaceofcheng/services/utils"
6
 	"spaceofcheng/services/utils"
7
+	"time"
7
 
8
 
8
 	"github.com/yl10/kit/guid"
9
 	"github.com/yl10/kit/guid"
9
 
10
 
165
 
166
 
166
 	return conf, nil
167
 	return conf, nil
167
 }
168
 }
169
+
170
+// InsertToken 插入 token 生成记录
171
+func (m *SysDAO) InsertToken(token string) error {
172
+	tk := model.SysTokenLog{
173
+		Token:      token,
174
+		Status:     STATUS_NORMAL,
175
+		CreateDate: time.Now().Local(),
176
+	}
177
+
178
+	if _, err := m.db.Insert(&tk); err != nil {
179
+		return err
180
+	}
181
+
182
+	return nil
183
+}
184
+
185
+// UpdateTokenExpire 设置 token 过期
186
+func (m *SysDAO) UpdateTokenExpire(token string) error {
187
+	tk := model.SysTokenLog{
188
+		Status: STATUS_DEL,
189
+	}
190
+
191
+	if _, err := m.db.Cols("status").Where("token=?", token).Update(&tk); err != nil {
192
+		return err
193
+	}
194
+
195
+	return nil
196
+}
197
+
198
+// GetToken 获取 token
199
+func (m *SysDAO) GetToken(token string) (*model.SysTokenLog, error) {
200
+	tk := model.SysTokenLog{}
201
+
202
+	if _, err := m.db.Where("token=?", token).Get(&tk); err != nil {
203
+		return nil, err
204
+	}
205
+
206
+	return &tk, nil
207
+}

+ 11
- 24
service/customer/customer.go 查看文件

1
 package customer
1
 package customer
2
 
2
 
3
 import (
3
 import (
4
+	"encoding/json"
4
 	"errors"
5
 	"errors"
5
 	"spaceofcheng/services/models"
6
 	"spaceofcheng/services/models"
6
 	"spaceofcheng/services/models/cases"
7
 	"spaceofcheng/services/models/cases"
104
 	}
105
 	}
105
 
106
 
106
 	if cust == nil {
107
 	if cust == nil {
107
-		wxInfoRaw := s.ctx.Get("wxInfo")
108
-		if wxInfoRaw == nil {
109
-			return nil, errors.New("请确定使用微信端登录")
108
+		wxInfo := utils.WechatUser{}
109
+		if err := json.Unmarshal([]byte(userMap.AccountInfo), &wxInfo); err != nil {
110
+			utils.LogError("解析用户微信映射信息失败: " + err.Error())
111
+			return nil, errors.New("系统数据异常")
110
 		}
112
 		}
111
 
113
 
112
-		wxInfo := wxInfoRaw.(map[string]interface{})
113
 		// 更新用户手机号码信息及userid信息
114
 		// 更新用户手机号码信息及userid信息
114
-		openid := wxInfo["openid"].(string)
115
-		customer, err := s.dao.GetCustWithWXByOpenID(openid)
115
+		customer, err := s.dao.GetCustWithWXByOpenID(wxInfo.OpenID)
116
 
116
 
117
 		newCust := model.TaCustomer{
117
 		newCust := model.TaCustomer{
118
 			CustomerId: customer.CustomerId,
118
 			CustomerId: customer.CustomerId,
128
 			return nil, err
128
 			return nil, err
129
 		}
129
 		}
130
 
130
 
131
-		// newCust, err := s.SaveNewCustomer(wxInfo, "", "", user.Phone, user.UserId)
132
-		// if err != nil {
133
-		// 	utils.LogError(err.Error())
134
-		// 	return nil, err
135
-		// }
136
 		cust = &newCust
131
 		cust = &newCust
137
 	} else {
132
 	} else {
138
 		if cust.Status != models.STATUS_NORMAL {
133
 		if cust.Status != models.STATUS_NORMAL {
168
 
163
 
169
 	// 用户不存在, 则新增
164
 	// 用户不存在, 则新增
170
 	if cust == nil {
165
 	if cust == nil {
171
-		wxInfoRaw := s.ctx.Get("wxInfo")
172
-		if wxInfoRaw == nil {
173
-			return nil, errors.New("请确定使用微信端登录")
166
+		wxInfo := utils.WechatUser{}
167
+		if err := json.Unmarshal([]byte(userMap.AccountInfo), &wxInfo); err != nil {
168
+			utils.LogError("解析用户微信映射信息失败: " + err.Error())
169
+			return nil, errors.New("系统数据异常")
174
 		}
170
 		}
175
 
171
 
176
-		wxInfo := wxInfoRaw.(map[string]interface{})
177
-
178
 		// 更新用户手机号码信息及userid信息
172
 		// 更新用户手机号码信息及userid信息
179
-		openid := wxInfo["openid"].(string)
180
-		customer, err := s.dao.GetCustWithWXByOpenID(openid)
173
+		customer, err := s.dao.GetCustWithWXByOpenID(wxInfo.OpenID)
181
 		newCust := model.TaCustomer{
174
 		newCust := model.TaCustomer{
182
 			CustomerId:  customer.CustomerId,
175
 			CustomerId:  customer.CustomerId,
183
 			Phone:       phone,
176
 			Phone:       phone,
228
 			return nil, err
221
 			return nil, err
229
 		}
222
 		}
230
 
223
 
231
-		// newCust, err := s.SaveNewCustomer(wxInfo, caseID, userID, phone, "")
232
-		// if err != nil {
233
-		// 	utils.LogError(err.Error())
234
-		// 	return nil, err
235
-		// }
236
-
237
 		cust = &newCust
224
 		cust = &newCust
238
 	} else {
225
 	} else {
239
 		if cust.Status != models.STATUS_NORMAL {
226
 		if cust.Status != models.STATUS_NORMAL {

+ 357
- 35
service/sys.go 查看文件

3
 import (
3
 import (
4
 	"encoding/json"
4
 	"encoding/json"
5
 	"errors"
5
 	"errors"
6
+	"net/http"
6
 	"spaceofcheng/services/models"
7
 	"spaceofcheng/services/models"
7
 	"spaceofcheng/services/models/customer"
8
 	"spaceofcheng/services/models/customer"
8
 	"spaceofcheng/services/models/model"
9
 	"spaceofcheng/services/models/model"
9
 	"spaceofcheng/services/utils"
10
 	"spaceofcheng/services/utils"
11
+	"strings"
12
+	"time"
10
 
13
 
11
 	"github.com/astaxie/beego"
14
 	"github.com/astaxie/beego"
15
+	"github.com/astaxie/beego/context"
12
 )
16
 )
13
 
17
 
14
 const (
18
 const (
17
 
21
 
18
 // SysServ 系统处理
22
 // SysServ 系统处理
19
 type SysServ struct {
23
 type SysServ struct {
24
+	org         model.SysOrg
20
 	ctx         *utils.Context
25
 	ctx         *utils.Context
21
 	dao         *models.SysDAO
26
 	dao         *models.SysDAO
22
 	customerdao *customer.CustomerDAO
27
 	customerdao *customer.CustomerDAO
31
 	}
36
 	}
32
 }
37
 }
33
 
38
 
39
+// AuthAndInitCtx 鉴权
40
+// gctx 是 beego 框架中的 Context
41
+func (s *SysServ) AuthAndInitCtx(gctx *context.Context) map[string]interface{} {
42
+	// 确认机构
43
+	orgID := gctx.Input.Query(":org")
44
+	if orgID == "" {
45
+		return map[string]interface{}{
46
+			"code":  http.StatusBadRequest,
47
+			"error": errors.New("接口地址访问不正确"),
48
+		}
49
+	}
50
+	if err := s.SetOrgByID(orgID); err != nil {
51
+		return map[string]interface{}{
52
+			"code":  http.StatusInternalServerError,
53
+			"error": err,
54
+		}
55
+	}
56
+
57
+	// 获取 token
58
+	token, err := s.getToken(gctx)
59
+	if err != nil {
60
+		// token 报错一律视为需要重新登录
61
+		return map[string]interface{}{
62
+			"code":  http.StatusUnauthorized,
63
+			"error": err,
64
+		}
65
+	}
66
+
67
+	// 客户端类型
68
+	// 通过 UA 判断
69
+	clientType := utils.GetClientType(gctx.Request)
70
+
71
+	// pc 管理端
72
+	if clientType == utils.ClientAdmin {
73
+		return s.authPCAdmin(gctx, token)
74
+	}
75
+
76
+	// wechat 端
77
+	if clientType == utils.ClientWechat {
78
+		return s.authWechat(gctx, token)
79
+	}
80
+
81
+	return map[string]interface{}{
82
+		"code":  http.StatusBadRequest,
83
+		"error": errors.New("暂无该客户端的 API"),
84
+	}
85
+}
86
+
87
+// NewToken 设置 TOKEN
88
+// 15 分钟后过期
89
+func (s *SysServ) NewToken() string {
90
+	var token *utils.JWTToken
91
+	exp := time.Now().Local().Add(15 * time.Second)
92
+
93
+	if s.ctx.Get("userMap") != nil {
94
+		userMap := s.ctx.Get("userMap").(model.TaUserMapping)
95
+
96
+		token = &utils.JWTToken{
97
+			Guest:  false,
98
+			ID:     userMap.Openid,
99
+			Expire: exp,
100
+		}
101
+	} else if s.ctx.Get("user") != nil {
102
+		user := s.ctx.Get("user").(model.SysUser)
103
+
104
+		token = &utils.JWTToken{
105
+			Guest:  false,
106
+			ID:     user.UserId,
107
+			Expire: exp,
108
+		}
109
+	} else {
110
+		token = &utils.JWTToken{
111
+			Guest:  true,
112
+			Expire: exp,
113
+		}
114
+	}
115
+
116
+	tokenEncodeStr, err := utils.CreateToken(token.ToMap())
117
+	if err != nil {
118
+		utils.LogError("系统生成 Token 失败: " + err.Error())
119
+		return ""
120
+	}
121
+
122
+	// 入库
123
+	if !token.Guest {
124
+		if err := s.dao.InsertToken(tokenEncodeStr); err != nil {
125
+			utils.LogError("入库 Token 失败: " + err.Error())
126
+			return tokenEncodeStr
127
+		}
128
+	}
129
+
130
+	return tokenEncodeStr
131
+}
132
+
133
+// authPCAdmin
134
+// 管理端 API 校验
135
+func (s *SysServ) authPCAdmin(gctx *context.Context, token *utils.JWTToken) map[string]interface{} {
136
+	if !s.needAuth(gctx) {
137
+		return nil
138
+	}
139
+
140
+	if token.ID == "" || token.Guest == true {
141
+		return map[string]interface{}{
142
+			"code":  http.StatusUnauthorized,
143
+			"error": errors.New("用户未登录"),
144
+		}
145
+	}
146
+
147
+	if err := s.SetUserProfile(token.ID); err != nil {
148
+		return map[string]interface{}{
149
+			"code":  http.StatusInternalServerError,
150
+			"error": err,
151
+		}
152
+	}
153
+
154
+	return nil
155
+}
156
+
157
+func (s *SysServ) authWechat(gctx *context.Context, token *utils.JWTToken) map[string]interface{} {
158
+	// 微信 code
159
+	code := gctx.Input.Query("code")
160
+
161
+	var wxUser *utils.WechatUser
162
+	var openID string
163
+
164
+	// 未登录 或 未验证
165
+	if token.ID == "" {
166
+		// 需要微信验证
167
+		if code == "" {
168
+			return map[string]interface{}{
169
+				"code":  http.StatusUnauthorized,
170
+				"error": errors.New("请授权微信用户登录"),
171
+				"message": map[string]interface{}{
172
+					"appid": utils.GetWxAppID(s.org.OrgId),
173
+				},
174
+			}
175
+		}
176
+
177
+		// 微信用户信息
178
+		var err error
179
+		wxUser, err = s.wechartSignIn(gctx, code)
180
+		if err != nil {
181
+			return map[string]interface{}{
182
+				"code":  http.StatusInternalServerError,
183
+				"error": err,
184
+			}
185
+		}
186
+
187
+		if wxUser == nil {
188
+			return map[string]interface{}{
189
+				"code":  http.StatusInternalServerError,
190
+				"error": errors.New("请先关注公众号"),
191
+			}
192
+		}
193
+
194
+		openID = wxUser.OpenID
195
+	} else {
196
+		openID = token.ID
197
+	}
198
+
199
+	// 查询数据库是否存在已有映射
200
+	userMapList, err := s.dao.GetUserMappingByOpenID(openID)
201
+	if err != nil {
202
+		utils.LogError("校验人员失败: " + err.Error())
203
+		return map[string]interface{}{
204
+			"code":  http.StatusInternalServerError,
205
+			"error": errors.New("校验人员失败"),
206
+		}
207
+	}
208
+
209
+	var userMapping *model.TaUserMapping
210
+	for _, ump := range userMapList {
211
+		if openID == ump.Openid && models.ACCMAP_WECHAT == ump.AccountType {
212
+			userMapping = &ump
213
+		}
214
+	}
215
+
216
+	// 如果尚无人员映射信息, 代表人员初次使用本系统
217
+	if userMapping == nil {
218
+		// 如果没有微信用户信息, 代表产生了未知异常
219
+		if wxUser == nil || wxUser.UnionID == "" {
220
+			return map[string]interface{}{
221
+				"code":  http.StatusInternalServerError,
222
+				"error": errors.New("系统异常, 请清空缓存后重试"),
223
+			}
224
+		}
225
+
226
+		wxInfoJSON, err := json.Marshal(wxUser)
227
+		if err != nil {
228
+			utils.LogError("转换微信json信息失败: " + err.Error())
229
+			return map[string]interface{}{
230
+				"code":  http.StatusInternalServerError,
231
+				"error": errors.New("微信信息异常"),
232
+			}
233
+		}
234
+
235
+		userMapping = &model.TaUserMapping{
236
+			AccountType: models.ACCMAP_WECHAT,
237
+			Openid:      openID,
238
+			Uuid:        wxUser.UnionID,
239
+			AccountInfo: string(wxInfoJSON),
240
+		}
241
+	}
242
+
243
+	// 更新映射信息, 没有的话则插入
244
+	err = s.dao.UpdateUserMapping(userMapping)
245
+	if err != nil {
246
+		utils.LogError("保存用户映射信息失败: " + err.Error())
247
+		return map[string]interface{}{
248
+			"code":  http.StatusInternalServerError,
249
+			"error": errors.New("更新用户信息失败"),
250
+		}
251
+	}
252
+	s.ctx.Set("userMap", *userMapping)
253
+
254
+	if !s.needAuth(gctx) {
255
+		return nil
256
+	}
257
+
258
+	var cust *model.TaCustomer
259
+
260
+	// 如果只有映射, 但是没有人员信息
261
+	// 则新增人员
262
+	if userMapping.UserId == "" {
263
+		cust, err = s.saveNewCustomer(wxUser, userMapping)
264
+		if err != nil {
265
+			return map[string]interface{}{
266
+				"code":  http.StatusInternalServerError,
267
+				"error": err,
268
+			}
269
+		}
270
+	} else {
271
+		cust, err = s.customerdao.GetCustomerByID(userMapping.UserId)
272
+		if err != nil {
273
+			utils.LogError("查询用户信息失败: " + err.Error())
274
+			return map[string]interface{}{
275
+				"code":  http.StatusInternalServerError,
276
+				"error": err,
277
+			}
278
+		}
279
+	}
280
+	s.ctx.Set("customer", *cust)
281
+
282
+	if cust.UserId != "" {
283
+		if err := s.SetUserProfile(cust.UserId); err != nil {
284
+			return map[string]interface{}{
285
+				"code":  http.StatusInternalServerError,
286
+				"error": err,
287
+			}
288
+		}
289
+	}
290
+
291
+	return nil
292
+}
293
+
294
+// wechartSignIn 使用 code 微信登录
295
+func (s *SysServ) wechartSignIn(gctx *context.Context, code string) (*utils.WechatUser, error) {
296
+	if beego.BConfig.RunMode == "dev" {
297
+		return &utils.WechatUser{
298
+			OpenID: "OPENID",
299
+		}, nil
300
+	}
301
+
302
+	// 初始化微信配置
303
+	if err := s.initWechatClient(s.org.OrgId); err != nil {
304
+		utils.LogError("初始化微信客户端失败: " + err.Error())
305
+		return nil, errors.New("初始化微信客户端失败")
306
+	}
307
+
308
+	// 获取 微信信息
309
+	// 可能出现的情况是 openid 获取到了, 但是详情没有获取到
310
+	wxUserMap, err := utils.WxClientFor(s.org.OrgId).GetUserInfo(code)
311
+	if err != nil {
312
+		utils.LogError("获取微信信息失败: " + err.Error())
313
+
314
+		if wxUserMap == nil {
315
+			return nil, errors.New("获取微信信息失败")
316
+		}
317
+	}
318
+
319
+	return utils.MapToWechatUser(wxUserMap), nil
320
+}
321
+
322
+func (s *SysServ) getToken(gctx *context.Context) (*utils.JWTToken, error) {
323
+	tokenRaw := gctx.Input.Header(utils.TokenHeader)
324
+	if tokenRaw == "" {
325
+		return nil, nil
326
+	}
327
+
328
+	tokenEnStr := strings.Trim(strings.TrimLeft(tokenRaw, utils.TokenSchema), " ")
329
+	s.ctx.Set("token", tokenEnStr)
330
+
331
+	token, err := utils.PareseToken(tokenEnStr)
332
+	if err != nil {
333
+		utils.LogError("解析 Token 失败: " + err.Error())
334
+		return nil, errors.New("解析Token失败或已过期")
335
+	}
336
+
337
+	// 校验 token
338
+	tk, err := s.dao.GetToken(tokenEnStr)
339
+	if err != nil {
340
+		utils.LogError("查询 Token 失败: " + err.Error())
341
+		return nil, errors.New("校验Token失败或已过期")
342
+	}
343
+
344
+	if tk.Status == models.STATUS_DEL {
345
+		return nil, errors.New("超时 或者 Token 已过期")
346
+	}
347
+
348
+	return utils.MapToJWTToken(token), nil
349
+}
350
+
351
+// UpdateTokenExpire 更新 token 为过期
352
+// 如果发生错误, 此处选择忽略
353
+func (s *SysServ) UpdateTokenExpire(token string) {
354
+	if err := s.dao.UpdateTokenExpire(token); err != nil {
355
+		utils.LogError("更新 Token 过期失败: " + err.Error())
356
+	}
357
+}
358
+
359
+func (s *SysServ) needAuth(gctx *context.Context) bool {
360
+	route := gctx.Input.URL()
361
+	apiPrefix := beego.AppConfig.String("api::prefix")
362
+	guestAPI := beego.AppConfig.String("api::guest")
363
+
364
+	if strings.Index(route, apiPrefix+strings.Split(guestAPI, ":")[0]) > -1 {
365
+		return false
366
+	}
367
+
368
+	return true
369
+}
370
+
34
 // SetUserProfile 设置用户信息
371
 // SetUserProfile 设置用户信息
35
 func (s *SysServ) SetUserProfile(id string) error {
372
 func (s *SysServ) SetUserProfile(id string) error {
36
 	user, err := s.dao.GetPureUserInfo(id)
373
 	user, err := s.dao.GetPureUserInfo(id)
60
 	return nil
397
 	return nil
61
 }
398
 }
62
 
399
 
63
-// SetCustomer 设置客户信息
64
-func (s *SysServ) SetCustomer(id string) error {
65
-	cust, err := s.dao.GetCustomer(id)
66
-	if err != nil {
67
-		return utils.LogError("获取客户基本信息失败: " + err.Error())
68
-	}
69
-
70
-	s.ctx.Set("customer", *cust)
71
-	return nil
72
-}
73
-
74
-// SaveNewCustomer 新增用户
75
-func (s *SysServ) SaveNewCustomer(wxInfo map[string]interface{}, userMap *model.TaUserMapping) (*model.TaCustomer, error) {
76
-	// 微信相关字段
77
-	nickyName := wxInfo["nickname"].(string)
78
-	sex := wxInfo["sex"].(float64)
79
-	headimgurl := wxInfo["headimgurl"].(string)
80
-
81
-	org := s.ctx.Get("org").(model.SysOrg)
82
-
400
+// saveNewCustomer 新增用户
401
+func (s *SysServ) saveNewCustomer(wxUser *utils.WechatUser, userMap *model.TaUserMapping) (*model.TaCustomer, error) {
83
 	cust := model.TaCustomer{
402
 	cust := model.TaCustomer{
84
-		CustomerName: nickyName,
85
-		Name:         nickyName,
86
-		Sex:          int(sex),
87
-		Headimgurl:   headimgurl,
88
-		OrgId:        org.OrgId,
403
+		CustomerName: wxUser.NickName,
404
+		Name:         wxUser.NickName,
405
+		Sex:          int(wxUser.Sex),
406
+		Headimgurl:   wxUser.HeadImgURL,
407
+		OrgId:        s.org.OrgId,
89
 	}
408
 	}
90
 
409
 
91
 	if err := s.customerdao.SaveCustomer(&cust); err != nil {
410
 	if err := s.customerdao.SaveCustomer(&cust); err != nil {
130
 
449
 
131
 	accountRaw, err := json.Marshal(user)
450
 	accountRaw, err := json.Marshal(user)
132
 	if err != nil {
451
 	if err != nil {
133
-		beego.Error(err)
134
 		return nil, err
452
 		return nil, err
135
 	}
453
 	}
136
 	account := string(accountRaw)
454
 	account := string(accountRaw)
146
 	userMapList, err := s.dao.GetUserMappingByOpenID(openID)
464
 	userMapList, err := s.dao.GetUserMappingByOpenID(openID)
147
 
465
 
148
 	if err != nil {
466
 	if err != nil {
149
-		beego.Error(err)
150
 		return nil, err
467
 		return nil, err
151
 	}
468
 	}
152
 
469
 
172
 	return &userMapping, nil
489
 	return &userMapping, nil
173
 }
490
 }
174
 
491
 
175
-// GetWeChatConfig 获取微信配置
176
-func (s SysServ) GetWeChatConfig(org string) (map[string]string, error) {
177
-	conf, err := s.dao.GetWeChatConfig(org)
492
+// initWechatClient 初始化微信客户端
493
+func (s SysServ) initWechatClient(orgID string) error {
494
+	conf, err := s.dao.GetWeChatConfig(orgID)
178
 	if err != nil {
495
 	if err != nil {
179
-		return nil, err
496
+		utils.LogError("获取微信配置失败: " + err.Error())
497
+		return errors.New("获取微信配置失败")
180
 	}
498
 	}
181
 
499
 
182
-	if conf.ConfId == "" {
183
-		return nil, errors.New("未找到微信配置")
500
+	if conf == nil || conf.ConfId == "" {
501
+		return errors.New("未找到微信配置")
184
 	}
502
 	}
185
 
503
 
186
-	return map[string]string{
504
+	cert := map[string]string{
187
 		"appid":  conf.Appid,
505
 		"appid":  conf.Appid,
188
 		"secret": conf.Secret,
506
 		"secret": conf.Secret,
189
 		"token":  conf.Token,
507
 		"token":  conf.Token,
190
 		"aeskey": conf.Aeskey,
508
 		"aeskey": conf.Aeskey,
191
 		"wxid":   conf.Wxid,
509
 		"wxid":   conf.Wxid,
192
-	}, nil
510
+	}
511
+
512
+	utils.WxClientSingleton(orgID, cert)
513
+	return nil
193
 }
514
 }
194
 
515
 
195
 // SetOrgByID 获取组织
516
 // SetOrgByID 获取组织
203
 	}
524
 	}
204
 
525
 
205
 	s.ctx.Set("org", *org)
526
 	s.ctx.Set("org", *org)
527
+	s.org = *org
206
 
528
 
207
 	return nil
529
 	return nil
208
 }
530
 }

+ 68
- 0
utils/excel.go 查看文件

1
+package utils
2
+
3
+import (
4
+	"io"
5
+	"reflect"
6
+
7
+	"github.com/tealeg/xlsx"
8
+)
9
+
10
+// TinyXLSXEngine 简版 xlsx 处理器
11
+type TinyXLSXEngine struct {
12
+	xlsxFile *xlsx.File
13
+	toSlice  func(interface{}) []interface{}
14
+}
15
+
16
+// NewTinyXLSXEngine init
17
+func NewTinyXLSXEngine() *TinyXLSXEngine {
18
+	return &TinyXLSXEngine{}
19
+}
20
+
21
+// SetTransFunc 设置转换函数
22
+func (t *TinyXLSXEngine) SetTransFunc(f func(interface{}) []interface{}) *TinyXLSXEngine {
23
+	t.toSlice = f
24
+	return t
25
+}
26
+
27
+// SetData 设置数据
28
+func (t *TinyXLSXEngine) SetData(data interface{}) *TinyXLSXEngine {
29
+	file := xlsx.NewFile()
30
+	t.xlsxFile = file
31
+
32
+	sheet, err := file.AddSheet("sheet1")
33
+	if err != nil {
34
+		LogError("创建 xlsx sheet 失败: " + err.Error())
35
+		return t
36
+	}
37
+
38
+	if t.toSlice == nil {
39
+		t.toSlice = func(dt interface{}) []interface{} {
40
+			return nil
41
+		}
42
+	}
43
+
44
+	v := reflect.ValueOf(data)
45
+	if v.Kind() == reflect.Ptr {
46
+		v.Elem()
47
+	}
48
+
49
+	if v.Kind() == reflect.Slice && v.IsValid() {
50
+		l := v.Len()
51
+		for i := 0; i < l; i++ {
52
+			rowData := v.Index(i).Interface()
53
+			cols := t.toSlice(rowData)
54
+
55
+			row := sheet.AddRow()
56
+			for _, col := range cols {
57
+				row.AddCell().SetValue(col)
58
+			}
59
+		}
60
+	}
61
+
62
+	return t
63
+}
64
+
65
+// Write 写数据
66
+func (t *TinyXLSXEngine) Write(w io.Writer) error {
67
+	return t.xlsxFile.Write(w)
68
+}

+ 48
- 0
utils/jwt.go 查看文件

2
 
2
 
3
 import (
3
 import (
4
 	"errors"
4
 	"errors"
5
+	"time"
5
 
6
 
6
 	jwt "github.com/dgrijalva/jwt-go"
7
 	jwt "github.com/dgrijalva/jwt-go"
7
 )
8
 )
8
 
9
 
10
+const (
11
+	TokenHeader = "Authorization"
12
+	TokenSchema = "Bearer"
13
+)
14
+
9
 var tokenSignedKey = []byte(`Yansen is so handsome!`)
15
 var tokenSignedKey = []byte(`Yansen is so handsome!`)
10
 
16
 
11
 // CreateToken 获取token
17
 // CreateToken 获取token
32
 
38
 
33
 	return nil, errors.New("Token 解析 未知错误")
39
 	return nil, errors.New("Token 解析 未知错误")
34
 }
40
 }
41
+
42
+// JWTToken token 内容
43
+type JWTToken struct {
44
+	Guest    bool
45
+	ID       string
46
+	Password string
47
+	Expire   time.Time
48
+}
49
+
50
+// ToMap 转 map
51
+func (t *JWTToken) ToMap() map[string]interface{} {
52
+	return map[string]interface{}{
53
+		"guest":    t.Guest,
54
+		"user":     t.ID,
55
+		"password": t.Password,
56
+		"exp":      t.Expire.Format("2006-01-02 15:04:05"),
57
+	}
58
+}
59
+
60
+// MapToJWTToken map 映射 Token
61
+func MapToJWTToken(data map[string]interface{}) *JWTToken {
62
+	token := JWTToken{}
63
+
64
+	if data["guest"] != nil {
65
+		token.Guest = data["guest"].(bool)
66
+	}
67
+
68
+	if data["user"] != nil {
69
+		token.ID = data["user"].(string)
70
+	}
71
+
72
+	if data["password"] != nil {
73
+		token.Password = data["password"].(string)
74
+	}
75
+
76
+	if data["exp"] != nil {
77
+		exp, _ := time.Parse("2006-01-02 15:04:05", data["exp"].(string))
78
+		token.Expire = exp
79
+	}
80
+
81
+	return &token
82
+}

+ 56
- 0
utils/wechat.go 查看文件

6
 
6
 
7
 var wxClients map[string]*wx.Client
7
 var wxClients map[string]*wx.Client
8
 
8
 
9
+type WechatUser struct {
10
+	OpenID     string  `json:"openid"`
11
+	NickName   string  `json:"nickname"`
12
+	Sex        float64 `json:"sex"`
13
+	Province   string  `json:"province"`
14
+	City       string  `json:"city"`
15
+	Country    string  `json:"country"`
16
+	HeadImgURL string  `json:"headimgurl"`
17
+	UnionID    string  `json:"unionid"`
18
+}
19
+
9
 // WxClientSingleton 初始化
20
 // WxClientSingleton 初始化
10
 func WxClientSingleton(org string, cert map[string]string) {
21
 func WxClientSingleton(org string, cert map[string]string) {
11
 	if wxClients == nil {
22
 	if wxClients == nil {
32
 func init() {
43
 func init() {
33
 	wx.SetLogInst(defaultLogger)
44
 	wx.SetLogInst(defaultLogger)
34
 }
45
 }
46
+
47
+// MapToWechatUser 映射微信人员
48
+func MapToWechatUser(data map[string]interface{}) *WechatUser {
49
+	subscribe, has := data["subscribe"]
50
+	if has {
51
+		if subscribe == nil {
52
+			return nil
53
+		}
54
+
55
+		sub := subscribe.(float64)
56
+		if sub == 0 {
57
+			return nil
58
+		}
59
+	}
60
+
61
+	user := WechatUser{
62
+		OpenID: data["openid"].(string),
63
+	}
64
+
65
+	if data["sex"] != nil {
66
+		user.Sex = data["sex"].(float64)
67
+	}
68
+
69
+	if data["province"] != nil {
70
+		user.Province = data["province"].(string)
71
+	}
72
+
73
+	if data["city"] != nil {
74
+		user.City = data["city"].(string)
75
+	}
76
+
77
+	if data["country"] != nil {
78
+		user.Country = data["country"].(string)
79
+	}
80
+
81
+	if data["headimgurl"] != nil {
82
+		user.HeadImgURL = data["headimgurl"].(string)
83
+	}
84
+
85
+	if data["unionid"] != nil {
86
+		user.UnionID = data["unionid"].(string)
87
+	}
88
+
89
+	return &user
90
+}