package service import ( "encoding/json" "errors" "net/http" "spaceofcheng/services/models" "spaceofcheng/services/models/model" "spaceofcheng/services/utils" "strings" "time" "github.com/astaxie/beego" "github.com/astaxie/beego/context" ) const ( PAGENUM = 10 ) // SysServ 系统处理 type SysServ struct { org model.SysOrg ctx *utils.Context } // NewSysServ 初始化 func NewSysServ(ctx *utils.Context) *SysServ { return &SysServ{ ctx: ctx, } } // AuthAndInitCtx 鉴权 // gctx 是 beego 框架中的 Context func (s *SysServ) AuthAndInitCtx(gctx *context.Context) map[string]interface{} { // 确认机构 orgID := gctx.Input.Query(":org") if orgID == "" { return map[string]interface{}{ "code": http.StatusBadRequest, "error": errors.New("接口地址访问不正确"), } } if err := s.SetOrgByID(orgID); err != nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } // 客户端类型 // 通过 UA 判断 clientType := utils.GetClientType(gctx.Request) // pc 管理端 if clientType == utils.ClientAdmin { return s.authPCAdmin(gctx) } // 小程序 端 if strings.Index(gctx.Input.URI(), "/wechat/mini") > -1 { return s.authMini(gctx) } // wechat 端 if clientType == utils.ClientWechat { return s.authWechat(gctx) } // if clientType == utils.ClientMini { // return s.authMini(gctx) // } return map[string]interface{}{ "code": http.StatusBadRequest, "error": errors.New("暂无该客户端的 API"), } } // NewToken 设置 TOKEN // 15 分钟后过期 func (s *SysServ) NewToken(batch string) string { var token *utils.JWTToken exp := time.Now().Local().Add(15 * time.Second) if s.ctx.Get("userMap") != nil { userMap := s.ctx.Get("userMap").(model.TaUserMapping) token = &utils.JWTToken{ Guest: false, ID: userMap.Openid, Expire: exp, BatchNo: batch, } } else if s.ctx.Get("user") != nil { user := s.ctx.Get("user").(model.SysUser) token = &utils.JWTToken{ Guest: false, ID: user.UserId, Expire: exp, BatchNo: batch, } } else { token = &utils.JWTToken{ Guest: true, Expire: exp, } } tokenEncodeStr, err := utils.CreateToken(token.ToMap()) if err != nil { utils.LogError("系统生成 Token 失败: " + err.Error()) return "" } // 入库 if !token.Guest { if err := models.InsertToken(tokenEncodeStr, token.ID, batch, exp); err != nil { utils.LogError("入库 Token 失败: " + err.Error()) return tokenEncodeStr } } return tokenEncodeStr } // authPCAdmin // 管理端 API 校验 func (s *SysServ) authPCAdmin(gctx *context.Context) map[string]interface{} { if !s.needAuth(gctx) { return nil } // 获取 token token, err := s.getToken(gctx) if err != nil { // token 报错一律视为需要重新登录 return map[string]interface{}{ "code": http.StatusUnauthorized, "error": err, } } if token.ID == "" || token.Guest == true { return map[string]interface{}{ "code": http.StatusUnauthorized, "error": errors.New("用户未登录"), } } if err := s.SetUserProfile(token.ID); err != nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } return nil } func (s *SysServ) authWechat(gctx *context.Context) map[string]interface{} { var wxUser *utils.WechatUser var openID string if beego.BConfig.RunMode == "dev" { openID = "ouHcHt8oyP4jofR5cV2CZYXYgqkQ" } else { // 初始化微信配置 if err := s.initWechatClient(s.org.OrgId); err != nil { utils.LogError("初始化微信服务失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("初始化微信服务失败"), } } // 微信 code code := gctx.Input.Query("code") // 获取 token token, err := s.getToken(gctx) if err != nil { tokenStr := s.ctx.Get("token").(string) if tokenStr != "" { // token 报错一律视为需要重新登录 return map[string]interface{}{ "code": http.StatusUnauthorized, "error": err, "message": map[string]interface{}{ "appid": utils.GetWxAppID(s.org.OrgId), }, } } } // 未登录 或 未验证 if token == nil || token.ID == "" { if code == "" { return map[string]interface{}{ "code": http.StatusUnauthorized, "error": errors.New("请授权微信用户登录"), "message": map[string]interface{}{ "appid": utils.GetWxAppID(s.org.OrgId), }, } } // 微信用户信息 var err error wxUser, err = s.wechartSignIn(gctx, code) if err != nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } if wxUser == nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("请先关注公众号"), } } utils.LogError("获取到微信人员: ", wxUser) openID = wxUser.OpenID } else { openID = token.ID } } // 查询数据库是否存在已有映射 userMapList, err := models.GetUserMappingByOpenID(openID) if err != nil { utils.LogError("校验人员失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("校验人员失败"), } } var userMapping *model.TaUserMapping for _, ump := range userMapList { if openID == ump.Openid && models.ACCMAP_WECHAT == ump.AccountType { userMapping = &ump } } // 如果尚无人员映射信息, 代表人员初次使用本系统 if userMapping == nil { // 如果没有微信用户信息, 代表产生了未知异常 if wxUser == nil || wxUser.OpenID == "" { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("系统异常, 请清空缓存后重试"), } } wxInfoJSON, err := json.Marshal(wxUser) if err != nil { utils.LogError("转换微信json信息失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("微信信息异常"), } } userMapping = &model.TaUserMapping{ AccountType: models.ACCMAP_WECHAT, Openid: openID, Uuid: wxUser.UnionID, AccountInfo: string(wxInfoJSON), } } // 防止JSON解析失败 if userMapping.AccountInfo == "" { userMapping.AccountInfo = "{}" } // 更新映射信息, 没有的话则插入 err = models.EditUserMapping(userMapping) if err != nil { utils.LogError("保存用户映射信息失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("更新用户信息失败"), } } s.ctx.Set("userMap", *userMapping) // if !s.needAuth(gctx) { // return nil // } var cust *model.TaCustomer // 如果只有映射, 但是没有人员信息 // 则新增人员 if userMapping.UserId == "" { cust, err = s.saveNewCustomer(wxUser, userMapping) if err != nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } } else { cust, err = models.GetCustomerByID(userMapping.UserId) if err != nil { utils.LogError("查询用户信息失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } } s.ctx.Set("customer", *cust) if cust.UserId != "" { if err := s.SetUserProfile(cust.UserId); err != nil { return map[string]interface{}{ "code": http.StatusInternalServerError, "error": err, } } } return nil } // 小程序端暂时无人员或者其他业务要求 func (s *SysServ) authMini(gctx *context.Context) map[string]interface{} { if err := s.initMiniClient(s.org.OrgId); err != nil { utils.LogError("初始化小程序服务失败: " + err.Error()) return map[string]interface{}{ "code": http.StatusInternalServerError, "error": errors.New("初始化小程序服务失败"), } } return nil } // wechartSignIn 使用 code 微信登录 func (s *SysServ) wechartSignIn(gctx *context.Context, code string) (*utils.WechatUser, error) { // 获取 微信信息 // 可能出现的情况是 openid 获取到了, 但是详情没有获取到 wxUserMap, err := utils.WxClientFor(s.org.OrgId).GetUserInfo(code) if err != nil { utils.LogError("获取微信信息失败: " + err.Error()) if wxUserMap == nil { return nil, errors.New("获取微信信息失败") } } return utils.MapToWechatUser(wxUserMap), nil } func (s *SysServ) getToken(gctx *context.Context) (*utils.JWTToken, error) { tokenEnStr := gctx.Input.Query("token") if tokenEnStr == "" { tokenRaw := gctx.Input.Header(utils.TokenHeader) if tokenRaw == "" { return new(utils.JWTToken), nil } tokenEnStr = strings.Trim(strings.TrimLeft(tokenRaw, utils.TokenSchema), " ") } else { tokenEnStr = strings.Trim(strings.TrimLeft(tokenEnStr, utils.TokenSchema), " ") } s.ctx.Set("token", tokenEnStr) token, err := utils.PareseToken(tokenEnStr) if err != nil { utils.LogError("解析 Token 失败: " + err.Error()) return nil, errors.New("解析Token失败或已过期") } // 校验 token tk, err := models.GetToken(tokenEnStr) if err != nil { utils.LogError("查询 Token 失败: " + err.Error()) return nil, errors.New("校验Token失败或已过期") } if tk.Status == models.STATUS_DEL { return nil, errors.New("超时 或者 Token 已过期") } s.ctx.Set("token-batch", tk.BatchNo) return utils.MapToJWTToken(token), nil } // UpdateTokenExpire 更新 token 为过期 // 如果发生错误, 此处选择忽略 func (s *SysServ) UpdateTokenExpire(token, uid string) { if err := models.UpdateTokenExpire(token, uid); err != nil { utils.LogError("更新 Token 过期失败: " + err.Error()) } } func (s *SysServ) needAuth(gctx *context.Context) bool { route := gctx.Input.URL() apiPrefix := beego.AppConfig.String("api::prefix") guestAPI := beego.AppConfig.String("api::guest") if strings.Index(route, apiPrefix+strings.Split(guestAPI, ":")[0]) > -1 { return false } return true } // SetUserProfile 设置用户信息 func (s *SysServ) SetUserProfile(id string) error { user, err := models.GetPureUserInfo(id) if err != nil { return utils.LogError("获取用户基本信息失败: " + err.Error()) } s.ctx.Set("user", *user) cases, err := models.GetUserCase(id) if err != nil { return utils.LogError("获取用户案场信息失败: " + err.Error()) } s.ctx.Set("cases", cases) found := false for _, cs := range cases { if cs.IsBelong == models.BOOL_TRUE { found = true s.ctx.Set("currentCase", cs) } } if !found { utils.LogError("用户没有设置默认案场") } return nil } // saveNewCustomer 新增用户 func (s *SysServ) saveNewCustomer(wxUser *utils.WechatUser, userMap *model.TaUserMapping) (*model.TaCustomer, error) { cust := model.TaCustomer{ CustomerName: wxUser.NickName, Name: wxUser.NickName, Sex: int(wxUser.Sex), Headimgurl: wxUser.HeadImgURL, OrgId: s.org.OrgId, } if err := models.SaveCustomer(&cust); err != nil { utils.LogError("更新客户信息失败: " + err.Error()) return nil, errors.New("更新客户信息失败") } account := new(model.TaCustomerAccount) account.CustomerId = cust.CustomerId account.CustomerName = cust.CustomerName account.OrgId = cust.OrgId account.Amount = "0" account.Points = "0" account.PayedAmount = "0" account.PayedPoints = "0" if err := models.SaveAccount(account); err != nil { utils.LogError("插入账户信息失败: " + err.Error()) return nil, errors.New("更新客户信息失败") } // 更新映射表信息 userMap.UserId = cust.CustomerId if err := models.UpdateUserMapping(userMap, []string{"user_id"}); err != nil { utils.LogError("更新用户映射信息失败:" + err.Error()) return nil, errors.New("映射用户信息失败") } return &cust, nil } // initWechatClient 初始化微信客户端 func (s *SysServ) initWechatClient(orgID string) error { cert, err := models.GetWeChatConfig(orgID, models.WECHAT_WX) if err != nil { utils.LogError("获取微信配置失败: " + err.Error()) return errors.New("获取微信配置失败") } if cert == nil { return errors.New("未找到微信配置") } utils.WxClientSingleton(orgID, cert) return nil } func (s *SysServ) initMiniClient(orgID string) error { cert, err := models.GetWeChatConfig(orgID, models.WECHAT_MINI) if err != nil { utils.LogError("获取小程序配置失败: " + err.Error()) return errors.New("获取小程序配置失败") } if cert == nil { return errors.New("未找到小程序配置") } utils.MiniClientSingleton(orgID, cert) return nil } // SetOrgByID 获取组织 func (s *SysServ) SetOrgByID(orgID string) error { orgID = utils.DecodeBase64NoTail(orgID) org, err := models.GetOrg(orgID) if err != nil { utils.LogError("获取组织信息失败: " + err.Error()) return errors.New("获取组织信息失败") } s.ctx.Set("org", *org) s.org = *org return nil }