|
@@ -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
|
|
|
+}
|