Browse Source

no message

zhangchuanxing 1 week ago
parent
commit
b64ea9894a
4 changed files with 294 additions and 0 deletions
  1. 22 0
      models/open_api_user.go
  2. 259 0
      services/ficc_report.go
  3. 9 0
      utils/config.go
  4. 4 0
      utils/constants.go

+ 22 - 0
models/open_api_user.go

@@ -0,0 +1,22 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type OpenApiUser struct {
+	Appid      string `orm:"column(appid);pk" json:"appid" description:"开放平台appid"`
+	Secret     string `orm:"column(secret);" json:"secret" description:"开放平台秘钥"`
+	Ip         string `orm:"column(ip);" json:"ip" description:"限制请求来源ip,多个ip用英文,隔开"`
+	Remark     string `orm:"column(remark);" json:"remark" description:"备注信息"`
+	CreateTime string `orm:"column(create_time);" json:"create_time" description:"创建时间"`
+	ModifyTime string `orm:"column(modify_time);" json:"modify_time" description:"最近一次更新时间"`
+}
+
+// GetByAppid 根据appid获取开放api用户信息
+func GetByAppid(appid string) (item *OpenApiUser, err error) {
+	o := orm.NewOrmUsingDB("weekly_report")
+	sql := `SELECT * FROM open_api_user WHERE appid=? LIMIT 1`
+	err = o.Raw(sql, appid).QueryRow(&item)
+	return
+}

+ 259 - 0
services/ficc_report.go

@@ -3,13 +3,20 @@ package services
 //FICC研报
 import (
 	"context"
+	"crypto/md5"
 	"encoding/json"
 	"errors"
 	"fmt"
+	html2 "golang.org/x/net/html"
 	"hongze/hongze_cygx/models"
 	"hongze/hongze_cygx/models/ficc_report"
 	"hongze/hongze_cygx/utils"
 	"html"
+	"io/ioutil"
+	"math"
+	"net/http"
+	"net/url"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -795,3 +802,255 @@ func UpdateCygxZhouqiArticleMapTime(fieldName string) {
 
 	return
 }
+
+// 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("处理链接失败,ERR:" + err.Error())
+		}
+	}()
+	if link == `` {
+		return link
+	}
+	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
+	}
+	link = strings.Replace(link, "?code="+code, "?code="+code+"&authToken="+token, -1)
+	// 在链接后面添加一个token值
+	return link
+}
+
+type GeneralChartTokenReq struct {
+	Appid      string `description:"APPID" json:"appid"`
+	UniqueCode string `description:"图表code" json:"unique_code"`
+	Source     string `description:"来源,图表:chart,表格:table" json:"source"`
+}
+
+type HzOpenApiResponse struct {
+	Code   int    `json:"code"`
+	Data   string `json:"data"`
+	Msg    string `json:"msg"`
+	ErrMsg string `json:"err_msg"`
+}
+
+// GeneralChartToken
+func GeneralChartToken(source, uniqueCode string) (token string, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go utils.SendAlarmMsg(fmt.Sprint("GeneralChartToken 失败,GeneralChartToken Err:"+err.Error()+"uniqueCode", uniqueCode), 3)
+		}
+	}()
+	postData := make(map[string]string)
+	postData["appid"] = utils.CLPT_APPID
+	postData["nonce_str"] = utils.GetRandDigit(10)
+	postData["timestamp"] = fmt.Sprint(time.Now().Unix())
+	postData["unique_code"] = uniqueCode
+	postData["source"] = source
+
+	sign, e := getSign(postData)
+	if e != nil {
+		fmt.Println(e)
+		err = errors.New("getSign, Err: " + e.Error())
+		return
+	}
+	postData["sign"] = sign
+
+	url := utils.HZ_OPEN_API_URL + "api/chart/general_token"
+	method := "POST"
+	data, e := json.Marshal(postData)
+	if e != nil {
+		err = errors.New("data json marshal err: " + e.Error())
+		return
+	}
+	payload := strings.NewReader(string(data))
+	client := &http.Client{}
+	req, e := http.NewRequest(method, url, payload)
+	if e != nil {
+		err = errors.New(" http.NewRequest err: " + e.Error())
+		return
+	}
+	req.Header.Add("Content-Type", "application/json")
+
+	res, e := client.Do(req)
+	if e != nil {
+		err = errors.New(" client.Do err: " + e.Error())
+		return
+	}
+	defer res.Body.Close()
+	var response *HzOpenApiResponse
+	body, e := ioutil.ReadAll(res.Body)
+	if e != nil {
+		err = errors.New("  ioutil.ReadAll: " + e.Error())
+		return
+	}
+	e = json.Unmarshal(body, &response)
+	if e != nil {
+		err = errors.New("json.Unmarshal " + e.Error())
+		return
+	}
+	//fmt.Println(response.Code)
+	token = response.Data
+	return
+}
+
+// 请求参数签名校验
+func getSign(postData map[string]string) (sign string, err error) {
+	appid := postData["appid"]
+	if appid == "" {
+		err = errors.New("参数异常,缺少appid")
+		return
+	}
+	openApiUserInfo, tmpErr := models.GetByAppid(appid)
+	if tmpErr != nil {
+		if tmpErr.Error() == utils.ErrNoRow() {
+			err = errors.New("appid异常,请联系管理员")
+		} else {
+			err = errors.New("系统异常,请联系管理员")
+		}
+		return
+	}
+
+	if openApiUserInfo == nil {
+		err = errors.New("系统异常,请联系管理员")
+		return
+	}
+
+	if postData["nonce_str"] == "" {
+		err = errors.New("参数异常,缺少随机字符串")
+		return
+	}
+	if postData["timestamp"] == "" {
+		err = errors.New("参数异常,缺少时间戳")
+		return
+	} else {
+		timeUnix := time.Now().Unix() //当前格林威治时间,int64类型
+		//将接口传入的时间做转换
+		timestamp, timeErr := strconv.ParseInt(postData["timestamp"], 10, 64)
+		if timeErr != nil {
+			err = errors.New("参数异常,时间戳格式异常")
+			return
+		}
+		if math.Abs(float64(timeUnix-timestamp)) > 300 {
+			err = errors.New("当前时间异常,请调整设备时间与北京时间一致")
+			return
+		}
+	}
+
+	//先取出除sign外的所有的提交的参数key
+	var keys []string
+	for k := range postData {
+		if k != "sign" {
+			keys = append(keys, k)
+		}
+	}
+
+	//1,根据参数名称的ASCII码表的顺序排序
+	sort.Strings(keys)
+
+	//2 根据排序后的参数名称,取出对应的值,并拼接字符串
+	var signStr string
+	for _, v := range keys {
+		signStr += v + "=" + postData[v] + "&"
+	}
+	sign = strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(signStr+"secret="+openApiUserInfo.Secret))))
+
+	return
+}

+ 9 - 0
utils/config.go

@@ -102,6 +102,7 @@ var (
 	YiDongHuaWeiYunUrl        string //易董 华为云请求域名
 	YiDonggetOriginalLink     string //易董 短连接转为长链接
 	ShangHaiCrmApiLink        string //上海CRM用户同步api调用地址
+	HZ_OPEN_API_URL           string //弘则openapi地址
 
 	ZHOU_QI_ID int // 行业周期的ID
 )
@@ -382,6 +383,14 @@ func FiccYbEtaHub() {
 	}
 }
 
+func OpenapiUrl() {
+	if RunMode == "release" {
+		HZ_OPEN_API_URL = "https://openapi.hzinsights.com/"
+	} else {
+		HZ_OPEN_API_URL = "http://8.136.199.33:8608/"
+	}
+}
+
 //
 //YiDongZhengTongYunUrl     string //易董 证通云请求域名
 //YiDongZhengTongYunAppid   string //易董 证通云请求appid

+ 4 - 0
utils/constants.go

@@ -329,3 +329,7 @@ const (
 	key        = "zDeESsxsXuionhqSLZYHWcDJ" //全局加密KEY
 	REPORT_KEY = "Zdhuef3ajCLTpfGLn10cOJLJ" //报告加密Key
 )
+
+const (
+	CLPT_APPID = "XVuGlcyEEVNYVWx5" //策略平台APPID
+)