Procházet zdrojové kódy

add:添加登录模块,研报模块,品种模块,微信模块的接口

zqbao před 9 měsíci
rodič
revize
b79dcf5dae

+ 17 - 6
controllers/base_auth.go

@@ -51,18 +51,25 @@ func (c *BaseAuthController) Prepare() {
 				return
 			}
 
-			account := utils.MD5(strconv.Itoa(session.UserId))
-			if !utils.CheckToken(account, token) {
-				c.JSON(models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"})
+			if time.Now().After(session.ExpireTime) {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常"})
 				c.StopRun()
 				return
 			}
-			if time.Now().After(session.ExpiredTime) {
-				c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常"})
+			var user *models.User
+			if session.OpenId != "" {
+				tmpWxUser, tmpErr := models.GetUserByOpenId(session.OpenId)
+				user = tmpWxUser
+				err = tmpErr
+			} else if session.UserId > 0 {
+				tmpWxUser, tmpErr := models.GetUserById(session.UserId)
+				user = tmpWxUser
+				err = tmpErr
+			} else {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "数据异常!", ErrMsg: "sesson is empty "})
 				c.StopRun()
 				return
 			}
-			user, err := models.GetUserById(session.UserId)
 			if err != nil {
 				if err == orm.ErrNoRows {
 					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "获取sysUser信息失败: " + strconv.Itoa(session.UserId)})
@@ -80,6 +87,10 @@ func (c *BaseAuthController) Prepare() {
 			}
 			c.User = user
 			c.Session = session
+		} else {
+			c.JSON(models.BaseResponse{Ret: 408, Msg: "请求异常,请联系客服!", ErrMsg: "POST之外的请求,暂不支持"})
+			c.StopRun()
+			return
 		}
 	}
 }

+ 57 - 0
controllers/base_common.go

@@ -1,7 +1,11 @@
 package controllers
 
 import (
+	"encoding/json"
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/services"
 	"eta/eta_mini_api/utils"
+	"net/http"
 	"net/url"
 
 	"github.com/beego/beego/v2/server/web"
@@ -23,3 +27,56 @@ func (c *BaseCommonController) Prepare() {
 	ip := c.Ctx.Input.IP()
 	utils.ApiLog.Info("uri:%s, requestBody:%s, ip:%s", c.Ctx.Input.URI(), requestBody, ip)
 }
+
+func (c *BaseCommonController) ServeJSON(encoding ...bool) {
+	var (
+		hasIndent   = false
+		hasEncoding = false
+	)
+	if web.BConfig.RunMode == web.PROD {
+		hasIndent = false
+	}
+	if len(encoding) > 0 && encoding[0] == true {
+		hasEncoding = true
+	}
+	if c.Data["json"] == nil {
+		go utils.SendEmail(utils.APPNAME+" "+utils.RunMode+"异常提醒", "接口:"+"URI:"+c.Ctx.Input.URI()+";无返回值", utils.EmailSendToUsers)
+		return
+	}
+	baseRes := c.Data["json"].(*models.BaseResponse)
+	if baseRes != nil && !baseRes.Success && baseRes.IsSendEmail {
+		go utils.SendEmail(utils.APPNAME+" "+utils.RunMode+" 失败提醒", "URI:"+c.Ctx.Input.URI()+" ErrMsg:"+baseRes.ErrMsg+";Msg"+baseRes.Msg, utils.EmailSendToUsers)
+	}
+	c.JSON(c.Data["json"], hasIndent, hasEncoding)
+}
+
+func (c *BaseCommonController) JSON(data interface{}, hasIndent bool, coding bool) error {
+	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = json.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = json.Marshal(data)
+	}
+	if err != nil {
+		http.Error(c.Ctx.Output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	ip := c.Ctx.Input.IP()
+	requestBody, _ := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+	utils.ApiLog.Info("请求地址:", c.Ctx.Input.URI(), "Authorization:", c.Ctx.Input.Header("Authorization"), "RequestBody:", requestBody, "ResponseBody", string(content), "IP:", ip)
+
+	if coding {
+		content = []byte(utils.StringsToJSON(string(content)))
+	}
+
+	// 数据加密
+	if services.CheckEncryption() {
+		content = utils.DesBase64Encrypt(content, utils.Key)
+		// get请求时,不加双引号就获取不到数据,不知道什么原因,所以还是在前后加上双引号吧
+		content = []byte(`"` + string(content) + `"`)
+	}
+
+	return c.Ctx.Output.Body(content)
+}

+ 43 - 0
controllers/chart_permission.go

@@ -0,0 +1,43 @@
+package controllers
+
+import (
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/services"
+)
+
+type ChartPermissionController struct {
+	BaseAuthController
+}
+
+// List
+// @Title 系统品种列表
+// @Description 系统品种列表
+// @Param   UserId   query   int  true       "角色ID"
+// @Success 200 {object} models.LoginResp
+// @router /list [get]
+func (this *ChartPermissionController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	id, _ := this.GetInt("chartPermissonId", 0)
+
+	var items []*services.ChartPermission
+	var err error
+	if id == 0 {
+		items, err = services.GetChartPermissionList()
+	} else if id > 0 {
+		items, err = services.GetChartPermissionSecondList(id)
+	}
+	if err != nil || items == nil {
+		br.Msg = "权限列表获取失败"
+		br.ErrMsg = "权限列表获取失败,系统错误,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Data = items
+	br.Msg = "列表获取成功"
+	br.Success = true
+}

+ 71 - 0
controllers/report.go

@@ -0,0 +1,71 @@
+package controllers
+
+import (
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/services"
+)
+
+type ReportController struct {
+	BaseAuthController
+}
+
+// @Title 日评详情
+// @Description 日评详情接口
+// @Param   ReportId   query   int  true       "报告id"
+// @Success 200 {object} models.ReportDetailResp
+// @router /detail [get]
+func (this *ReportController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	reportId, _ := this.GetInt("ReportId")
+	if reportId <= 0 {
+		br.Msg = "报告不存在"
+		return
+	}
+
+	result, err := services.GetReportDetail(reportId)
+	if err != nil {
+		br.Msg = "查询报告详情失败"
+		br.ErrMsg = "查询报告失败,系统异常,Err:" + err.Error()
+		return
+	}
+
+	if result.Ret != 200 {
+		br.Msg = "查询报告详情失败"
+		br.ErrMsg = result.ErrMsg
+		return
+	}
+
+	br.Msg = "查询成功"
+	br.Success = true
+	br.Ret = 200
+	br.Data = result
+}
+
+// @Title 研报列表
+// @Description 研报列表
+// @Param   ReportId   query   int  true       "报告id"
+// @Success 200 {object} models.ReportDetailResp
+// @router /list [get]
+func (this *ReportController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	reports, err := services.GetReportList()
+	if err != nil {
+		br.Msg = "研报列表查询失败"
+		br.ErrMsg = "研报列表查询失败,系统异常,Err:" + err.Error()
+		return
+	}
+
+	br.Data = reports
+	br.Msg = "查询成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 158 - 30
controllers/user.go

@@ -4,9 +4,13 @@ import (
 	"encoding/json"
 	"eta/eta_mini_api/models"
 	"eta/eta_mini_api/models/request"
+	"eta/eta_mini_api/models/response"
 	"eta/eta_mini_api/services"
+	"eta/eta_mini_api/services/wx_app"
 	"eta/eta_mini_api/utils"
 	"fmt"
+	"strconv"
+	"time"
 )
 
 type UserController struct {
@@ -21,6 +25,9 @@ type UserController struct {
 func (this *UserController) Login() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
+		if err := recover(); err != nil {
+			fmt.Println(err)
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
@@ -32,46 +39,133 @@ func (this *UserController) Login() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+	if req.Code == "" {
+		br.Msg = "授权码不存在"
+		return
+	}
+
+	userInfo, err := wx_app.GetSession(req.Code)
+	if err != nil {
+		br.Msg = "登录失败,请重新尝试"
+		br.ErrMsg = "用户信息获取失败,系统错误,Err:" + err.Error()
+		return
+	}
+
 	switch req.LoginType {
 	case 1:
-		if req.Mobile == "" {
+		if req.Phone == "" {
 			br.Msg = "请输入手机号"
+			br.ErrMsg = "请输入手机号"
 			return
 		}
-		if req.VerifyCode == "" {
+		if req.SmsCode == "" {
 			br.Msg = "请输入验证码"
+			br.ErrMsg = "请输入验证码"
 			return
 		}
-		code := utils.GetRandDigit(6)
-		ok := services.SendSmsCode(req.Mobile, code)
-		if !ok {
-			br.Msg = "短信验证码发送失败"
+		phone := req.AreaCode + req.Phone
+		item, err := models.GetMsgCode(phone, req.SmsCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误,请重新输入"
+				br.ErrMsg = "校验验证码失败,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "验证码错误,请重新输入"
+				br.ErrMsg = "校验验证码失败,Err:" + err.Error()
+				return
+			}
+		}
+		if item == nil {
+			br.Msg = "验证码错误,请重新输入"
 			return
 		}
 
 	case 2:
 		if req.Email == "" {
-			br.Msg = "请输入邮箱"
+			br.Msg = "请输入手机号"
+			br.ErrMsg = "请输入手机号"
 			return
 		}
-		if req.VerifyCode == "" {
+		if req.SmsCode == "" {
 			br.Msg = "请输入验证码"
+			br.ErrMsg = "请输入验证码"
+			return
+		}
+		item, err := models.GetMsgCode(req.Email, req.SmsCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误,请重新输入"
+				br.ErrMsg = "校验验证码失败,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "验证码错误,请重新输入"
+				br.ErrMsg = "校验验证码失败,Err:" + err.Error()
+				return
+			}
+		}
+		if item == nil {
+			br.Msg = "验证码错误,请重新输入"
 			return
 		}
+	}
+	user, errMsg, err := services.BindWxUser(userInfo.UnionID, userInfo.OpenID, req.Phone, req.Email, req.AreaCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "登录失败,系统处理中,请稍后重试"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "登录失败:" + err.Error()
+		return
+	}
+	userId := user.UserId
 
+	var token string
+	tokenItem, err := models.GetTokenByOpenId(userInfo.OpenID)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "登录失败"
+		br.ErrMsg = "登录失败,获取token失败:" + err.Error()
+		return
+	}
+	if tokenItem == nil || (err != nil && err.Error() == utils.ErrNoRow()) {
+		timeUnix := time.Now().Unix()
+		timeUnixStr := strconv.FormatInt(timeUnix, 10)
+		token := utils.MD5(strconv.Itoa(userId)) + utils.MD5(timeUnixStr)
+		//新增session
+		{
+			session := new(models.WxSession)
+			session.OpenId = userInfo.OpenID
+			session.UserId = userId
+			session.CreateTime = time.Now()
+			session.LastUpdateTime = time.Now()
+			session.ExpireTime = time.Now().AddDate(0, 3, 0)
+			session.AccessToken = token
+			err = session.AddWxSession()
+			if err != nil {
+				br.Msg = "登录失败"
+				br.ErrMsg = "登录失败,新增用户session信息失败:" + err.Error()
+				return
+			}
+		}
+	} else {
+		token = tokenItem.AccessToken
+		_ = models.UpdateSession(tokenItem.WxSessionId, userId, time.Now().AddDate(0, 1, 0))
 	}
+	resp := new(response.LoginResp)
+	resp.UserId = userId
+	resp.Authorization = token
 
+	br.Data = resp
 	br.Msg = "登录成功"
 	br.Success = true
 	br.Ret = 200
 }
 
-// GetVerifyCode
 // @Title 获取短信/邮箱验证码
-// @Description 获取短信/邮箱验证码
-// @Param	request	body VerifyCodeReq true "type json string"
-// @Success 200 Ret=200 获取成功
-// @Router /getSmsCode [get]
+// @Description 用户登录
+// @Param	request	body models.LoginReq true "type json string"
+// @Success 200 {object} models.LoginResp
+// @router /getVerifyCode [post]
 func (this *UserController) GetVerifyCode() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
@@ -91,24 +185,44 @@ func (this *UserController) GetVerifyCode() {
 		br.ErrMsg = fmt.Sprintf("验证方式异常<%d>", req.VerifyType)
 	}
 
+	code := utils.GetRandDigit(6)
 	switch req.VerifyType {
 	case 1:
-		if req.TelAreaCode == "" {
+		if req.AreaCode == "" {
 			br.Msg = "请选择区号"
 			return
 		}
-		if req.Mobile == "" {
+		if req.Phone == "" {
 			br.Msg = "请输入手机号"
 			return
 		}
-		if req.TelAreaCode == utils.TelAreaCodeHome && !utils.ValidateMobileFormatat(req.Mobile) {
+		if req.AreaCode == utils.TelAreaCodeHome && !utils.ValidateMobileFormatat(req.Phone) {
 			br.Msg = "您的手机号输入有误, 请检查"
 			return
 		}
-		ok := services.SendSmsCode(req.Mobile, req.TelAreaCode)
+		var ok bool
+		if req.AreaCode == "86" {
+			ok = services.SendSmsCode(req.Phone, code)
+		} else {
+			ok = services.SendSmsCodeGj(req.Phone, code, req.AreaCode)
+		}
 		if !ok {
-			br.ErrMsg = "短信验证码发送错误" + err.Error()
+			br.ErrMsg = "短信验证码发送失败"
 			return
+		} else {
+			item := new(models.MsgCode)
+			item.OpenId = ""
+			item.Code = code
+			item.Mobile = req.AreaCode + req.Phone
+			item.ExpiredIn = time.Now().Add(15 * time.Minute).Unix()
+			item.CreateTime = time.Now()
+			err = item.Insert()
+			if err != nil {
+				br.Msg = "发送失败"
+				br.ErrMsg = "发送失败,Err:" + err.Error()
+				return
+			}
+			br.Msg = "发送成功"
 		}
 	case 2:
 		if req.Email == "" {
@@ -118,21 +232,35 @@ func (this *UserController) GetVerifyCode() {
 			br.Msg = "您的邮箱格式输入有误, 请检查"
 			return
 		}
-		// err := services.SendEmailCode(req.Email)
+		date := time.Now()
+		content := "尊敬的用户:</br>本次请求的验证码为:" + code + "(为了保障您账号的安全性,请在15分钟内完成验证。)</br>东吴期货研究团队 </br>" + fmt.Sprintf("%d年%02d月%02d日", date.Year(), date.Month(), date.Day())
+		title := "东吴期货登录验证"
+		result, err := utils.SendEmailByDw(title, content, req.Email)
+		if err != nil {
+			br.Msg = "发送失败"
+			br.ErrMsg = "发送失败,Err:" + err.Error()
+			return
+		}
+		if result {
+			item := new(models.MsgCode)
+			item.OpenId = ""
+			item.Code = code
+			item.Mobile = req.Email
+			item.ExpiredIn = time.Now().Add(15 * time.Minute).Unix()
+			item.CreateTime = time.Now()
+			err = item.Insert()
+			if err != nil {
+				br.Msg = "发送失败"
+				br.ErrMsg = "发送失败,Err:" + err.Error()
+				return
+			}
+			br.Msg = "发送成功"
+		} else {
+			br.Msg = "发送失败"
+		}
 	}
 
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "发送成功"
 }
-
-// SendEmail
-// @Title 获取短信/邮箱验证码
-// @Description 获取短信/邮箱验证码
-// @Param	request	body VerifyCodeReq true "type json string"
-// @Success 200 Ret=200 获取成功
-// @Router /sendEmail [get]
-func (this *UserController) SendEmail() {
-	utils.SendEmailByHz("测试邮箱", "测试内容", "564693862@qq.com")
-
-}

+ 146 - 61
controllers/wechat.go

@@ -1,86 +1,171 @@
 package controllers
 
 import (
+	"encoding/xml"
 	"eta/eta_mini_api/models"
 	"eta/eta_mini_api/services/wechat"
-	"eta/eta_mini_api/services/wx_app"
 	"eta/eta_mini_api/utils"
 	"fmt"
+	"strconv"
+	"time"
 )
 
 type WechatController struct {
 	BaseCommonController
 }
 
-// Login
-// @Title 微信用户登录
-// @Description 用户登录
-// @Param	request	body UserLoginReq true "type json string"
-// @Success 200 {object} models.LoginResp
-// @router /login [get]
-func (this *WechatController) Login() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
+// @Title 微信获取签名接口
+// @Description 微信获取签名接口
+// @Param   Url   query   string  true       "url地址"
+// @Success 200 {object} models.WechatSign
+// @router /notify [get,post]
+func (this *WechatController) Notify() {
+	echostr := this.GetString("echostr")
+	method := this.Ctx.Input.Method()
 
-	code := this.GetString("code")
-	wxUserInfo, err := wx_app.GetSession(code)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
-		return
+	type Notify struct {
+		ToUserName   string `xml:"ToUserName"`
+		FromUserName string `xml:"FromUserName"`
+		CreateTime   int    `xml:"CreateTime"`
+		MsgType      string `xml:"MsgType"`
+		Event        string `xml:"Event"`
+		EventKey     string `xml:"EventKey"`
+		Content      string `xml:"Content"`
 	}
+	if method == "POST" {
+		body := this.Ctx.Input.RequestBody
+		utils.FileLog.Info("wechat notify:" + string(body))
+		item := new(Notify)
+		err := xml.Unmarshal(body, &item)
+		if err != nil {
+			utils.FileLog.Info("xml.Unmarshal:" + err.Error())
+		}
+		contactMsg := "感谢关注东吴期货研究所\r\n公司地址:上海市黄浦区西藏南路1208号东吴证券大厦19楼\r\n\r\n业务合作:\r\n电话:021-6312 3065\r\n邮箱:lvan@dwqh88.com\r\n邮编:200001"
 
-	fmt.Println("openid", wxUserInfo.OpenID)
-	user, err := wechat.GetUserInfo(wxUserInfo.OpenID)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
-		return
+		var openId, returnResult string
+		if item.MsgType != "" {
+			openId = item.FromUserName
+		}
+		xmlTpl := `<xml>
+		<ToUserName><![CDATA[%s]]></ToUserName>
+		<FromUserName><![CDATA[%s]]></FromUserName>
+		<CreateTime>%s</CreateTime>
+		<MsgType><![CDATA[text]]></MsgType>
+		<Content><![CDATA[%s]]></Content>
+		</xml>`
+		createTime := strconv.FormatInt(time.Now().Unix(), 10)
+		xmlTpl = fmt.Sprintf(xmlTpl, openId, utils.DW_WX_Id, createTime, contactMsg)
+
+		if item.MsgType == "event" {
+			switch item.Event {
+			case "subscribe":
+				fmt.Println("关注")
+				go subscribe(openId)
+			case "unsubscribe":
+				fmt.Println("取消关注")
+				go models.UserSubscribe(0, openId)
+			case "CLICK":
+				returnResult = xmlTpl
+			default:
+				utils.FileLog.Info("wechat notify event:" + item.Event)
+			}
+			this.Ctx.WriteString(xmlTpl)
+		} else {
+			returnResult = xmlTpl
+		}
+		this.Ctx.WriteString(returnResult)
+	} else {
+		this.Ctx.WriteString(echostr)
 	}
-	fmt.Println("user----", user)
-	fmt.Println(wxUserInfo)
-	fmt.Println("openid", wxUserInfo.OpenID)
-	fmt.Println("unionid", wxUserInfo.UnionID)
-	// token, userId, isBind, err := services.WxLogin(wxUserInfo)
 }
 
-// GetUserInfo
-// @Title 获取微信用户信息
-// @Description 获取微信用户信息
-// @Param	request	body UserLoginReq true "type json string"
-// @Success 200 {object} models.LoginResp
-// @router /userInfo [get]
-func (this *WechatController) UserInfo() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-
-	openid := `oxgGc63ul7XT_kkxijd5mQI_6agg`
-	userInfo, err := wechat.GetUserInfo(openid)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,系统错误,Err:" + err.Error()
+// subscribe 关注后的处理逻辑
+func subscribe(openId string) {
+	userRecord, err := models.GetUserRecordByOpenId(openId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		fmt.Println("通过openid获取user_record记录失败,err:" + err.Error())
 		return
 	}
+	err = nil
 
-	br.Data = userInfo
-	br.Msg = "获取成功"
-	br.Success = true
-	br.Ret = 200
-}
+	// openId已存在
+	if userRecord != nil {
+		if userRecord.UserId > 0 { //已经绑定了的话,那么就去修改用户状态
+			models.UserSubscribe(1, openId)
+		} else {
+			// 没有绑定的话,那么校验下unionid,然后再去修改
+			unionId := userRecord.UnionId
+			if unionId == `` {
+				wxUserItem, err := wechat.GetUserInfo(openId)
+				if err != nil {
+					fmt.Println("获取用户信息失败,err:" + err.Error())
+					return
+				}
+				if wxUserItem.UnionID != `` {
+					unionId = wxUserItem.UnionID
+				}
+			}
 
-// SendEmail
-// @Title 获取微信用户信息
-// @Description 获取微信用户信息
-// @Param	request	body UserLoginReq true "type json string"
-// @Success 200 {object} models.LoginResp
-// @router /SendEmail [get]
-func (this *WechatController) SendEmail() {
-	utils.SendEmailByHz("测试邮箱", "测试内容", "564693862@qq.com")
+			updateCol := make([]string, 0)
+			userRecord.Subscribe = 1
+			userRecord.SubscribeTime = time.Now()
+			updateCol = append(updateCol, "Subscribe")
+			if unionId != `` {
+				userRecord.UnionId = unionId
+				// 通过unionid获取已绑定用户的user_record信息
+				bindUserRecord, _ := models.GetBindUserRecordByUnionId(unionId)
+				if bindUserRecord != nil {
+					userRecord.UserId = bindUserRecord.UserId
+					userRecord.RealName = bindUserRecord.RealName
+					userRecord.Sex = bindUserRecord.Sex
+					updateCol = append(updateCol, "UserId", "RealName")
+				}
+			}
+			err = userRecord.Update(updateCol)
+			if err != nil {
+				fmt.Println("关注后,通过openid更新user_record异常,ERR:", err)
+			}
+		}
 
+		return
+	}
+
+	// 没有记录,那么需要获取下unionid
+	wxUserItem, err := wechat.GetUserInfo(openId)
+	if err != nil {
+		fmt.Println("获取用户信息失败,err:" + err.Error())
+		return
+	}
+	newUserRecord := &models.UserRecord{
+		UserRecordId:  0,
+		OpenId:        openId,
+		UnionId:       wxUserItem.UnionID,
+		Subscribe:     1,
+		SubscribeTime: time.Now(),
+		NickName:      wxUserItem.Nickname,
+		Sex:           int(wxUserItem.Sex),
+		Province:      wxUserItem.Province,
+		City:          wxUserItem.City,
+		Country:       wxUserItem.Country,
+		Headimgurl:    wxUserItem.Headimgurl,
+		CreateTime:    time.Now(),
+	}
+	if wxUserItem.UnionID != `` {
+		// 通过unionid获取已绑定用户的user_record信息
+		bindUserRecord, _ := models.GetBindUserRecordByUnionId(wxUserItem.UnionID)
+		if bindUserRecord != nil {
+			newUserRecord.UserId = bindUserRecord.UserId
+			newUserRecord.RealName = bindUserRecord.RealName
+			newUserRecord.Sex = bindUserRecord.Sex
+			newUserRecord.Province = bindUserRecord.Province
+			newUserRecord.City = bindUserRecord.City
+			newUserRecord.Country = bindUserRecord.Country
+			newUserRecord.Headimgurl = bindUserRecord.Headimgurl
+		}
+	}
+	err = newUserRecord.Insert()
+	if err != nil {
+		fmt.Println("关注后,添加user_record信息失败,err:" + err.Error())
+		return
+	}
 }

+ 2 - 2
go.mod

@@ -7,7 +7,9 @@ require github.com/beego/beego/v2 v2.1.0
 require (
 	github.com/beego/bee/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.7.0
+	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/silenceper/wechat/v2 v2.1.6
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 )
@@ -18,11 +20,9 @@ require (
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/fatih/structs v1.1.0 // indirect
-	github.com/go-redis/redis/v8 v8.11.5 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/kr/text v0.2.0 // indirect
-	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect

+ 24 - 0
models/classify.go

@@ -0,0 +1,24 @@
+package models
+
+import (
+	"time"
+)
+
+type ClassifyDetail struct {
+	ClassifyId     int       `description:"分类id"`
+	ClassifyName   string    `description:"分类名称"`
+	Sort           int       `json:"-"`
+	ParentId       int       `description:"父级分类id"`
+	CreateTime     time.Time `description:"创建时间"`
+	ModifyTime     time.Time `description:"修改时间"`
+	Abstract       string    `description:"栏目简介"`
+	Descript       string    `description:"分享描述"`
+	ReportAuthor   string    `description:"栏目作者"`
+	AuthorDescript string    `description:"作者简介"`
+	ColumnImgUrl   string    `description:"栏目配图"`
+	HeadImgUrl     string    `description:"头部banner"`
+	AvatarImgUrl   string    `description:"头像"`
+	ReportImgUrl   string    `description:"报告配图"`
+	HomeImgUrl     string    `description:"首页配图"`
+	Stage          int       `description:"最新期数"`
+}

+ 5 - 4
models/db.go

@@ -16,11 +16,11 @@ func init() {
 	report_db, _ := orm.GetDB("rddp")
 	report_db.SetConnMaxLifetime(10 * time.Minute)
 
-	_ = orm.RegisterDataBase("master", "mysql", utils.MYSQL_URL_MASTER)
-	orm.SetMaxIdleConns("master", 50)
-	orm.SetMaxOpenConns("master", 100)
+	_ = orm.RegisterDataBase("default", "mysql", utils.MYSQL_URL_MASTER)
+	orm.SetMaxIdleConns("default", 50)
+	orm.SetMaxOpenConns("default", 100)
 
-	master_db, _ := orm.GetDB("master")
+	master_db, _ := orm.GetDB("default")
 	master_db.SetConnMaxLifetime(10 * time.Minute)
 
 	orm.Debug = true
@@ -32,6 +32,7 @@ func init() {
 		new(MsgCode),
 		new(User),
 		new(WxToken),
+		new(UserTemplateRecord),
 	)
 
 }

+ 10 - 3
models/msg_code.go

@@ -8,8 +8,8 @@ import (
 
 // MsgCode 验证码列表
 type MsgCode struct {
-	MsgCodeID  int64     `orm:"pk" description:"id"` // 短信验证码id
-	OpenID     string    `description:"用户openId"`    // 用户id
+	MsgCodeId  int64     `orm:"pk" description:"id"` // 短信验证码id
+	OpenId     string    `description:"用户openId"`    // 用户id
 	Mobile     string    `description:"手机号/邮箱"`      // 手机号/邮箱
 	Code       string    `description:"验证码"`         // 验证码
 	ExpiredIn  int64     `description:"过期时间"`        // 过期时间
@@ -17,7 +17,14 @@ type MsgCode struct {
 }
 
 func (m *MsgCode) Insert() (err error) {
-	o := orm.NewOrmUsingDB("master")
+	o := orm.NewOrm()
 	_, err = o.Insert(m)
 	return
 }
+
+func GetMsgCode(mobile, code string) (item *MsgCode, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM msg_code WHERE mobile=? AND code=? AND FROM_UNIXTIME(expired_in)>=NOW() `
+	err = o.Raw(sql, mobile, code).QueryRow(&item)
+	return
+}

+ 60 - 0
models/report.go

@@ -0,0 +1,60 @@
+package models
+
+import "time"
+
+type ReportList struct {
+	Id                 int       `description:"报告Id"`
+	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	Title              string    `description:"标题"`
+	Abstract           string    `description:"摘要"`
+	Author             string    `description:"作者"`
+	Frequency          string    `description:"频度"`
+	CreateTime         string    `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	State              int       `description:"1:未发布,2:已发布"`
+	PublishTime        string    `description:"发布时间"`
+	Stage              int       `description:"期数"`
+	MsgIsSend          int       `description:"消息是否已发送,0:否,1:是"`
+	Content            string    `description:"内容"`
+	VideoUrl           string    `description:"音频文件URL"`
+	VideoName          string    `description:"音频文件名称"`
+	VideoPlaySeconds   string    `description:"音频播放时长"`
+	VideoSize          string    `description:"音频文件大小,单位M"`
+	HasPermission      int       `description:"是否拥有报告权限,1:拥有,0:没有"`
+	TitleType          string    `description:"标题类型,FICC或者权益"`
+	IsCurrentDate      int       `description:"是否当前日期:1是,0不是"`
+	ClassifyDetail
+}
+
+type ReportDetail struct {
+	Id                 int    `description:"报告Id"`
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	CreateTime         string `description:"创建时间"`
+	ModifyTime         string `description:"修改时间"`
+	State              int    `description:"1:未发布,2:已发布"`
+	PublishTime        string `description:"发布时间"`
+	Stage              int    `description:"期数"`
+	MsgIsSend          int    `description:"消息是否已发送,0:否,1:是"`
+	Content            string `description:"内容"`
+	VideoUrl           string `description:"音频文件URL"`
+	VideoName          string `description:"音频文件名称"`
+	VideoPlaySeconds   string `description:"音频播放时长"`
+	VideoSize          string `description:"音频文件大小,单位M"`
+	ContentSub         string `description:"内容前两个章节"`
+	IsShowNewLabel     int    `description:"是否显示新标签"`
+	IsCurrentDate      int    `description:"是否当前日期"`
+	ClassifyName       string `description:"分类名称"`
+	TitleType          string `description:"标题类型,FICC或者权益"`
+}

+ 29 - 10
models/request/user.go

@@ -1,17 +1,36 @@
 package request
 
+// type LoginReq struct {
+// 	WxCode     string `description:"用户code"`
+// 	LoginType  int    `description:"登录方式:1:手机,2:邮箱"`
+// 	Mobile     string `description:"手机号"`
+// 	Email      string `description:"邮箱"`
+// 	AreaNum    int    `description:"国际区号"`
+// 	VerifyCode string `description:"短信/邮箱 验证码"`
+// }
 type LoginReq struct {
-	WxCode     string `description:"用户code"`
-	LoginType  int    `description:"登录方式:1:手机,2:邮箱"`
-	Mobile     string `description:"手机号"`
-	Email      string `description:"邮箱"`
-	AreaNum    int    `description:"国际区号"`
-	VerifyCode string `description:"短信/邮箱 验证码"`
+	LoginType int    `description:"登录方式:1:手机,2:邮箱"`
+	Phone     string `description:"手机号"`
+	Email     string `description:"邮箱"`
+	AreaCode  string `description:"国际区号"`
+	Code      string `description:"用户code"`
+	SmsCode   string `description:"短信/邮箱验证码"`
 }
 
 type VerifyCodeReq struct {
-	VerifyType  int    `description:"验证方式: 1-手机号; 2-邮箱"`
-	Mobile      string `description:"手机号"`
-	TelAreaCode string `description:"手机区号"`
-	Email       string `description:"邮箱"`
+	VerifyType int    `description:"验证方式: 1-手机号; 2-邮箱"`
+	Phone      string `description:"手机号"`
+	AreaCode   string `description:"手机区号"`
+	Email      string `description:"邮箱"`
+}
+
+type CheckEmailCodeReq struct {
+	Email   string `description:"邮箱"`
+	SmsCode string `description:"验证码"`
+}
+
+type CheckSmsCodeReq struct {
+	AreaCode string `description:"手机区号"`
+	Phone    string `description:"手机号"`
+	SmsCode  string `description:"验证码"`
 }

+ 5 - 0
models/request/wechat.go

@@ -0,0 +1,5 @@
+package request
+
+type SendTemplateMsgRep struct {
+	ReportId int
+}

+ 23 - 0
models/response/report.go

@@ -0,0 +1,23 @@
+package response
+
+import (
+	"eta/eta_mini_api/models"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ReportListResp struct {
+	List   []*models.ReportList
+	Paging *paging.PagingItem
+}
+
+type ReportDetailResp struct {
+	Report   *models.ReportDetail   `description:"报告"`
+	Classify *models.ClassifyDetail `description:"对应专栏"`
+}
+
+type ReportResp[T any] struct {
+	Ret  int
+	Data T
+	Msg  string
+}

+ 13 - 0
models/response/user.go

@@ -0,0 +1,13 @@
+package response
+
+type LoginResp struct {
+	UserId        int    `description:"用户id"`
+	Authorization string `description:"Token"`
+	Headimgurl    string `description:"用户头像"`
+	Mobile        string `description:"手机号"`
+	Email         string `description:"邮箱"`
+	CompanyName   string `description:"客户名称"`
+	Status        string `description:"状态"`
+	EndDate       string `description:"到期日期"`
+	ProductName   string `description:"客户类型名称"`
+}

+ 0 - 38
models/sys_session.go

@@ -1,38 +0,0 @@
-package models
-
-import (
-	"time"
-
-	"github.com/beego/beego/v2/client/orm"
-)
-
-type WxSession struct {
-	WxSessionId     int `orm:"pk"`
-	UserId          int
-	OpenId          string
-	AccessToken     string
-	ExpiredTime     time.Time
-	CreatedTime     time.Time
-	LastUpdatedTime time.Time
-}
-
-func (s *WxSession) AddWxSession() (err error) {
-	o := orm.NewOrmUsingDB("master")
-	_, err = o.Insert(s)
-	return
-}
-
-func GetWxSessionByToken(token string) (item *WxSession, err error) {
-	sql := `SELECT * FROM wx_session WHERE access_token=? AND expired_time> NOW() ORDER BY expired_time DESC LIMIT 1 `
-	o := orm.NewOrmUsingDB("master")
-	err = o.Raw(sql, token).QueryRow(&item)
-	return
-}
-
-// ExpiredWxSessionByUserId 过期掉用户token
-func ExpiredWxSessionByUserId(UserId int) (err error) {
-	sql := `UPDATE wx_session SET expired_time = NOW()  WHERE user_id=? AND expired_time > NOW()`
-	o := orm.NewOrmUsingDB("master")
-	_, err = o.Raw(sql, UserId).Exec()
-	return
-}

+ 72 - 1
models/user.go

@@ -8,6 +8,8 @@ import (
 
 type User struct {
 	UserId         int       `orm:"pk" description:"用户id"`
+	OpenId         string    `description:"openid"`
+	UnionId        string    `description:"unionid"`
 	RealName       string    `description:"姓名"`
 	Phone          string    `description:"手机号"`
 	AreaCode       string    `description:"区号"`
@@ -24,9 +26,78 @@ type User struct {
 	IsRegistered   bool      `description:"是否注册: 0表示没有注册,1表示注册"`
 }
 
+func (u *User) Insert() (insertId int64, err error) {
+	o := orm.NewOrm()
+	insertId, err = o.Insert(u)
+	return
+}
+
+type UserItem struct {
+	UserId       int       `description:"用户id"`
+	OpenId       string    `description:"open_id"`
+	UnionId      string    `description:"union_id"`
+	NickName     string    `description:"用户昵称"`
+	RealName     string    `description:"用户实际名称"`
+	Phone        string    `description:"手机号码"`
+	Componey     string    `description:"所属公司"`
+	AreaCode     string    `description:"区号"`
+	SellerId     int       `description:"销售id"`
+	Email        string    `description:"邮箱"`
+	Headimgurl   string    `description:"用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空"`
+	ValidEndTime time.Time `description:"服务截至时间"`
+	RegisterTime time.Time `description:"登录时间,用户首次登录小程序的时间"`
+	CreateTime   time.Time `description:"系统中新增用户的时间"`
+	ModifyTime   time.Time `description:"系统中用户信息更新的时间"`
+	IsRegistered bool      `description:"是否注册:1:已注册,0:未注册"`
+}
+
+// 根据openid获取用户关系
+func GetUserByOpenId(openId string) (item *User, err error) {
+	sql := `SELECT * FROM user WHERE open_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, openId).QueryRow(&item)
+	return
+}
+
+// 变更联系人是否已注册状态
+func ModifyUserRegisterStatus(userId int, status bool, registerTime, modifyTime time.Time) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE user SET is_registered=?, register_time=?, modify_time=? WHERE user_id = ? `
+	_, err = o.Raw(sql, status, registerTime, modifyTime, userId).Exec()
+	return
+}
+
 func GetUserById(userId int) (item *User, err error) {
-	o := orm.NewOrmUsingDB("master")
+	o := orm.NewOrm()
 	sql := `SELECT * FROM user WHERE user_id=? `
 	err = o.Raw(sql, userId).QueryRow(&item)
 	return
 }
+
+func GetUserList(condition string, pars []interface{}) (items []*User, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+func GetUserItemByPhone(phone string) (item *UserItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE phone=? `
+	err = o.Raw(sql, phone).QueryRow(&item)
+	return
+}
+
+func GetUserItemByEmail(email string) (item *UserItem, err error) {
+	sql := `SELECT * FROM user WHERE email=? `
+	err = orm.NewOrm().Raw(sql, email).QueryRow(&item)
+	return
+}
+func GetUserItemByUserId(userId int) (item *UserItem, err error) {
+	sql := `SELECT * FROM user WHERE user_id=? `
+	err = orm.NewOrm().Raw(sql, userId).QueryRow(&item)
+	return
+}

+ 60 - 0
models/user_record.go

@@ -0,0 +1,60 @@
+package models
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type UserRecord struct {
+	UserRecordId  int       `orm:"column(user_record_id);pk"`
+	OpenId        string    `description:"用户openid,最大长度:32"`
+	UnionId       string    `description:"用户unionid,最大长度:64"`
+	Subscribe     int       `description:"是否关注"`
+	SubscribeTime time.Time `description:""`
+	NickName      string    `descritpion:"用户昵称,最大长度:32"`
+	RealName      string    `descritpion:"用户实际名称,最大长度:32"`
+	Sex           int       `descritpion:"普通用户性别,1为男性,2为女性"`
+	Province      string    `description:"普通用户个人资料填写的省份,最大长度:30"`
+	City          string    `description:"普通用户个人资料填写的城市,最大长度:30"`
+	Country       string    `description:"国家,如中国为CN,最大长度:30"`
+	Headimgurl    string    `description:"用户第三方(微信)头像,最大长度:512"`
+	CreateTime    time.Time `description:"创建时间,关系添加时间、用户授权时间"`
+	SessionKey    string    `description:"微信小程序会话密钥,最大长度:255"`
+	UserId        int       `description:"用户id"`
+}
+
+func (item *UserRecord) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func (item *UserRecord) Insert() (err error) {
+	o := orm.NewOrm()
+	recordId, err := o.Insert(item)
+	item.UserRecordId = int(recordId)
+	return
+}
+
+// 根据openid获取用户关系
+func GetUserRecordByOpenId(openId string) (item *UserRecord, err error) {
+	sql := `SELECT * FROM user_record WHERE open_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, openId).QueryRow(&item)
+	return
+}
+
+func UserSubscribe(subscribeType int, openId string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE user_record SET subscribe=?,subscribe_time=NOW() WHERE open_id = ? AND create_platform=1 `
+	_, err = o.Raw(sql, subscribeType, openId).Exec()
+	return
+}
+
+// GetBindUserRecordByUnionId 通过unionid获取已绑定用户的user_record信息
+func GetBindUserRecordByUnionId(unionId string) (item *UserRecord, err error) {
+	sql := `SELECT * FROM user_record WHERE union_id=? AND user_id >0`
+	err = orm.NewOrm().Raw(sql, unionId).QueryRow(&item)
+	return
+}

+ 22 - 0
models/user_template_record.go

@@ -0,0 +1,22 @@
+package models
+
+import "github.com/beego/beego/v2/client/orm"
+
+type UserTemplateRecord struct {
+	ID         int    `description:"id"`
+	UserID     int    `description:"用户id"`
+	OpenID     string `description:"用户openid"`
+	Resource   string `description:"openid"` // 资源:报告id/手机号/活动id
+	SendData   string `description:"发送内容"`
+	Result     string `description:"响应结果"`
+	CreateDate string `description:"创建日期"`
+	CreateTime string `description:"创建时间"`
+	SendStatus int    `description:"发送状态"`   // 1:发送成功,0:发送失败
+	SendType   int    `description:"发送消息类型"` // 1:报告模板消息
+}
+
+func (u *UserTemplateRecord) Insert() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(u)
+	return
+}

+ 54 - 0
models/wx_session.go

@@ -0,0 +1,54 @@
+package models
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type WxSession struct {
+	WxSessionId    int `orm:"pk"`
+	UserId         int
+	OpenId         string
+	AccessToken    string
+	ExpireTime     time.Time
+	CreateTime     time.Time
+	LastUpdateTime time.Time
+}
+
+func (s *WxSession) AddWxSession() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(s)
+	return
+}
+
+func GetWxSessionByToken(token string) (item *WxSession, err error) {
+	sql := `SELECT * FROM wx_session WHERE access_token=? AND expire_time> NOW() ORDER BY expire_time DESC LIMIT 1 `
+	o := orm.NewOrm()
+	err = o.Raw(sql, token).QueryRow(&item)
+	return
+}
+
+// ExpiredWxSessionByUserId 过期掉用户token
+func ExpiredWxSessionByUserId(UserId int) (err error) {
+	sql := `UPDATE wx_session SET expire_time = NOW()  WHERE user_id=? AND expire_time > NOW()`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, UserId).Exec()
+	return
+}
+
+// 根据用户id获取token
+func GetTokenByOpenId(openId string) (item *WxSession, err error) {
+	sql := `SELECT * FROM wx_session WHERE open_id=? AND expire_time> NOW() ORDER BY wx_session_id DESC LIMIT 1 `
+	o := orm.NewOrm()
+	err = o.Raw(sql, openId).QueryRow(&item)
+	return
+}
+
+// 更新session
+func UpdateSession(sessionId, userId int, expireTime time.Time) (err error) {
+	sql := `update wx_session set user_id=?,expire_time=? where wx_session_id = ? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, userId, expireTime, sessionId).Exec()
+	return
+}

+ 2 - 2
models/wx_token.go

@@ -12,14 +12,14 @@ type WxToken struct {
 
 // Update 更新对应字段数据
 func (w *WxToken) Update(cols []string) (err error) {
-	o := orm.NewOrmUsingDB("master")
+	o := orm.NewOrm()
 	_, err = o.Update(w, cols...)
 	return
 }
 
 // GetById 根据id获取accessToken信息
 func GetWxTokenById() (info WxToken, err error) {
-	o := orm.NewOrmUsingDB("master")
+	o := orm.NewOrm()
 	sql := `SELECT * FROM wx_token WHERE id = ?`
 	err = o.Raw(sql, 0).QueryRow(&info)
 	return

+ 30 - 12
routers/commentsRouter.go

@@ -7,38 +7,56 @@ import (
 
 func init() {
 
-    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"],
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ChartPermissionController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ChartPermissionController"],
         beego.ControllerComments{
-            Method: "Login",
-            Router: `/login`,
-            AllowHTTPMethods: []string{"post"},
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"],
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ReportController"],
         beego.ControllerComments{
-            Method: "SendEmail",
-            Router: `/SendEmail`,
+            Method: "Detail",
+            Router: `/detail`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"],
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"],
+        beego.ControllerComments{
+            Method: "GetVerifyCode",
+            Router: `/getVerifyCode`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"],
         beego.ControllerComments{
             Method: "Login",
             Router: `/login`,
-            AllowHTTPMethods: []string{"get"},
+            AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
     beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:WechatController"],
         beego.ControllerComments{
-            Method: "UserInfo",
-            Router: `/userInfo`,
-            AllowHTTPMethods: []string{"get"},
+            Method: "Notify",
+            Router: `/notify`,
+            AllowHTTPMethods: []string{"get","post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})

+ 11 - 1
routers/router.go

@@ -16,7 +16,7 @@ func init() {
 		AllowCredentials: true,
 	}))
 	// beego.Router("/", &controllers.MainController{})
-	ns := beego.NewNamespace("api",
+	ns := beego.NewNamespace("/api",
 		beego.NSNamespace("/user",
 			beego.NSInclude(
 				&controllers.UserController{},
@@ -27,6 +27,16 @@ func init() {
 				&controllers.WechatController{},
 			),
 		),
+		beego.NSNamespace("/report",
+			beego.NSInclude(
+				&controllers.ReportController{},
+			),
+		),
+		beego.NSNamespace("/chart_perimission",
+			beego.NSInclude(
+				&controllers.ChartPermissionController{},
+			),
+		),
 	)
 	beego.AddNamespace(ns)
 }

+ 116 - 0
services/chart_permission.go

@@ -0,0 +1,116 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_mini_api/utils"
+	"io"
+	"net/http"
+	"strconv"
+	"time"
+)
+
+type ChartPermission struct {
+	ChartPermissionId     int       `orm:"column(chart_permission_id);pk" description:"问题ID" json:"chart_permission_id"`
+	ChartPermissionName   string    `description:"名称" json:"chart_permission_name"`
+	PermissionName        string    `description:"权限名" json:"permission_name"`
+	Sort                  int       `description:"排序" json:"sort"`
+	Enabled               int       `description:"是否可用" json:"enabled"`
+	CreatedTime           time.Time `description:"创建时间" json:"created_time"`
+	LastUpdatedTime       time.Time `description:"更新时间" json:"last_updated_time"`
+	TeleconferenceSort    int       `description:"电话会类型排序" json:"teleconference_sort"`
+	Remark                string    `description:"备注" json:"remark"`
+	ClassifyName          string    `description:"分类名称" json:"classify_name"`
+	ProductName           string    `description:"产品名称" json:"product_name"`
+	ProductId             int       `description:"产品ID" json:"product_id"`
+	ImageURL              string    `orm:"column(image_url);" description:"图片地址" json:"image_url"`
+	ShowType              int       `description:"1:查研观向小程序展示" json:"show_type"`
+	IsOther               int       `description:"是否是其他,用于查研观向小程序后台展示" json:"is_other"`
+	IsReport              int       `description:"是否是报告,用于查研观向小程序前台报告展示" json:"is_report"`
+	CygxAuth              int       `description:"是否是权限,用于查研观向小程序前台权限校验" json:"cygx_auth"`
+	PermissionType        int       `description:"1主观,2客观" json:"permission_type"`
+	YbImgUrl              string    `description:"研报小程序报告列表icon" json:"yb_img_url"`
+	ProductPermissionName string    `description:"种类权限名称" json:"product_permission_name"`
+	PriceDrivenState      int       `description:"品种价格驱动开启状态 0-关闭 1-开启" json:"price_driven_state"`
+	ImageUrlM             string    `description:"图片地址(查研观向移动端)" json:"image_url_m"`
+	ParentId              int       `description:"父级权限id" json:"parent_id"`
+	IsPublic              int       `description:"是否是公有权限1:公有权限,0私有权限" json:"is_public"`
+}
+
+func GetChartPermissionSecondList(chartPermissionId int) (resp []*ChartPermission, err error) {
+	url := utils.ETA_MINI_BRIDGE_URL + "/chart_permission/second/list"
+	client := &http.Client{}
+	if url != "" {
+		url += "?chartPermissonId=" + strconv.Itoa(chartPermissionId)
+	}
+	// 提交请求
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.ETA_MINI_APPID, utils.ETA_MINI_APP_SECRET)
+	//增加header选项
+	req.Header.Add("Nonce", nonce)
+	req.Header.Add("Timestamp", timestamp)
+	req.Header.Add("Appid", utils.ETA_MINI_APPID)
+	req.Header.Add("Signature", signature)
+	req.Header.Set("Content-Type", "application/json")
+
+	response, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer response.Body.Close()
+
+	body, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		return
+	}
+	utils.FileLog.Info("result:" + string(body))
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return
+	}
+	return
+
+}
+
+func GetChartPermissionList() (resp []*ChartPermission, err error) {
+	url := utils.ETA_MINI_BRIDGE_URL + "/chart_permission/list"
+	client := &http.Client{}
+	// 提交请求
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.ETA_MINI_APPID, utils.ETA_MINI_APP_SECRET)
+	//增加header选项
+	req.Header.Add("Nonce", nonce)
+	req.Header.Add("Timestamp", timestamp)
+	req.Header.Add("Appid", utils.ETA_MINI_APPID)
+	req.Header.Add("Signature", signature)
+	req.Header.Set("Content-Type", "application/json")
+
+	response, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer response.Body.Close()
+
+	body, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		return
+	}
+	utils.FileLog.Info("result:" + string(body))
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return
+	}
+	return
+
+}

+ 87 - 0
services/go_redis/redis.go

@@ -0,0 +1,87 @@
+package go_redis
+
+import (
+	"context"
+	"encoding/json"
+	"eta/eta_mini_api/utils"
+	"fmt"
+	"time"
+)
+
+// IsExist 检测redis中存在该key
+func IsExist(key string) (ok bool) {
+	result, err := utils.Redis.Exists(context.TODO(), key).Result()
+	if err != nil {
+		fmt.Println("err:", err)
+		return
+	}
+	if result > 0 {
+		ok = true
+	}
+
+	return
+}
+
+// SetNX 给key设置值,并设置过期时间
+func SetNX(key string, val interface{}, timeout time.Duration) (ok bool) {
+	result, err := utils.Redis.SetEX(context.TODO(), key, val, timeout).Result()
+	if err != nil {
+		return false
+	}
+	if result == "OK" {
+		ok = true
+	}
+	return
+}
+
+// RedisInt 获取int的值
+func RedisInt(key string) (result int, err error) {
+	result, err = utils.Redis.Get(context.TODO(), key).Int()
+	return
+}
+
+// RedisString 获取string的值
+func RedisString(key string) (result string, err error) {
+	result, err = utils.Redis.Get(context.TODO(), key).Result()
+	return
+}
+
+// Delete 删除key
+func Delete(key string) (err error) {
+	err = utils.Redis.Del(context.TODO(), key).Err()
+	return
+}
+
+// LPush 从列表左侧push数据
+func LPush(key string, val interface{}) (err error) {
+	data, _ := json.Marshal(val)
+	err = utils.Redis.LPush(context.TODO(), key, data).Err()
+	return err
+}
+
+// BRPop 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
+func BRPop(key string) (data string, err error) {
+	result, err := utils.Redis.BRPop(context.TODO(), 1*time.Second, key).Result()
+	if err != nil {
+		//fmt.Println("err:", err)
+		return
+	}
+	if len(result) == 2 {
+		data = result[1]
+	} else {
+		fmt.Println("result异常:", result)
+	}
+	return
+}
+
+// BRPop2Func 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
+func BRPop2Func(key string, callback func([]byte)) {
+	result, err := BRPop(key)
+	if err != nil {
+		//fmt.Println("err:", err)
+		return
+	}
+	resultByte := []byte(result)
+	callback(resultByte)
+	return
+}

+ 104 - 0
services/report.go

@@ -0,0 +1,104 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/utils"
+	"io"
+	"net/http"
+	"strconv"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ReportList struct {
+	List   []*models.ReportList
+	Paging *paging.PagingItem
+}
+
+type ReportResp[T any] struct {
+	Ret    int
+	Data   T
+	Msg    string
+	ErrMsg string
+}
+
+func GetReportList() (resp *ReportResp[ReportList], err error) {
+	url := utils.ETA_MINI_BRIDGE_URL + "/report/list"
+	client := &http.Client{}
+	// 提交请求
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return &ReportResp[ReportList]{}, err
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.ETA_MINI_APPID, utils.ETA_MINI_APP_SECRET)
+	//增加header选项
+	req.Header.Add("Nonce", nonce)
+	req.Header.Add("Timestamp", timestamp)
+	req.Header.Add("Appid", utils.ETA_MINI_APPID)
+	req.Header.Add("Signature", signature)
+	req.Header.Set("Content-Type", "application/json")
+
+	response, err := client.Do(req)
+	if err != nil {
+		return &ReportResp[ReportList]{}, err
+	}
+	defer response.Body.Close()
+
+	body, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		return &ReportResp[ReportList]{}, err
+	}
+
+	utils.FileLog.Info("result:" + string(body))
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return resp, err
+	}
+	return resp, nil
+
+}
+
+func GetReportDetail(reportId int) (resp *ReportResp[models.ReportDetail], err error) {
+	url := utils.ETA_MINI_BRIDGE_URL + "/report/detail"
+	client := &http.Client{}
+	if url != "" {
+		url += "?ReportId=" + strconv.Itoa(reportId)
+	}
+	// 提交请求
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return
+	}
+	nonce := utils.GetRandStringNoSpecialChar(16)
+	timestamp := time.Now().Format(utils.FormatDateTimeUnSpace)
+	signature := utils.GetSign(nonce, timestamp, utils.ETA_MINI_APPID, utils.ETA_MINI_APP_SECRET)
+	//增加header选项
+	req.Header.Add("Nonce", nonce)
+	req.Header.Add("Timestamp", timestamp)
+	req.Header.Add("Appid", utils.ETA_MINI_APPID)
+	req.Header.Add("Signature", signature)
+	req.Header.Set("Content-Type", "application/json")
+
+	response, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer response.Body.Close()
+
+	body, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		return
+	}
+	utils.FileLog.Info("result:" + string(body))
+	err = json.Unmarshal(body, &resp)
+	if err != nil {
+		return resp, err
+	}
+	return resp, nil
+}

+ 5 - 5
services/sms.go

@@ -92,11 +92,11 @@ func sendSmsGj(mobile, code, areaNum string) (rs []byte, err error) {
 	//初始化参数
 	param := url.Values{}
 	//配置请求参数,方法内部已处理urlencode问题,中文参数可以直接传参
-	param.Set("mobile", mobile)           //接受短信的用户手机号码
-	param.Set("tplId", "10054")           //您申请的短信模板ID,根据实际情况修改
-	param.Set("tplValue", "#code#="+code) //您设置的模板变量,根据实际情况
-	param.Set("key", utils.JhGjAppKey)    //应用APPKEY(应用详细页查询)
-	param.Set("areaNum", areaNum)         //应用APPKEY(应用详细页查询)
+	param.Set("mobile", mobile)                    //接受短信的用户手机号码
+	param.Set("tplId", "262642")                   //您申请的短信模板ID,根据实际情况修改
+	param.Set("tplValue", "#code#="+code+"#m#=15") //您设置的模板变量,根据实际情况
+	param.Set("key", utils.JhGjAppKey)             //应用APPKEY(应用详细页查询)
+	param.Set("areaNum", areaNum)                  //应用APPKEY(应用详细页查询)
 
 	Url, err = url.Parse(apiURL)
 	if err != nil {

+ 76 - 1
services/user.go

@@ -1,6 +1,14 @@
 package services
 
-import "github.com/silenceper/wechat/v2/miniprogram/auth"
+import (
+	"errors"
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/services/go_redis"
+	"eta/eta_mini_api/utils"
+	"time"
+
+	"github.com/silenceper/wechat/v2/miniprogram/auth"
+)
 
 type UserInfo struct {
 }
@@ -17,3 +25,70 @@ func WxLogin(wxSession auth.ResCode2Session) (token string, userId int, isBind b
 func GetWxUserItemByOpenId(openid string) (userInfo UserInfo, err error) {
 	return
 }
+
+// BindWxUser 用户绑定
+func BindWxUser(unionId, openId, phone, email, areaCode string) (wxUser *models.UserItem, errMsg string, err error) {
+	if phone == "" && email == "" {
+		err = errors.New("手机号或邮箱必填一个")
+		return
+	}
+	//根据手机号获取用户信息
+	if phone != "" {
+		tmpWxUser, wxUserErr := models.GetUserItemByPhone(phone)
+		if wxUserErr != nil && wxUserErr.Error() != utils.ErrNoRow() {
+			err = wxUserErr
+			return
+		}
+		wxUser = tmpWxUser
+	}
+	//根据邮箱获取用户信息
+	if wxUser == nil && email != "" {
+		tmpWxUser, wxUserErr := models.GetUserItemByEmail(email)
+		if wxUserErr != nil && wxUserErr.Error() != utils.ErrNoRow() {
+			err = wxUserErr
+			return
+		}
+		wxUser = tmpWxUser
+	}
+
+	var userId int
+	//如果查询出来的用户是nil,那么需要新增用户
+	if wxUser == nil {
+		// key := "bind_wx_user:mobile:" + phone + ":email:" + email
+		key := utils.CACHE_ACCESS_WX_BIND + phone + ":" + email
+		isHas := go_redis.IsExist(key)
+		if isHas {
+			err = errors.New("多次提交,请关闭页面重新进入")
+			return
+		}
+		go_redis.SetNX(key, "ok", time.Second*300)
+		user := &models.User{
+			Phone:    phone,
+			AreaCode: areaCode,
+			Email:    email,
+			OpenId:   openId,
+			UnionId:  unionId,
+		}
+		tmpUserId, addUserErr := user.Insert()
+		//添加完成,清除缓存
+		_ = go_redis.Delete(key)
+		if addUserErr != nil {
+			err = addUserErr
+			return
+		}
+		user.UserId = int(tmpUserId)
+		userId = int(tmpUserId)
+		wxUser, err = models.GetUserItemByUserId(userId)
+	} else {
+		userId = wxUser.UserId
+	}
+
+	//如果该用户 绑定注册状态 字段处于 未注册 的情况下,那么去修改该数据
+	if !wxUser.IsRegistered {
+		err = models.ModifyUserRegisterStatus(userId, true, time.Now(), time.Now())
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 10 - 0
services/verify.go

@@ -0,0 +1,10 @@
+package services
+
+import "eta/eta_mini_api/utils"
+
+func CheckEncryption() (ok bool) {
+	if utils.RunMode == "release" {
+		ok = true
+	}
+	return
+}

+ 177 - 0
services/wechat/template_msg.go

@@ -0,0 +1,177 @@
+package wechat
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/utils"
+	"fmt"
+	"io"
+	"net/http"
+	"time"
+)
+
+var (
+	TemplateMsgSendUrl       = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s"
+	TemplateMsgClearQuotaUrl = "https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s"
+)
+
+type TemplateMsgSendClient struct {
+	AccessToken string
+	Data        []byte
+}
+
+type SendTemplateResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+	MsgID   int    `json:"msgid"`
+}
+
+type ClearQuotaResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+}
+
+type OpenIdList struct {
+	OpenId string
+	UserId int
+}
+
+// TemplateMsgSendClient.ClearQuota 清除发送超过当日10万次限制
+func (c *TemplateMsgSendClient) ClearQuota() (result *ClearQuotaResponse, err error) {
+	key := "CACHE_SendTemplateMsg_ERR"
+	exists, _ := utils.Redis.Exists(context.TODO(), key).Result()
+	if exists == 1 {
+		return
+	}
+	_ = utils.Redis.SetEX(context.TODO(), key, 1, 6*time.Minute)
+
+	sendUrl := fmt.Sprintf(TemplateMsgClearQuotaUrl, c.AccessToken)
+	client := http.Client{}
+	clearData := make(map[string]interface{})
+	clearData["appid"] = WxAppId
+	clearJson, _ := json.Marshal(clearData)
+	resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(clearJson))
+	if err != nil {
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	clearBody, err := io.ReadAll(resp.Body)
+	err = json.Unmarshal(clearBody, &result)
+	return
+}
+
+// TemplateMsgSendClient.SendMsg 推送消息
+func (c *TemplateMsgSendClient) SendMsg() (sendRes *SendTemplateResponse, err error) {
+	// 请求接口
+	sendUrl := fmt.Sprintf(TemplateMsgSendUrl, c.AccessToken)
+	client := http.Client{}
+	resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(c.Data))
+	if err != nil {
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	body, _ := io.ReadAll(resp.Body)
+	if err = json.Unmarshal(body, &sendRes); err != nil {
+		return
+	}
+	// 模板消息发送超过当日10万次限制错误处理
+	if sendRes.Errcode == 45009 {
+		// 发送提示邮件
+		// go alarm_msg.SendAlarmMsg("模板消息发送超过当日10万次限制, SendTemplateResponse: "+string(body), 3)
+		// 清理限制
+		clearRes, e := c.ClearQuota()
+		if e != nil {
+			err = e
+			return
+		}
+		if clearRes.Errcode != 0 {
+			clearJson, er := json.Marshal(clearRes)
+			if er != nil {
+				return nil, er
+			}
+			fmt.Println("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: " + string(clearJson))
+			// go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: "+string(clearJson), 3)
+			return
+		}
+		// 发送成功邮件
+		// go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用成功", 1)
+		// 重新推送
+		go func() {
+			_, e := c.SendMsg()
+			if e != nil {
+				return
+				// reSendJson, _ := json.Marshal(reSend)
+				// alarm_msg.SendAlarmMsg("重新推送模板消息失败, SendTemplateResponse: "+string(reSendJson), 3)
+			}
+		}()
+	}
+	if sendRes.Errcode != 0 {
+		err = errors.New("推送模板消息失败, SendTemplateResponse: " + string(body))
+	}
+	return
+}
+
+// AddUserTemplateRecord 新增模板消息推送记录
+func AddUserTemplateRecord(userId, sendStatus, sendType int, openid, resource, sendData, result string) (err error) {
+	item := &models.UserTemplateRecord{
+		UserID:     userId,
+		OpenID:     openid,
+		Resource:   resource,
+		SendData:   sendData,
+		Result:     result,
+		CreateDate: time.Now().Format(utils.FormatDate),
+		CreateTime: time.Now().Format(utils.FormatDateTime),
+		SendStatus: sendStatus,
+		SendType:   sendType,
+	}
+	err = item.Insert()
+	return
+}
+
+// SendMultiTemplateMsg 推送模板消息至多个用户
+func SendMultiTemplateMsg(sendMap map[string]interface{}, items []*OpenIdList, resource string, sendType int) (err error) {
+	ws := GetWxChat()
+	accessToken, err := ws.GetAccessToken()
+	if err != nil {
+		return
+	}
+	for _, item := range items {
+		sendMap["touser"] = item.OpenId
+		data, e := json.Marshal(sendMap)
+		if e != nil {
+			err = e
+			return
+		}
+		ts := &TemplateMsgSendClient{
+			AccessToken: accessToken,
+			Data:        data,
+		}
+		result, e := ts.SendMsg()
+		if result == nil {
+			return
+		}
+		// 推送消息记录
+		{
+			go func(v *OpenIdList) {
+				sendStatus := 1
+				if e != nil {
+					sendStatus = 0
+				}
+				resultJson, _ := json.Marshal(result)
+				_ = AddUserTemplateRecord(v.UserId, sendStatus, sendType, v.OpenId, resource, string(data), string(resultJson))
+			}(item)
+		}
+		if e != nil {
+			err = e
+			return
+		}
+	}
+	return
+}

+ 3 - 7
services/wechat/wechat.go

@@ -2,6 +2,7 @@ package wechat
 
 import (
 	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/utils"
 	"fmt"
 	"time"
 
@@ -19,11 +20,6 @@ var (
 	WxAppSecret string
 )
 
-func init() {
-	WxAppId = `wx1c6d59a9ca4b42b3`
-	WxAppSecret = `090716fa7b7fd89172cb26065fa4e6af`
-}
-
 type WechatAccessToken struct {
 }
 
@@ -31,8 +27,8 @@ func GetWxChat() (officialAccount *officialaccount.OfficialAccount) {
 	wc := wechat.NewWechat()
 	memory := cache.NewMemory()
 	conf := &config.Config{
-		AppID:          WxAppId,
-		AppSecret:      WxAppSecret,
+		AppID:          utils.DW_WX_APPID,
+		AppSecret:      utils.DW_WX_APP_SECRET,
 		Token:          "",
 		EncodingAESKey: "",
 		Cache:          memory,

+ 4 - 20
services/wx_app/wx_app.go

@@ -1,6 +1,8 @@
 package wx_app
 
 import (
+	"eta/eta_mini_api/utils"
+
 	wechat "github.com/silenceper/wechat/v2"
 	"github.com/silenceper/wechat/v2/cache"
 	"github.com/silenceper/wechat/v2/miniprogram"
@@ -9,31 +11,13 @@ import (
 	"github.com/silenceper/wechat/v2/miniprogram/encryptor"
 )
 
-// 微信小程序配置信息
-var (
-	WxId                 string //微信原始ID
-	WxAppId              string
-	WxAppSecret          string
-	WxPlatform           int    //用户来源,需要入库,用来保存该用户来自哪个平台,默认是:1
-	EnvVersion           string // 小程序版本, release-正式版; trial-体验版; develop-开发版
-	SendWxTemplateMsgUrl string
-)
-
-func init() {
-	WxAppId = `wx1c6d59a9ca4b42b3`
-	// WxId = `gh_75abb562a946`
-	WxAppSecret = `090716fa7b7fd89172cb26065fa4e6af`
-	EnvVersion = "trial"
-	SendWxTemplateMsgUrl = "http://127.0.0.1:8086/v1/wechat/send_template_msg"
-}
-
 func GetWxApp() (miniprogram *miniprogram.MiniProgram) {
 	wc := wechat.NewWechat()
 	memory := cache.NewMemory()
 	//memory := cache.NewRedis(global.Redis)
 	cfg := &config.Config{
-		AppID:     WxAppId,
-		AppSecret: WxAppSecret,
+		AppID:     utils.WX_APPID,
+		AppSecret: utils.WX_APP_SECRET,
 		Cache:     memory,
 	}
 

+ 69 - 0
utils/common.go

@@ -1,13 +1,18 @@
 package utils
 
 import (
+	"crypto/hmac"
 	"crypto/md5"
+	"crypto/sha256"
+	"encoding/base64"
 	"encoding/hex"
 	"fmt"
 	"math"
 	"math/rand"
 	"regexp"
+	"sort"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -19,6 +24,11 @@ const (
 
 const TelAreaCodeHome = "86" // 大陆区号
 
+// 数据没有记录
+func ErrNoRow() string {
+	return "<QuerySeter> no row found"
+}
+
 var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
 
 // ValidateMobileFormatat 校验手机格式
@@ -78,3 +88,62 @@ func GetRandString(size int) string {
 	}
 	return randomSb
 }
+
+// HmacSha256 计算HmacSha256
+// key 是加密所使用的key
+// data 是加密的内容
+func HmacSha256(key string, data string) []byte {
+	mac := hmac.New(sha256.New, []byte(key))
+	_, _ = mac.Write([]byte(data))
+	return mac.Sum(nil)
+}
+
+// HmacSha256ToBase64 将加密后的二进制转Base64字符串
+func HmacSha256ToBase64(key string, data string) string {
+	return base64.URLEncoding.EncodeToString(HmacSha256(key, data))
+}
+
+func GetSign(nonce, timestamp, appId, secret string) (sign string) {
+	signStrMap := map[string]string{
+		"nonce":     nonce,
+		"timestamp": timestamp,
+		"appid":     appId,
+	}
+	keys := make([]string, 0, len(signStrMap))
+	for k := range signStrMap {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	var signStr string
+	for _, k := range keys {
+		signStr += k + "=" + signStrMap[k] + "&"
+	}
+	signStr = strings.Trim(signStr, "&")
+	fmt.Println("signStr:" + signStr)
+	sign = HmacSha256ToBase64(secret, signStr)
+	return
+}
+
+func GetRandStringNoSpecialChar(size int) string {
+	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
+	randomSb := ""
+	digitSize := len(allLetterDigit)
+	for i := 0; i < size; i++ {
+		randomSb += allLetterDigit[rnd.Intn(digitSize)]
+	}
+	return randomSb
+}
+
+func StringsToJSON(str string) string {
+	rs := []rune(str)
+	jsons := ""
+	for _, r := range rs {
+		rint := int(r)
+		if rint < 128 {
+			jsons += string(r)
+		} else {
+			jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
+		}
+	}
+	return jsons
+}

+ 79 - 0
utils/config.go

@@ -1,10 +1,14 @@
 package utils
 
 import (
+	"context"
+	"encoding/json"
 	"fmt"
+	"strconv"
 
 	beeLogger "github.com/beego/bee/v2/logger"
 	"github.com/beego/beego/v2/server/web"
+	"github.com/go-redis/redis/v8"
 )
 
 // 数据库配置
@@ -12,9 +16,15 @@ var (
 	RunMode          string
 	MYSQL_URL_MASTER string
 	MYSQL_URL_RDDP   string
+
+	REDIS_CACHE string        //缓存地址
+	Redis       *redis.Client //redis链接
 )
 
+// 日志配置
 var (
+	LogPath    string //调用过程中的日志存放地址
+	LogFile    string
 	ApiLogPath string // 接口请求地址和接口返回值日志存放地址
 	ApiLogFile string
 	BinLogPath string // 数据库相关的日志存放地址
@@ -22,6 +32,26 @@ var (
 	LogMaxDays int // 日志最大保留天数
 )
 
+var (
+	SMS_TPLID string // 短信模板
+)
+
+// 微信相关
+var (
+	DW_WX_Id               string //微信原始ID
+	WX_APPID               string
+	WX_APP_SECRET          string
+	DW_WX_APPID            string
+	DW_WX_APP_SECRET       string
+	TEMPLATE_ID_BY_PRODUCT string
+)
+
+// 桥接服务
+var (
+	ETA_MINI_BRIDGE_URL string
+	ETA_MINI_APPID      string
+	ETA_MINI_APP_SECRET string
+)
 var DesKey string // 接口返回加密KEY
 
 func init() {
@@ -40,4 +70,53 @@ func init() {
 	MYSQL_URL_RDDP = config["mysql_url_rddp"]
 	MYSQL_URL_MASTER = config["mysql_url_master"]
 
+	SMS_TPLID = config["sms_tplId"]
+
+	WX_APPID = config["wx_appid"]
+	WX_APP_SECRET = config["wx_app_secret"]
+	TEMPLATE_ID_BY_PRODUCT = config["template_id_by_product"]
+	DW_WX_Id = config["dw_wx_id"]
+	DW_WX_APPID = config["dw_wx_appid"]
+	DW_WX_APP_SECRET = config["dw_wx_app_secret"]
+
+	ETA_MINI_BRIDGE_URL = config["eta_mini_bridge_url"]
+	ETA_MINI_APPID = config["eta_mini_appid"]
+	ETA_MINI_APP_SECRET = config["eta_mini_app_secret"]
+
+	initRedis(config)
+}
+
+// initRedis 初始化redis配置
+func initRedis(config map[string]string) {
+	REDIS_CACHE = config["beego_cache"]
+	if len(REDIS_CACHE) <= 0 {
+		panic("redis链接参数没有配置")
+	}
+
+	var redisConf map[string]string
+	err := json.Unmarshal([]byte(REDIS_CACHE), &redisConf)
+	if err != nil {
+		panic("redis 配置异常失败:" + err.Error())
+	}
+
+	redisDb := 0 //默认使用redis的0库
+	if dbStr, ok := redisConf["db"]; ok {
+		redisDb, err = strconv.Atoi(dbStr)
+		if err != nil {
+			panic("redis 操作db库配置异常,db:" + dbStr)
+		}
+	}
+	client := redis.NewClient(&redis.Options{
+		Addr:     redisConf["conn"],
+		Password: redisConf["password"],
+		DB:       redisDb,
+		//PoolSize: 10, //连接池最大socket连接数,默认为10倍CPU数, 10 * runtime.NumCPU(暂不配置)
+	})
+	_, err = client.Ping(context.TODO()).Result()
+	if err != nil {
+		panic("redis 链接失败:" + err.Error())
+	}
+
+	//全局赋值redis链接
+	Redis = client
 }

+ 29 - 2
utils/constants.go

@@ -6,16 +6,43 @@ const (
 	VerifyCodeExpireMinute = 15                // 短信/邮箱验证码过期时间-分钟
 )
 
+// 常量定义
+const (
+	FormatTime            = "15:04:05"                //时间格式
+	FormatDate            = "2006-01-02"              //日期格式
+	FormatDateCN          = "2006年01月02日"             //日期格式(中文)
+	FormatDateUnSpace     = "20060102"                //日期格式
+	FormatDateTime        = "2006-01-02 15:04:05"     //完整时间格式
+	HlbFormatDateTime     = "2006-01-02_15:04:05.999" //完整时间格式
+	FormatDateTimeUnSpace = "20060102150405"          //完整时间格式
+	PageSize15            = 15                        //列表页每页数据量
+	PageSize5             = 5
+	PageSize10            = 10
+	PageSize20            = 20
+	PageSize30            = 30
+)
+
 const (
 	UserLoginSalt = "kOld9YUdf89T2uIH"         // 用户登录盐值
 	DesKeySalt    = "odNMloUrTAmyRd9fb0TtlrPk" // DesKey盐值
 )
 
 const (
-	APPNAME          = "弘则-日度点评"
-	EmailSendToUsers = "glji@hzinsights.com;pyan@hzinsights.com"
+	Key = "KcSJaJodi78LAA7HEWpaiH49" //全局加密KEY
 )
+
 const (
 	JhGnAppKey = "4c8504c49dd335e99cfd7b6a3a9e2415" //聚合国内AppKey
 	JhGjAppKey = "3326ad2c1047a4cd92ace153e6044ca3"
 )
+
+const (
+	APPNAME          = "东吴研报小程序"
+	EmailSendToUsers = "glji@hzinsights.com;pyan@hzinsights.com"
+)
+
+const (
+	CACHE_ACCESS_TOKEN_LOGIN          = "pc_eta_min_crm:login:"          //管理后台登录
+	CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST = "pc_eta_min_crm:login:no_trust:" //管理后台登录(不可信登录态)
+	CACHE_ACCESS_WX_BIND              = "eta_mini_api:phone:email:"      //管理后台登录(不可信登录态)
+)

+ 3 - 2
utils/email.go

@@ -31,7 +31,7 @@ func SendEmail(title, content string, touser string) bool {
 }
 
 // 发送邮件
-func SendEmailByHz(title, content string, touser string) (result bool, err error) {
+func SendEmailByDw(title, content string, touser string) (result bool, err error) {
 	var arr []string
 	sub := strings.Index(touser, ";")
 	if sub >= 0 {
@@ -48,7 +48,8 @@ func SendEmailByHz(title, content string, touser string) (result bool, err error
 	m.SetHeader("To", arr...)
 	m.SetHeader("Subject", title)
 	m.SetBody("text/html", content)
-	d := gomail.NewDialer("smtp.mxhichina.com", 465, "lvan@dwqh88.com", "Dwqh20248888")
+	// d := gomail.NewDialer("smtp.mxhichina.com", 465, "lvan@dwqh88.com", "Dwqh20248888")
+	d := gomail.NewDialer("mail.dwqh88.com", 465, "lvan@dwqh88.com", "Dwqh20248888")
 	if err := d.DialAndSend(m); err != nil {
 		result = false
 		return result, err

+ 26 - 2
utils/logs.go

@@ -9,16 +9,40 @@ import (
 )
 
 const (
+	DefaultLogPath    = "./etalogs/filelog"
 	DefaultBinlogPath = "./etalogs/binlog"
 	DefaultApiLogPath = "./etalogs/apilog"
 )
 
 var (
-	BinLog *logs.BeeLogger
-	ApiLog *logs.BeeLogger
+	BinLog  *logs.BeeLogger
+	ApiLog  *logs.BeeLogger
+	FileLog *logs.BeeLogger
 )
 
 func init() {
+	if LogMaxDays == 0 {
+		LogMaxDays = 30
+	}
+	logPath := LogPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogFile
+	if logFile == "" {
+		logFile = "filelog.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	FileLog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	b, _ := json.Marshal(logConf)
+	FileLog.SetLogger(logs.AdapterFile, string(b))
+	FileLog.EnableFuncCallDepth(true)
 	initApiLog()
 	initBinLog()
 }