浏览代码

fix: 图表权限

hsun 14 小时之前
父节点
当前提交
fa17a1daa7
共有 8 个文件被更改,包括 378 次插入8 次删除
  1. 31 7
      controllers/report.go
  2. 49 1
      controllers/user.go
  3. 80 0
      models/business_conf.go
  4. 14 0
      models/db.go
  5. 9 0
      routers/commentsRouter.go
  6. 186 0
      services/report.go
  7. 5 0
      utils/config.go
  8. 4 0
      utils/constants.go

+ 31 - 7
controllers/report.go

@@ -5,6 +5,7 @@ import (
 	"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/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -195,6 +196,12 @@ func (this *ReportOpenController) Detail() {
 		br.ErrMsg = "获取报告详情失败,Err:" + err.Error()
 		return
 	}
+	if report == nil {
+		br.Msg = "报告不存在"
+		return
+	}
+
+	// 版头版尾
 	if report.HeadResourceId > 0 || report.EndResourceId > 0 {
 		headImg, err := models.GetSmartReportResourceById(report.HeadResourceId)
 		if err != nil && err.Error() != utils.ErrNoRow() {
@@ -212,26 +219,43 @@ func (this *ReportOpenController) Detail() {
 		}
 	}
 
+	// 章节
+	chapters := make([]*models.ReportChapter, 0)
 	if report.HasChapter == 1 {
-		chapterList, err := models.GetReportChapterList(report.Id)
-		if err != nil {
-			br.Msg = "该报告已删除"
-			br.ErrMsg = "获取章节列表失败,Err:" + err.Error()
+		chapterList, e := models.GetReportChapterList(report.Id)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取章节列表失败, %v", e)
 			return
 		}
 		for _, v := range chapterList {
 			v.Content = html.UnescapeString(v.Content)
 		}
-		report.ChapterContent = chapterList
+		chapters = chapterList
 	}
 	report.ContentSub = html.UnescapeString(report.ContentSub)
 	report.Content = html.UnescapeString(report.Content)
-	if report == nil {
-		br.Msg = "报告不存在"
+
+	// 图表授权token
+	isOpenChartExpired, e := services.CheckIsOpenChartExpired()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取图表是否开启鉴权失败, %v", e)
 		return
 	}
+	if isOpenChartExpired {
+		tokenMap := make(map[string]string)
+		report.Content = services.HandleReportContent(report.Content, "add", tokenMap)
+		report.ContentSub = services.HandleReportContent(report.ContentSub, "add", tokenMap)
+
+		for _, v := range chapters {
+			v.Content = html.UnescapeString(v.Content)
+			v.Content = services.HandleReportContent(v.Content, "add", tokenMap)
+		}
+	}
 
 	resp := new(response.ReportDetailResp)
+	report.ChapterContent = chapters
 	resp.Report = report
 
 	br.Data = resp

+ 49 - 1
controllers/user.go

@@ -113,7 +113,7 @@ func (this *UserController) GetVerifyCode() {
 		var pars services.SmsApiPars
 		pars.PhoneList = append(pars.PhoneList, req.Phone)
 		pars.ServiceProvider = services.SmsApiServiceProviderDefault
-		pars.SmsContent = fmt.Sprintf("验证码短信:<br>【金瑞期货】您的短信验证码是%s,有效期15分钟", code)
+		pars.SmsContent = fmt.Sprintf("【金瑞期货】您的短信验证码是%s,有效期15分钟", code)
 		pars.Sender = "金瑞小程序"
 		pars.Department = "信息技术部"
 		pars.Purpose = "小程序登录短信验证码"
@@ -271,3 +271,51 @@ func (this *UserAuthController) Info() {
 	br.Msg = "获取成功"
 	br.Success = true
 }
+
+// SendSms
+// @Title 测试-发送短信
+// @Description 测试-发送短信
+// @Param	request	body models.LoginReq true "type json string"
+// @Success 200 {object} models.Users
+// @router /send_sms [post]
+func (this *UserController) SendSms() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req request.LoginReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Phone == "" {
+		br.Msg = "请输入手机号"
+		br.ErrMsg = "请输入手机号"
+		return
+	}
+	code := utils.GetRandDigit(6)
+
+	var pars services.SmsApiPars
+	pars.PhoneList = append(pars.PhoneList, req.Phone)
+	pars.ServiceProvider = services.SmsApiServiceProviderDefault
+	pars.SmsContent = fmt.Sprintf("【金瑞期货】您的短信验证码是%s,有效期15分钟", code)
+	pars.Sender = "金瑞小程序"
+	pars.Department = "信息技术部"
+	pars.Purpose = "小程序登录短信验证码"
+	result, e := services.PostSmsApi(pars)
+	sendOk := true
+	if e != nil {
+		sendOk = false
+	}
+	fmt.Println(result)
+	utils.FileLog.Debug(result)
+
+	br.Data = sendOk
+	br.Ret = 200
+	br.Msg = "发送成功"
+	br.Success = true
+}

+ 80 - 0
models/business_conf.go

@@ -0,0 +1,80 @@
+package models
+
+import (
+	"eta/eta_mini_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"html"
+	"strconv"
+	"time"
+)
+
+var (
+	BusinessConfMap map[string]string
+)
+
+const (
+	BusinessConfReportChartExpiredTime = "ReportChartExpiredTime" // 图表有效期鉴权时间,单位:分钟
+	BusinessConfIsOpenChartExpired     = "IsOpenChartExpired"     // 是否开启图表权限
+)
+
+// BusinessConf 商户配置表
+type BusinessConf struct {
+	Id         int       `orm:"column(id);pk" gorm:"column:id;primaryKey;autoIncrement"` //`orm:"column(id);pk" gorm:"primaryKey" `
+	ConfKey    string    `gorm:"column:conf_key"`                                        //`description:"配置Key"`
+	ConfVal    string    `gorm:"column:conf_val"`                                        //`description:"配置值"`
+	ValType    int       `gorm:"column:val_type"`                                        //`description:"1-字符串;2-数值;3-字符串数组;4-富文本;"`
+	Necessary  int       `gorm:"column:necessary"`                                       //`description:"是否必填:0-否;1-是"`
+	Remark     string    `gorm:"column:remark"`                                          // `description:"备注"`
+	CreateTime time.Time `gorm:"column:create_time"`
+}
+
+func (b *BusinessConf) TableName() string {
+	return "business_conf"
+}
+
+// GetItemByConfKey 获取配置项
+func (b *BusinessConf) GetItemByConfKey(key string) (item *BusinessConf, err error) {
+	sql := `SELECT * FROM business_conf WHERE conf_key = ? `
+	err = orm.NewOrmUsingDB("eta").Raw(sql, key).QueryRow(&item)
+	return
+}
+
+// GetBusinessConf 获取商家配置
+func GetBusinessConf() (list map[string]string, err error) {
+	list = make(map[string]string)
+
+	var items []*BusinessConf
+	sql := `SELECT * FROM business_conf`
+	_, err = orm.NewOrmUsingDB("eta").Raw(sql).QueryRows(&items)
+	if err != nil {
+		return
+	}
+
+	for _, v := range items {
+		if v.ValType == 4 {
+			list[v.ConfKey] = html.UnescapeString(v.ConfVal)
+			continue
+		}
+		list[v.ConfKey] = v.ConfVal
+	}
+	return
+}
+
+func InitBusinessConf() {
+	var e error
+	BusinessConfMap, e = GetBusinessConf()
+	if e != nil {
+		return
+	}
+
+	// 图表有效期的过期时间
+	if BusinessConfMap[BusinessConfReportChartExpiredTime] != "" {
+		reportChartExpiredTime, _ := strconv.Atoi(BusinessConfMap[BusinessConfReportChartExpiredTime])
+		if reportChartExpiredTime <= 0 {
+			reportChartExpiredTime = 30
+		}
+		utils.BusinessConfReportChartExpiredTime = time.Duration(reportChartExpiredTime) * time.Minute
+	} else {
+		utils.BusinessConfReportChartExpiredTime = 30 * time.Minute
+	}
+}

+ 14 - 0
models/db.go

@@ -23,6 +23,13 @@ func init() {
 	report_db, _ := orm.GetDB("rddp")
 	report_db.SetConnMaxLifetime(10 * time.Minute)
 
+	_ = orm.RegisterDataBase("eta", "mysql", utils.MYSQL_URL_ETA)
+	orm.SetMaxIdleConns("eta", 50)
+	orm.SetMaxOpenConns("eta", 100)
+
+	etaDb, _ := orm.GetDB("eta")
+	etaDb.SetConnMaxLifetime(10 * time.Minute)
+
 	orm.Debug = true
 	orm.DebugLog = orm.NewLog(utils.BinLog)
 
@@ -37,4 +44,11 @@ func init() {
 		//new(UserTemplateRecord),
 		new(UserReadRecord),
 	)
+
+	// 初始化后的操作
+	afterInitDb()
+}
+
+func afterInitDb() {
+	InitBusinessConf()
 }

+ 9 - 0
routers/commentsRouter.go

@@ -97,6 +97,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"] = append(beego.GlobalControllerRouter["eta/eta_mini_api/controllers:UserController"],
+        beego.ControllerComments{
+            Method: "SendSms",
+            Router: `/send_sms`,
+            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: "Login",

+ 186 - 0
services/report.go

@@ -1 +1,187 @@
 package services
+
+import (
+	"eta/eta_mini_api/models"
+	"eta/eta_mini_api/services/go_redis"
+	"eta/eta_mini_api/utils"
+	"fmt"
+	html2 "golang.org/x/net/html"
+	"net/url"
+	"strings"
+	"time"
+)
+
+// HandleReportContent
+// @Description: 处理报告内容(动态图表/表格添加授权token)
+// @author: Roc
+// @datetime 2025-01-07 10:03:15
+// @param body string
+// @return newBody string
+func HandleReportContent(body string, opType string, tokenMap map[string]string) (newBody string) {
+	if body == `` {
+		return
+	}
+	newBody = body
+
+	// 解析HTML
+	doc, err := html2.Parse(strings.NewReader(body))
+	if err != nil {
+		fmt.Println("Error parsing HTML:", err)
+		return
+	}
+
+	replaceIframeSrc(doc, opType, tokenMap)
+
+	// 输出修改后的HTML
+	var modifiedHtml strings.Builder
+	err = html2.Render(&modifiedHtml, doc)
+	if err != nil {
+		fmt.Println("Error rendering HTML:", err)
+		return
+	}
+
+	newBody = modifiedHtml.String()
+	fmt.Println(newBody)
+
+	return
+}
+
+// replaceIframeSrc 遍历HTML节点,替换iframe的src属性
+func replaceIframeSrc(n *html2.Node, opType string, tokenMap map[string]string) {
+	if n.Type == html2.ElementNode && n.Data == "iframe" {
+		for i, attr := range n.Attr {
+			if attr.Key == "src" {
+				newLink := attr.Val
+				// 处理链接
+				switch opType {
+				case `add`:
+					newLink = linkAddToken(attr.Val, tokenMap)
+				case `del`:
+					newLink = linkDelToken(attr.Val)
+				}
+				// 替换原来的链接
+				n.Attr[i].Val = newLink
+				break
+			}
+		}
+	}
+	// 递归处理子节点
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		replaceIframeSrc(c, opType, tokenMap)
+	}
+}
+
+// linkAddToken 链接添加token
+func linkAddToken(link string, tokenMap map[string]string) string {
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(fmt.Sprintf("linkAddToken: 处理链接失败, %v", err))
+		}
+	}()
+	parsedURL, err := url.Parse(link)
+	if err != nil {
+		return link
+	}
+
+	// 获取查询参数
+	queryParams := parsedURL.Query()
+
+	// 先移除authToken参数,避免莫名其妙的这个值入库了
+	queryParams.Del("authToken")
+
+	// 获取code参数
+	code := queryParams.Get("code")
+	if code == "" {
+		return link
+	}
+
+	showType := `chart`
+	if strings.Contains(parsedURL.Path, "sheetshow") {
+		showType = `excel`
+	}
+
+	// 避免报告里面一个图表/表格重复生成token
+	key := fmt.Sprint(showType, `:`, code)
+	if tokenMap != nil {
+		if token, ok := tokenMap[key]; ok {
+			// 在链接后面添加一个token值
+			return link + "&authToken=" + token
+		}
+	}
+
+	token, err := GeneralChartToken(showType, code)
+	if err != nil {
+		return link
+	}
+
+	if tokenMap != nil {
+		tokenMap[key] = token
+	}
+
+	// 在链接后面添加一个token值
+	return link + "&authToken=" + token
+}
+
+// linkDelToken 链接添加token
+func linkDelToken(link string) string {
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(fmt.Sprintf("linkDelToken: 处理链接失败, %v", err))
+		}
+	}()
+	parsedURL, err := url.Parse(link)
+	if err != nil {
+		return link
+	}
+
+	// 获取查询参数
+	queryParams := parsedURL.Query()
+
+	// 移除authToken参数
+	queryParams.Del("authToken")
+
+	// 更新URL的查询参数
+	parsedURL.RawQuery = queryParams.Encode()
+
+	return parsedURL.String()
+}
+
+// GeneralChartToken
+// @Description: 生产图表/表格授权token
+// @author: Roc
+// @datetime 2025-01-07 10:41:36
+// @param showType string
+// @param uniqueCode string
+// @return token string
+// @return err error
+func GeneralChartToken(showType, uniqueCode string) (token string, err error) {
+	// 缓存key
+	token = utils.MD5(fmt.Sprint(showType+`:`, uniqueCode, time.Now().UnixNano()/1e6))
+	key := fmt.Sprint(utils.CACHE_CHART_AUTH, token)
+	go_redis.SetNX(key, uniqueCode, utils.BusinessConfReportChartExpiredTime)
+	return
+}
+
+// CheckIsOpenChartExpired
+// @Description: 判断是否开启图表授权
+// @author: Roc
+// @datetime 2025-01-07 16:51:34
+// @return isOpenChartExpired bool
+// @return err error
+func CheckIsOpenChartExpired() (isOpenChartExpired bool, err error) {
+	obj := new(models.BusinessConf)
+	item, e := obj.GetItemByConfKey(models.BusinessConfIsOpenChartExpired)
+	if e != nil {
+		err = fmt.Errorf("CheckIsOpenChartExpired, %v", e)
+		return
+	}
+	if item.Id <= 0 {
+		return
+	}
+	if item.ConfVal == `true` {
+		isOpenChartExpired = true
+	}
+	return
+}

+ 5 - 0
utils/config.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"strconv"
+	"time"
 
 	beeLogger "github.com/beego/bee/v2/logger"
 	"github.com/beego/beego/v2/server/web"
@@ -16,6 +17,7 @@ var (
 	RunMode          string
 	MYSQL_URL_MASTER string
 	MYSQL_URL_RDDP   string
+	MYSQL_URL_ETA    string
 
 	REDIS_CACHE string        //缓存地址
 	Redis       *redis.Client //redis链接
@@ -51,6 +53,8 @@ var SmsApiUrl string // 金瑞短信API调用地址
 
 var DesKey string // 接口返回加密KEY
 
+var BusinessConfReportChartExpiredTime time.Duration //图表有效期鉴权时间,单位:分钟
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {
@@ -76,6 +80,7 @@ func init() {
 	beeLogger.Log.Info(RunMode + " 模式")
 	MYSQL_URL_RDDP = config["mysql_url_rddp"]
 	MYSQL_URL_MASTER = config["mysql_url_master"]
+	MYSQL_URL_ETA = config["mysql_url_eta"]
 
 	SMS_TPLID = config["sms_tplId"]
 	DesKey = config["des_key"]

+ 4 - 0
utils/constants.go

@@ -75,3 +75,7 @@ const (
 const (
 	CACHE_WX_ACCESS_TOKEN_DW = "wx:accesstoken:dw" //东吴公众号 微信accessToken
 )
+
+const (
+	CACHE_CHART_AUTH = "eta:chart:auth:" //图表数据授权
+)