Ver Fonte

Merge branch 'master' into feature/eta2.2.6_material

# Conflicts:
#	controllers/data_manage/excel/balance_table.go
#	models/data_manage/excel/excel_info.go
#	models/data_manage/excel/response/excel_info.go
#	routers/commentsRouter.go
xyxie há 3 meses atrás
pai
commit
69d44898bd

+ 36 - 0
controllers/data_manage/edb_info.go

@@ -3895,6 +3895,42 @@ func (this *ChartInfoController) EdbInfoReplace() {
 		br.ErrMsg = "原指标频度为:" + oldEdbInfo.Frequency + " 替换指标频度为:" + newEdbInfo.Frequency + ",频度不同,不可进行替换操作!"
 		return
 	}
+	if oldEdbInfo.EdbInfoType == 1 || newEdbInfo.EdbInfoType == 1 {
+		br.Msg = "预测指标不允许替换"
+		br.ErrMsg = "预测指标不允许替换"
+		return
+	}
+
+	// 判断指标是否循环引用
+	if oldEdbInfo.EdbType == 2 {
+		// 查询该计算指标的所有相关指标,如果相关指标中包含新指标,则提示
+		hasRelation, e := data.CheckTwoEdbInfoRelation(oldEdbInfo, newEdbInfo)
+		if e != nil {
+			br.Msg = "替换失败!"
+			br.ErrMsg = "查询指标引用关系失败,Err:" + e.Error()
+			return
+		}
+		if hasRelation {
+			br.Msg = "原指标与替换指标存在引用关系,不允许替换"
+			br.ErrMsg = "原指标与替换指标存在引用关系,不允许替换"
+			return
+		}
+	}
+
+	if newEdbInfo.EdbType == 2 {
+		// 查询该计算指标的所有相关指标,如果相关指标中包含新指标,则提示
+		hasRelation, e := data.CheckTwoEdbInfoRelation(newEdbInfo, oldEdbInfo)
+		if e != nil {
+			br.Msg = "替换失败!"
+			br.ErrMsg = "查询指标引用关系失败,Err:" + e.Error()
+			return
+		}
+		if hasRelation {
+			br.Msg = "原指标与替换指标存在引用关系,不允许替换"
+			br.ErrMsg = "原指标与替换指标存在引用关系,不允许替换"
+			return
+		}
+	}
 
 	sysAdminId := sysUser.AdminId
 	//replaceChartTotal, replaceCalculateTotal, err := data.EdbInfoReplace(oldEdbInfo, newEdbInfo, sysAdminId, sysUser.RealName)

+ 229 - 14
controllers/data_manage/excel/balance_table.go

@@ -1,6 +1,7 @@
 package excel
 
 import (
+	"archive/zip"
 	"encoding/json"
 	"errors"
 	"eta/eta_mobile/models"
@@ -11,8 +12,12 @@ import (
 	"eta/eta_mobile/services/data"
 	"eta/eta_mobile/services/data/data_manage_permission"
 	excelService "eta/eta_mobile/services/data/excel"
+	excel2 "eta/eta_mobile/services/excel"
 	"eta/eta_mobile/utils"
 	"fmt"
+	"github.com/tealeg/xlsx"
+	"io/ioutil"
+	"os"
 	"time"
 )
 
@@ -240,6 +245,50 @@ func refreshBalanceTable(excelDetail response.ExcelInfoDetail, lang string) (err
 	return
 }
 
+// GetChildTable
+// @Title 获取子表
+// @Description 获取子表
+// @Param	request	body request.MixedTableCellDataReq true "type json string"
+// @router /excel_info/child_table [get]
+func (c *ExcelInfoController) GetChildTable() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	parentId, _ := c.GetInt("ParentId")
+	if parentId <= 0 {
+		br.Msg = "请选择父表"
+		return
+	}
+	list := make([]*excel.ExcelInfo, 0)
+	// 查询所有子表
+	childList, err := excel.GetChildExcelInfoByParentId(parentId)
+	if err != nil {
+		br.Msg = "查询子表失败"
+		return
+	}
+	if len(childList) > 0 {
+		list = childList
+	}
+
+	resp := &response.BalanceChildTableResp{List: list}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = resp
+}
+
 // BalanceVersionList
 // @Title 查询平衡表版本号列表
 // @Description 查询平衡表版本号列表
@@ -319,12 +368,12 @@ func (c *ExcelInfoController) BalanceVersionList() {
 	br.Msg = "操作成功"
 }
 
-// GetChildTable
-// @Title 获取
-// @Description 获取
+// GetBalanceChartList
+// @Title 获取平衡表表关联的图
+// @Description 获取平衡表表关联的图
 // @Param	request	body request.MixedTableCellDataReq true "type json string"
-// @router /excel_info/child_table [get]
-func (c *ExcelInfoController) GetChildTable() {
+// @router /excel_info/balance/chart_list [get]
+func (c *ExcelInfoController) GetBalanceChartList() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		c.Data["json"] = br
@@ -339,26 +388,192 @@ func (c *ExcelInfoController) GetChildTable() {
 		return
 	}
 
-	parentId, _ := c.GetInt("ParentId")
-	if parentId <= 0 {
-		br.Msg = "请选择表"
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择平衡表"
 		return
 	}
-	list := make([]*excel.ExcelInfo, 0)
 	// 查询所有子表
-	childList, err := excel.GetChildExcelInfoByParentId(parentId)
+	excelInfo, err := excel.GetExcelInfoById(excelInfoId)
 	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在"
+			return
+		}
 		br.Msg = "查询子表失败"
+		br.ErrMsg = "查询子表失败,Err:" + err.Error()
 		return
 	}
-	if len(childList) > 0 {
-		list = childList
+	list := make([]*data_manage.BalanceChartInfoDetailResp, 0)
+	chartInfoList, mappingListMap, dataListMap, err, errMsg := excelService.GetBalanceExcelChartList(excelInfo, "")
+	if err != nil {
+		if errMsg != "" {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		} else {
+			br.Msg = "查询图表失败"
+			br.ErrMsg = "查询图表失败,Err:" + err.Error()
+		}
+		return
 	}
+	for _, chartInfo := range chartInfoList {
+		mappingList, ok := mappingListMap[chartInfo.ChartInfoId]
+		if !ok {
+			br.Msg = "未找到图表关联的指标信息"
+			return
+		}
+		var chartInfoResp *data_manage.ChartInfoDetailResp
+		chartInfoResp, err, errMsg = data.GetBalanceExcelChartDetail(chartInfo, mappingList, sysUser, dataListMap)
+		if err != nil {
+			br.Msg = "查询图表详情失败"
+			br.ErrMsg = "查询图表详情失败,Err:" + err.Error()
+			return
+		}
+		chartEdbList := make([]*data_manage.ExcelChartEdbView, 0)
+		for _, v := range mappingList {
+			tmp := &data_manage.ExcelChartEdbView{
+				ExcelChartEdbId: v.ExcelChartEdbId,
+				DateSequenceStr: v.DateSequence,
+				DataSequenceStr: v.DataSequence,
+				FromTag:         v.FromTag,
+			}
+			chartEdbList = append(chartEdbList, tmp)
+		}
 
-	resp := &response.BalanceChildTableResp{List: list}
+		balanceChartInfoResp := &data_manage.BalanceChartInfoDetailResp{
+			ChartInfoDetailResp: chartInfoResp,
+			ExcelEdbList:        chartEdbList,
+		}
+		list = append(list, balanceChartInfoResp)
+	}
+
+	ret := &data_manage.BalanceTableChartListResp{List: list}
 
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "查询成功"
-	br.Data = resp
+	br.Data = ret
+}
+
+func downloadBalanceTable(excelInfo *excel.ExcelInfo, lang string) (savePath, zipName string, uploadDir string, err error, errMsg string) {
+	dateDir := time.Now().Format("20060102")
+	randStr := time.Now().Format(utils.FormatDateTimeUnSpace)
+	uploadDir = "static/xls/" + dateDir + "/" + randStr
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		return
+	}
+	fileList := make([]string, 0)
+	if excelInfo.ParentId != 0 && excelInfo.BalanceType != 0 {
+		errMsg = "平衡表类型错误"
+		err = fmt.Errorf("平衡表类型错误 ")
+		return
+	}
+	//复制静态表
+	staticCondition := " AND parent_id = 0 AND balance_type = 1 AND rel_excel_info_id=? "
+	var staticPars []interface{}
+	staticPars = append(staticPars, excelInfo.ExcelInfoId)
+	excelList, err := excel.GetExcelInfoListByCondition(staticCondition, staticPars)
+	if err != nil {
+		errMsg = "获取表格失败"
+		err = fmt.Errorf("获取表格失败 %s", err.Error())
+		return
+	}
+	excelList = append(excelList, excelInfo)
+	for _, staticExcelInfo := range excelList {
+		cCondition := " AND parent_id = ?"
+		var cPars []interface{}
+		cPars = append(cPars, staticExcelInfo.ExcelInfoId)
+		childList, e := excel.GetExcelInfoListByCondition(cCondition, cPars)
+		if e != nil {
+			errMsg = "获取子表失败"
+			err = fmt.Errorf("获取子表失败 %s", err.Error())
+			return
+		}
+		xlsxFile := xlsx.NewFile()
+		fileName := staticExcelInfo.ExcelName + ".xlsx"
+		fpath := uploadDir + "/" + fileName
+		for _, childExcelInfo := range childList {
+			var result request.MixedTableReq
+			err = json.Unmarshal([]byte(childExcelInfo.Content), &result)
+			if err != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf("表格json转结构体失败,Err:" + err.Error())
+				return
+			}
+			newResult, er, msg := excelService.GetMixedTableCellData(result, lang)
+			if er != nil {
+				err = er
+				errMsg = msg
+				return
+			}
+			tableData, er := excel2.GetTableDataByMixedTableData(newResult, false)
+			if er != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf("转换成table失败,Err:" + err.Error())
+				return
+			}
+			tableData, err = excel2.HandleRuleToTableCell(childExcelInfo.ExcelInfoId, tableData)
+			if err != nil {
+				errMsg = "获取失败"
+				err = fmt.Errorf("处理条件格式管理规则失败,Err:%w", err)
+				return
+			}
+			// 将单个sheet的数据写入到excel
+			err = tableData.WriteExcelSheetData(xlsxFile, childExcelInfo.ExcelName)
+			if err != nil {
+				return
+			}
+		}
+		//处理excel文件
+		//return
+		err = xlsxFile.Save(fpath)
+		if err != nil {
+			return
+		}
+		fileList = append(fileList, fileName)
+	}
+	// 创建zip
+	zipName = excelInfo.ExcelName + ".zip"
+
+	savePath = uploadDir + "/" + zipName
+	fmt.Println(savePath)
+	zipFile, err := os.Create(savePath)
+	if err != nil {
+		return
+	}
+	zipWriter := zip.NewWriter(zipFile)
+	// 生成zip过程中报错关闭
+	defer func() {
+		if err != nil {
+			zipWriter.Close()
+			zipFile.Close()
+		}
+		//os.Remove(savePath)
+	}()
+	//写入zip
+	for i := 0; i < len(fileList); i++ {
+		ioWriter, e := zipWriter.Create(fileList[i])
+		if e != nil {
+			err = fmt.Errorf("创建zip失败,Err:" + e.Error())
+			if os.IsPermission(e) {
+				fmt.Println("权限不足: ", e)
+				return
+			}
+			return
+		}
+		fullPath := uploadDir + "/" + fileList[i]
+		content, e := ioutil.ReadFile(fullPath)
+		if e != nil {
+			err = fmt.Errorf("读取文件失败,Err:" + e.Error())
+			return
+		}
+		ioWriter.Write(content)
+	}
+	// 生成zip后关闭,否则下载文件会损坏
+	zipWriter.Close()
+	zipFile.Close()
+	//this.Ctx.Output.Download(downLoadnFilePath, downloadFileName)
+	return
 }

+ 21 - 0
controllers/data_manage/excel/excel_info.go

@@ -2299,6 +2299,27 @@ func (c *ExcelInfoController) Download() {
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			return
 		}
+	case utils.BALANCE_TABLE: // 混合表格
+		savePath, fileName, uploadDir, err, errMsg := downloadBalanceTable(excelInfo, c.Lang)
+		if err != nil {
+			br.Msg = "下载失败"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
+			return
+		}
+		defer func() {
+			_ = os.RemoveAll(uploadDir)
+		}()
+		// todo 删除文件
+		c.Ctx.Output.Download(savePath, fileName)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = excelInfo
+		return
+
 	}
 
 	downloadFilePath, err := tableData.ToExcel()

+ 72 - 51
controllers/data_manage/future_good/future_good_chart_info.go

@@ -923,7 +923,7 @@ func (this *FutureGoodChartInfoController) ChartEnInfoEdit() {
 	switch chartItem.Source {
 	case utils.CHART_SOURCE_FUTURE_GOOD:
 		err = data_manage.EditFutureGoodChartEnInfoAndEdbEnInfo(req.ChartInfoId, req.ChartNameEn, edbInfo.EdbInfoId, req.EdbNameEn, req.UnitEn)
-		if req.FutureGoodNameEn != `` {
+		/*if req.FutureGoodNameEn != `` {
 			futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
 			if err != nil {
 				br.Msg = "修改失败"
@@ -959,7 +959,7 @@ func (this *FutureGoodChartInfoController) ChartEnInfoEdit() {
 				}
 				v.Update([]string{"FutureGoodEdbNameEn"})
 			}
-		}
+		}*/
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		err = data_manage.EditFutureGoodProfitChartEnInfoAndEdbEnInfo(req.ChartInfoId, req.ChartNameEn, edbInfo.EdbInfoId, req.EdbNameEn, req.UnitEn, req.ProfitNameEn)
 	default:
@@ -1400,6 +1400,29 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 				}
 			}
 		}
+		if v.Source == 0 {
+			name := strings.Split(v.EdbName, "(")
+			if barConfig.FutureGoodEdbName != "" {
+				name[0] = barConfig.FutureGoodEdbName
+			}
+			v.EdbName = name[0]
+			if len(name) > 1 {
+				v.EdbName = v.EdbName + "(" + name[1]
+			}
+			//英文
+			// 编译正则表达式,匹配一个或多个数字
+
+			if v.EdbNameEn != "" {
+				name = strings.Split(v.EdbNameEn, "(")
+				if barConfig.FutureGoodEdbNameEn != "" {
+					name[0] = barConfig.FutureGoodEdbNameEn
+				}
+				v.EdbNameEn = name[0]
+				if len(name) > 1 {
+					v.EdbNameEn = v.EdbNameEn + "(" + name[1]
+				}
+			}
+		}
 	}
 	if len(warnEdbList) > 0 {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
@@ -1731,6 +1754,29 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 				}
 			}
 		}
+		if v.Source == 0 {
+			name := strings.Split(v.EdbName, "(")
+			if barConfig.FutureGoodEdbName != "" {
+				name[0] = barConfig.FutureGoodEdbName
+			}
+			v.EdbName = name[0]
+			if len(name) > 1 {
+				v.EdbName = v.EdbName + "(" + name[1]
+			}
+			//英文
+			// 编译正则表达式,匹配一个或多个数字
+
+			if v.EdbNameEn != "" {
+				name = strings.Split(v.EdbNameEn, "(")
+				if barConfig.FutureGoodEdbNameEn != "" {
+					name[0] = barConfig.FutureGoodEdbNameEn
+				}
+				v.EdbNameEn = name[0]
+				if len(name) > 1 {
+					v.EdbNameEn = v.EdbNameEn + "(" + name[1]
+				}
+			}
+		}
 	}
 	if len(warnEdbList) > 0 {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
@@ -3272,6 +3318,22 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			}
 			xDataList := barConfig.XDataList
 			length := len(xDataList)
+			oldFutureEdbName := barConfig.FutureGoodEdbName
+			oldFutureEdbNameEn := barConfig.FutureGoodEdbNameEn
+			if oldFutureEdbName == "" {
+				futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
+				if err != nil {
+					br.Msg = "修改失败"
+					br.ErrMsg = "获取图表现货价格指标信息失败,指标信息失败,Err:" + err.Error()
+					return
+				}
+				list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfoMapping.EdbInfoId)
+				for _, v := range list {
+					oldFutureEdbName = v.FutureGoodEdbName
+					oldFutureEdbNameEn = v.FutureGoodEdbNameEn
+					break
+				}
+			}
 			switch this.Lang {
 			case utils.EnLangVersion:
 				for k, v := range req.XDataList {
@@ -3285,6 +3347,10 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 						}
 					}
 				}
+				if req.FutureGoodName != `` {
+					barConfig.FutureGoodEdbNameEn = req.FutureGoodName
+					barConfig.FutureGoodEdbName = oldFutureEdbName
+				}
 			default:
 				for k, v := range req.XDataList {
 					v = strings.TrimPrefix(v, " ")
@@ -3297,6 +3363,10 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 						}
 					}
 				}
+				if req.FutureGoodName != `` {
+					barConfig.FutureGoodEdbName = req.FutureGoodName
+					barConfig.FutureGoodEdbNameEn = oldFutureEdbNameEn
+				}
 			}
 			barConfig.XDataList = xDataList
 			barConfigByte, e := json.Marshal(barConfig)
@@ -3308,55 +3378,6 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			chartItem.BarConfig = string(barConfigByte)
 		}
 		err = data_manage.EditBaseFutureGoodChartInfoAndEdbEnInfo(chartItem, &req, this.Lang)
-		if req.FutureGoodName != `` {
-			futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
-			if err != nil {
-				br.Msg = "修改失败"
-				br.ErrMsg = "获取图表现货价格指标信息失败,指标信息失败,Err:" + err.Error()
-				return
-			}
-			futureGoodEdbInfo, err := future_good.GetFutureGoodEdbInfo(futureGoodEdbInfoMapping.EdbInfoId)
-			if err != nil {
-				if err.Error() == utils.ErrNoRow() {
-					br.Msg = "图表不存在!"
-					br.ErrMsg = "图表指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
-					return
-				} else {
-					br.Msg = "获取图表信息失败!"
-					br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
-					return
-				}
-			}
-			if futureGoodEdbInfo == nil {
-				br.Msg = "期货商品指标不存在!"
-				br.ErrMsg = "期货商品指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
-				return
-			}
-
-			list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfo.FutureGoodEdbInfoId)
-			for _, v := range list {
-				switch this.Lang {
-				case utils.EnLangVersion:
-					if v.FutureGoodEdbNameEn == `` {
-						v.FutureGoodEdbNameEn = strings.TrimPrefix(req.FutureGoodName, " ")
-						v.FutureGoodEdbNameEn = strings.TrimSuffix(req.FutureGoodName, " ")
-					} else {
-						v.FutureGoodEdbNameEn = strings.TrimPrefix(strings.Replace(v.FutureGoodEdbNameEn, v.FutureGoodEdbNameEn, req.FutureGoodName, -1), " ")
-						v.FutureGoodEdbNameEn = strings.TrimSuffix(req.FutureGoodName, " ")
-					}
-					v.Update([]string{"FutureGoodEdbNameEn"})
-				default:
-					if v.FutureGoodEdbName == `` {
-						v.FutureGoodEdbName = strings.TrimPrefix(req.FutureGoodName, " ")
-						v.FutureGoodEdbName = strings.TrimSuffix(req.FutureGoodName, " ")
-					} else {
-						v.FutureGoodEdbName = strings.TrimPrefix(strings.Replace(v.FutureGoodEdbName, v.FutureGoodEdbName, req.FutureGoodName, -1), " ")
-						v.FutureGoodEdbName = strings.TrimSuffix(req.FutureGoodName, " ")
-					}
-					v.Update([]string{"FutureGoodEdbName"})
-				}
-			}
-		}
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		if len(req.XDataList) > 0 {
 			// 处理横轴名称

+ 68 - 141
controllers/english_report/report.go

@@ -16,7 +16,6 @@ import (
 	"html"
 	"strconv"
 	"strings"
-	"sync"
 	"time"
 )
 
@@ -449,143 +448,70 @@ func (this *EnglishReportController) ListReport() {
 			return
 		}
 	}
-	// 未群发邮件(包含推送邮件失败的)
-	if emailState == 1 {
-		failIds, e := models.GetHasFailEmailLogReportIds()
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取存在邮件推送失败记录的英文报告IDs失败, Err:" + e.Error()
-			return
-		}
-		condition += ` AND email_state = 0`
-		if len(failIds) > 0 {
-			condition += ` OR id IN (` + utils.GetOrmInReplace(len(failIds)) + `)`
-			pars = append(pars, failIds)
-		}
-	}
-	// 已群发邮件
-	if emailState == 2 {
-		successIds, e := models.GetSuccessEmailLogReportIds()
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取邮件推送记录均为成功的英文报告IDs失败, Err:" + e.Error()
-			return
+
+	// 群发邮件状态筛选
+	{
+		// 未群发邮件(包含推送邮件失败的)
+		if emailState == 1 {
+			condition += ` AND (email_state = 0 OR email_has_fail = 1) `
 		}
-		condition += ` AND email_state = 1`
-		if len(successIds) > 0 {
-			condition += ` AND id IN (` + utils.GetOrmInReplace(len(successIds)) + `)`
-			pars = append(pars, successIds)
+		// 已群发邮件
+		if emailState == 2 {
+			condition += ` AND email_state = 1 AND email_has_fail = 0 `
 		}
 	}
 
-	var total int
-	var errCount, errList, errOther error
 	var authOk bool
-	list := make([]*models.EnglishReportList, 0)
-	failMap := make(map[int]bool, 0)    // 有群发失败记录的研报
 	adminMap := make(map[int]string, 0) // 编辑中的研究员姓名
+	total, e := models.GetEnglishReportListCount(condition, pars, companyType)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文研报Count失败, Err: " + e.Error()
+		return
+	}
 
-	wg := sync.WaitGroup{}
-	wg.Add(3)
-
-	// 列表总数
-	go func() {
-		defer func() {
-			wg.Done()
-		}()
-
-		t, e := models.GetEnglishReportListCount(condition, pars, companyType)
-		if e != nil {
-			errCount = fmt.Errorf("获取英文研报Count失败, Err: %s", e.Error())
-			return
-		}
-		total = t
-	}()
-
-	// 列表数据
-	go func() {
-		defer func() {
-			wg.Done()
-		}()
-
-		// 限制一下富文本字段, 列表用不到
-		fieldArr := []string{
-			"id", "add_type", "classify_id_first", "classify_name_first", "classify_id_second", "classify_name_second", "title", "abstract", "author",
-			"frequency", "create_time", "modify_time", "state", "publish_time", "pre_publish_time", "stage", "msg_is_send", "report_code", "pv", "share_url",
-			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name", "approve_time", "detail_img_url", "detail_pdf_url",
-		}
-		items, e := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize, fieldArr)
-		if e != nil {
-			errList = fmt.Errorf("获取英文研报列表失败, Err: %s", e.Error())
-			return
-		}
-		list = items
-	}()
-
-	// 群发权限/失败记录
-	go func() {
-		defer func() {
-			wg.Done()
-		}()
-
-		// 获取邮件配置-是否有权限群发
-		conf := new(models.EnglishReportEmailConf)
-		authKey := "english_report_email_conf"
-		confAuth, e := company.GetConfigDetailByCode(authKey)
-		if e != nil {
-			errOther = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
-			return
-		}
-		if confAuth.ConfigValue == "" {
-			errOther = fmt.Errorf("群发邮件配置为空")
-			return
-		}
-		if e := json.Unmarshal([]byte(confAuth.ConfigValue), &conf); e != nil {
-			errOther = fmt.Errorf("群发邮件配置有误")
-			return
-		}
-		authArr := strings.Split(conf.SendAuthGroup, ",")
-		if utils.InArrayByStr(authArr, sysUser.RoleTypeCode) {
-			authOk = true
-		}
-
-		// 是否有群发邮件失败的记录,标记红点
-		failList, e := models.GetEnglishReportEmailLogFailList(0)
-		if e != nil {
-			errOther = fmt.Errorf("获取群发邮件记录失败, Err: %s", e.Error())
-			return
-		}
-		for i := range failList {
-			failMap[failList[i].ReportId] = true
-		}
-
-		// 获取admin, 用于匹配编辑中的研究员姓名
-		admins, e := system.GetSysAdminList("", make([]interface{}, 0), []string{"admin_id", "real_name"}, "")
-		if e != nil {
-			errOther = fmt.Errorf("获取系统用户列表失败, Err: %s", e.Error())
-			return
-		}
-		for _, a := range admins {
-			adminMap[a.AdminId] = a.RealName
-		}
-	}()
-	wg.Wait()
+	list, e := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize, []string{})
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文研报列表失败, Err: " + e.Error()
+		return
+	}
 
-	if errCount != nil {
+	// 获取邮件配置-是否有权限群发
+	conf := new(models.EnglishReportEmailConf)
+	authKey := "english_report_email_conf"
+	confAuth, e := company.GetConfigDetailByCode(authKey)
+	if e != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = errCount.Error()
+		br.ErrMsg = "获取群发邮件权限失败, Err: " + e.Error()
 		return
 	}
-	if errList != nil {
+	if confAuth.ConfigValue == "" {
 		br.Msg = "获取失败"
-		br.ErrMsg = errList.Error()
+		br.ErrMsg = "群发邮件配置为空"
 		return
 	}
-	if errOther != nil {
+	if e = json.Unmarshal([]byte(confAuth.ConfigValue), &conf); e != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = errOther.Error()
+		br.ErrMsg = "群发邮件配置有误, Err: " + e.Error()
 		return
 	}
+	authArr := strings.Split(conf.SendAuthGroup, ",")
+	if utils.InArrayByStr(authArr, sysUser.RoleTypeCode) {
+		authOk = true
+	}
+
+	// 获取admin, 用于匹配编辑中的研究员姓名
+	admins, e := system.GetSysAdminList("", make([]interface{}, 0), []string{"admin_id", "real_name"}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取系统用户列表失败, Err: " + e.Error()
+		return
+	}
+	for _, a := range admins {
+		adminMap[a.AdminId] = a.RealName
+	}
+
 	// 查询分类信息
 	var classifyIdSecondSlice []int
 	for _, item := range list {
@@ -603,21 +529,17 @@ func (this *EnglishReportController) ListReport() {
 			classifyNameMap[v.Id] = v
 		}
 	}
-	for _, item := range list {
-		if item.State == 2 {
-			item.ShareUrl = "https://share.hzinsights.com/reportEn?code=" + item.ReportCode
-		}
-		item.EmailAuth = authOk
-		item.EmailHasFail = failMap[item.Id]
-
-		// 邮箱PV大于0的时候, 不展示最初版本的PV
-		if item.PvEmail > 0 {
-			item.Pv = 0
+	respList := make([]*models.EnglishReportList, 0)
+	for _, v := range list {
+		t := models.FormatEnglishReport2ListItem(v)
+		if v.State == 2 {
+			t.ShareUrl = "https://share.hzinsights.com/reportEn?code=" + v.ReportCode
 		}
+		t.EmailAuth = authOk
 
 		// 报告是否正在编辑中
 		var opUser models.MarkReportItem
-		key := fmt.Sprint(`crm:enReport:edit:`, item.Id)
+		key := fmt.Sprint(`crm:enReport:edit:`, v.Id)
 		opUserId, e := utils.Rc.RedisInt(key)
 		if e != nil {
 			str, te := utils.Rc.RedisString(key)
@@ -635,31 +557,36 @@ func (this *EnglishReportController) ListReport() {
 				editor = adminMap[opUserId]
 			}
 			ret.Status = 1
-			ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+			if this.Lang == utils.EnLangVersion {
+				ret.Msg = fmt.Sprintf("%s is currently editing the report", editor)
+			} else {
+				ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
+			}
 			ret.Editor = editor
 		}
 		if ret.Status == 0 {
-			item.CanEdit = true
+			t.CanEdit = true
 		} else {
-			item.Editor = ret.Editor
+			t.Editor = ret.Editor
 		}
 
 		//处理分类名
-		if n, ok := classifyNameMap[item.ClassifyIdSecond]; ok {
+		if n, ok := classifyNameMap[v.ClassifyIdSecond]; ok {
 			if n.RootId == 0 {
-				item.FullClassifyName = strings.Join([]string{n.ParentName, n.ClassifyName}, "/")
+				t.FullClassifyName = strings.Join([]string{n.ParentName, n.ClassifyName}, "/")
 			} else {
-				item.FullClassifyName = strings.Join([]string{n.RootName, n.ParentName, n.ClassifyName}, "/")
+				t.FullClassifyName = strings.Join([]string{n.RootName, n.ParentName, n.ClassifyName}, "/")
 			}
-			item.ClassifyIdRoot = n.RootId
-			item.ClassifyNameRoot = n.RootName
+			t.ClassifyIdRoot = n.RootId
+			t.ClassifyNameRoot = n.RootName
 		}
+		respList = append(respList, t)
 	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(models.EnglishReportListResp)
 	resp.Paging = page
-	resp.List = list
+	resp.List = respList
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

+ 93 - 0
controllers/report_chapter.go

@@ -372,6 +372,12 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.ErrMsg = "该报告已发布,不允许编辑"
 		return
 	}
+	if reportChapterInfo.PublishState == 2 {
+		br.Msg = "该报告章节已发布,不允许编辑"
+		br.ErrMsg = "该报告章节已发布,不允许编辑"
+		br.IsSendEmail = false
+		return
+	}
 	// 报告的最后编辑人
 	reportInfo.LastModifyAdminId = sysUser.AdminId
 	reportInfo.LastModifyAdminName = sysUser.RealName
@@ -1616,3 +1622,90 @@ func (this *ReportController) EditChapterTitle() {
 	br.Success = true
 	br.Msg = "保存成功"
 }
+
+// CancelPublishReportChapter
+// @Title 取消发布章节
+// @Description 取消发布章节
+// @Param	request	body models.PublishReportChapterReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /chapter/publish/cancel [post]
+func (this *ReportController) CancelPublishReportChapter() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.PublishReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+
+	// 获取章节详情
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "章节信息有误"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+	// 章节发布状态校验
+	if chapterInfo.PublishState == 1 {
+		br.Msg = "该章节未发布,不能重复撤销"
+		br.ErrMsg = "该章节未发布,不能重复撤销"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "查询报告有误"
+		br.ErrMsg = "查询报告信息失败, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许撤销章节"
+		br.ErrMsg = "该报告已发布,不允许撤销章节"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 更新章节信息
+	chapterInfo.PublishState = 1
+	chapterInfo.PublishTime = time.Time{}
+	chapterInfo.LastModifyAdminId = this.SysUser.AdminId
+	chapterInfo.LastModifyAdminName = this.SysUser.RealName
+	chapterInfo.ModifyTime = time.Now()
+
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "PublishState", "PublishTime", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime")
+	err = chapterInfo.UpdateChapter(updateCols)
+	if err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+
+	// 更新章节ES
+	{
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "撤销成功"
+}

+ 106 - 0
controllers/resource.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"bufio"
+	"encoding/base64"
 	"encoding/json"
 	"eta/eta_mobile/models"
 	"eta/eta_mobile/services"
@@ -10,6 +11,7 @@ import (
 	"fmt"
 	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"io"
+	"net/http"
 	"os"
 	"path"
 	"regexp"
@@ -23,6 +25,11 @@ type ResourceController struct {
 	BaseCommonController
 }
 
+// ResourceAuthController 文件资源
+type ResourceAuthController struct {
+	BaseAuthController
+}
+
 // @Title 图片上传
 // @Description 图片上传接口
 // @Param   file   query   file  true       "文件"
@@ -919,3 +926,102 @@ func (this *ResourceController) WechatWarning() {
 	br.Ret = 200
 	br.Success = true
 }
+
+
+// FileDownload
+// @Title 文件下载
+// @Description 文件下载
+// @Param   FileUrl  query  string  true  "文件路径"
+// @Success 200 Ret=200 操作成功
+// @router /file/download [get]
+func (this *ResourceAuthController) FileDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	//fileName := this.GetString("FileName")
+	//fileName = strings.TrimSpace(fileName)
+	//if fileName == "" {
+	//	br.Msg = "参数有误"
+	//	return
+	//}
+	fileEncode := this.GetString("FileUrl")
+	fileEncode = strings.TrimSpace(fileEncode)
+	if fileEncode == "" {
+		br.Msg = "参数有误"
+		return
+	}
+	fileByte, e := base64.StdEncoding.DecodeString(fileEncode)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "文件地址解析失败, Err: " + e.Error()
+		return
+	}
+	fileUrl := string(fileByte)
+	fileArr := strings.Split(fileUrl, "/")
+	if len(fileArr) == 0 {
+		br.Msg = "文件地址有误"
+		return
+	}
+	fileName := fileArr[len(fileArr)-1]
+	//fmt.Println(fileName)
+
+	// 获取文件
+	down, e := http.Get(fileUrl)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "文件下载失败, http get: " + e.Error()
+		return
+	}
+	defer down.Body.Close()
+	if down.StatusCode != http.StatusOK {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("文件下载失败, http status: %d", down.StatusCode)
+		return
+	}
+
+	// 生成本地文件
+	localFilePath := fmt.Sprintf("%s%s", utils.GetRandStringNoSpecialChar(6), fileName)
+	localFile, e := os.Create(localFilePath)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "生成本地文件失败, Err: " + e.Error()
+		return
+	}
+	defer func() {
+		if e = localFile.Close(); e != nil {
+			fmt.Println("local file close err: ", e.Error())
+		}
+		if e = os.Remove(localFilePath); e != nil {
+			fmt.Println("local file remove err: ", e.Error())
+		}
+	}()
+
+	// 写入响应流
+	//_, e = io.Copy(this.Ctx.ResponseWriter, down.Body)
+	_, e = io.Copy(localFile, down.Body)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "复制文件资源失败, Err: " + e.Error()
+		return
+	}
+	// 设置响应头
+	//this.Ctx.ResponseWriter.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
+	//this.Ctx.ResponseWriter.Header().Set("Content-Type", "application/octet-stream")
+
+	br.Ret = 200
+	br.Msg = "下载成功"
+	br.Success = true
+	this.Ctx.Output.Download(localFilePath, fileName)
+}

+ 10 - 0
models/classify.go

@@ -617,3 +617,13 @@ func GetClassifyListByIdStrList(classifyIdStrList []string) (items []*Classify,
 	_, err = o.Raw(sql, classifyIdStrList).QueryRows(&items)
 	return
 }
+
+// GetClassifyListByParentId 查询父分类下所有的子分类
+func GetClassifyListByParentId(parentId int) (items []*Classify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM classify WHERE parent_id = ? and enabled = 1`
+
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+
+	return items, err
+}

+ 22 - 4
models/data_manage/chart_info.go

@@ -1935,10 +1935,12 @@ func ModifyChartInfoUserIdByOldUserId(oldUserIdList []int, userId int, userName
 }
 
 type FutureGoodBarChartInfoReq struct {
-	EdbInfoIdList []BarChartInfoEdbItemReq `description:"指标信息"`
-	DateList      []BarChartInfoDateReq    `description:"日期配置"`
-	XDataList     []XData                  `description:"横轴配置"`
-	BaseEdbInfoId int                      `description:"日期基准指标id"`
+	EdbInfoIdList       []BarChartInfoEdbItemReq `description:"指标信息"`
+	DateList            []BarChartInfoDateReq    `description:"日期配置"`
+	XDataList           []XData                  `description:"横轴配置"`
+	BaseEdbInfoId       int                      `description:"日期基准指标id"`
+	FutureGoodEdbName   string                   `description:"期货名称"`
+	FutureGoodEdbNameEn string                   `description:"期货英文名称"`
 }
 
 // BarChartInfoReq 柱方图预览请求数据
@@ -2737,3 +2739,19 @@ func getThsHfEdbDataListMinAndMaxByMongo(source, subSource, edbInfoId int, start
 	maxData = result.MaxValue
 	return
 }
+
+type BalanceTableChartListResp struct {
+	List []*BalanceChartInfoDetailResp
+}
+
+type BalanceChartInfoDetailResp struct {
+	*ChartInfoDetailResp
+	ExcelEdbList []*ExcelChartEdbView
+}
+
+type ExcelChartEdbView struct {
+	ExcelChartEdbId int
+	DateSequenceStr string `description:"日期序列选区"`
+	DataSequenceStr string `description:"数据序列选区"`
+	FromTag         string `description:"标签"`
+}

+ 36 - 8
models/data_manage/excel/excel_info.go

@@ -645,14 +645,6 @@ func ModifyExcelInfoUserIdByOldUserId(oldUserIdList []int, userId int, userName
 	return
 }
 
-// GetChildExcelInfoByParentId 根据id 获取eta表格详情
-func GetChildExcelInfoByParentId(parentId int) (items []*ExcelInfo, err error) {
-	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission, parent_id, balance_type, update_user_id,update_user_real_name FROM excel_info WHERE parent_id=? AND is_delete=0 order by sort asc, excel_info_id asc`
-	_, err = o.Raw(sql, parentId).QueryRows(&items)
-	return
-}
-
 // ExcelInfoDetailButton 操作按钮
 type ExcelInfoDetailButton struct {
 	RefreshButton    bool `description:"是否可刷新"`
@@ -665,6 +657,14 @@ type ExcelInfoDetailButton struct {
 	OpWorkerButton   bool `description:"是否修改协作人"`
 }
 
+// GetChildExcelInfoByParentId 根据id 获取eta表格详情
+func GetChildExcelInfoByParentId(parentId int) (items []*ExcelInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT excel_info_id,source,excel_type,excel_name,unique_code,excel_classify_id,sys_user_id,sys_user_real_name,excel_image,file_url,sort,create_time,modify_time,is_join_permission, parent_id, balance_type, update_user_id,update_user_real_name FROM excel_info WHERE parent_id=? AND is_delete=0 order by sort asc, excel_info_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
 func GetNoContentExcelInfoListByConditionNoPage(condition string, pars []interface{}) (items []*ExcelInfo, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := `SELECT excel_info_id,excel_classify_id,excel_name,
@@ -691,3 +691,31 @@ FROM excel_info WHERE 1=1 AND is_delete=0 `
 	_, err = o.Raw(sql, pars).QueryRows(&item)
 	return
 }
+
+type ExcelInfoRuleMappingView struct {
+	ExcelInfoRuleMappingId int    `orm:"pk" description:"主键"`
+	ExcelInfoId            int    `description:"Excel信息ID"`
+	RuleType               int    `description:"规则类型"`
+	LeftValue              string `description:"左值"`
+	LeftValueBack          string `description:"左值后端存储" json:"-"`
+	LeftValueType          int    `description:"左值类型"`
+	RightValue             string `description:"右值"`
+	RightValueBack         string `description:"右值后端存储" json:"-"`
+	RightValueType         int    `description:"右值类型"`
+	FontColor              string `description:"字体颜色"`
+	BackgroundColor        string `description:"背景颜色"`
+	Remark                 string `description:"预设颜色说明"`
+	RemarkEn               string `description:"预设颜色英文说明"`
+	Scope                  string `description:"作用范围"`
+	ScopeCoord             string `description:"作用范围坐标"`
+	ScopeShow              string `description:"作用范围坐标前端显示"`
+	CreateTime             string `description:"创建时间"`
+}
+
+// GetExcelRuleMappingByExcelInfoId 根据excelInfoId获取规则映射信息
+func GetExcelRuleMappingByExcelInfoId(id int) (items []*ExcelInfoRuleMappingView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_rule_mapping WHERE excel_info_id = ? ORDER BY create_time ASC`
+	_, err = o.Raw(sql, id).QueryRows(&items)
+	return
+}

+ 4 - 4
models/data_manage/excel/response/excel_info.go

@@ -103,6 +103,10 @@ type ExcelInfoDetailButton struct {
 	RefreshEdbButton bool `description:"是否可刷新指标"`
 }
 
+type BalanceChildTableResp struct {
+	List []*excel2.ExcelInfo
+}
+
 type BalanceTableVersionListItem struct {
 	ExcelInfoId    int    `description:"表格id"`
 	UniqueCode     string `description:"表格唯一编码"`
@@ -114,7 +118,3 @@ type BalanceTableVersionListItem struct {
 type BalanceTableVersionListResp struct {
 	List []*BalanceTableVersionListItem
 }
-
-type BalanceChildTableResp struct {
-	List []*excel2.ExcelInfo
-}

+ 11 - 0
models/data_manage/my_chart.go

@@ -975,3 +975,14 @@ func GetChartItems(cond string, pars []interface{}, orderRule string) (item []*C
 	_, err = o.Raw(sql, pars).QueryRows(&item)
 	return
 }
+
+func GetChartInfoViewByIdList(chartInfoIdList []int) (items []*ChartInfoView, err error) {
+	num := len(chartInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, chartInfoIdList).QueryRows(&items)
+	return
+}

+ 1 - 1
models/document_manage_model/outside_report.go

@@ -70,7 +70,7 @@ func GetOutsideReportListByConditionCount(condition string, pars []interface{})
 // GetOutsideReportListByCondition 根据条件查询列表
 func GetOutsideReportListByCondition(condition string, pars []interface{}, currentIndex int, pageSize int) (list []OutsideReportResp, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `select distinct t1.outside_report_id, t1.source, t1.title, t1.abstract, t1.classify_id, t1.classify_name, t1.sys_user_id, t1.sys_user_name, t1.email_message_uid, t1.report_update_time, t1.modify_time, t1.create_time, t1.report_code  from outside_report t1 left join chart_permission_search_key_word_mapping t2 on t1.classify_id = t2.classify_id  where 1 = 1 `
+	sql := `select t1.outside_report_id, t1.source, t1.title, t1.abstract, t1.classify_id, t1.classify_name, t1.sys_user_id, t1.sys_user_name, t1.email_message_uid, t1.report_update_time, t1.modify_time, t1.create_time, t1.report_code  from outside_report t1 left join chart_permission_search_key_word_mapping t2 on t1.classify_id = t2.classify_id  where 1 = 1 `
 	sql += condition
 	sql += ` limit ?, ?`
 	_, err = o.Raw(sql, pars, (currentIndex-1)*pageSize, pageSize).QueryRows(&list)

+ 54 - 2
models/english_report.go

@@ -36,6 +36,7 @@ type EnglishReport struct {
 	ReportCode         string    `description:"报告唯一编码"`
 	Pv                 int       `description:"Pv"`
 	PvEmail            int       `description:"邮箱PV"`
+	UvEmail            int       `description:"邮箱UV"`
 	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	Overview           string    `description:"英文概述部分"`
 	KeyTakeaways       string    `description:"关键点"`
@@ -44,6 +45,9 @@ type EnglishReport struct {
 	AdminRealName      string    `description:"创建者姓名"`
 	ApproveTime        time.Time `description:"审批时间"`
 	ApproveId          int       `description:"审批ID"`
+	DetailImgUrl       string    `description:"报告详情长图地址"`
+	DetailPdfUrl       string    `description:"报告详情PDF地址"`
+	EmailHasFail       int       `description:"是否存在邮件发送失败的记录: 0-否; 1-是"`
 }
 
 func GetEnglishReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
@@ -257,6 +261,7 @@ type EnglishReportList struct {
 	Pv                 int       `description:"Pv"`
 	ShareUrl           string    `description:"分享url"`
 	PvEmail            int       `description:"邮箱PV"`
+	UvEmail            int       `description:"邮箱UV"`
 	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	EmailAuth          bool      `description:"是否有权限群发邮件"`
 	EmailHasFail       bool      `description:"是否存在邮件发送失败的记录"`
@@ -296,7 +301,7 @@ func GetEnglishReportListCount(condition string, pars []interface{}, companyType
 	return
 }
 
-func GetEnglishReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int, fieldArr []string) (items []*EnglishReportList, err error) {
+func GetEnglishReportList(condition string, pars []interface{}, companyType string, startSize, pageSize int, fieldArr []string) (items []*EnglishReport, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	//产品权限
 	companyTypeSqlStr := ``
@@ -316,7 +321,7 @@ func GetEnglishReportList(condition string, pars []interface{}, companyType stri
 		sql += condition
 	}
 	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
-	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
+	sql += ` ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -939,3 +944,50 @@ func UpdatePdfUrlEnglishReportById(reportId int) (err error) {
 	_, err = o.Raw(sql, reportId).Exec()
 	return
 }
+
+func FormatEnglishReport2ListItem(origin *EnglishReport) (item *EnglishReportList) {
+	if origin == nil {
+		return
+	}
+	item = new(EnglishReportList)
+	item.Id = origin.Id
+	item.AddType = origin.AddType
+	item.ClassifyIdFirst = origin.ClassifyIdFirst
+	item.ClassifyNameFirst = origin.ClassifyNameFirst
+	item.ClassifyIdSecond = origin.ClassifyIdSecond
+	item.ClassifyNameSecond = origin.ClassifyNameSecond
+	item.Title = origin.Title
+	item.Abstract = origin.Abstract
+	item.Author = origin.Author
+	item.Frequency = origin.Frequency
+	item.CreateTime = origin.CreateTime
+	item.ModifyTime = origin.ModifyTime
+	item.State = origin.State
+	item.PublishTime = utils.TimeTransferString(utils.FormatDateTime, origin.PublishTime)
+	item.PrePublishTime = utils.TimeTransferString(utils.FormatDateTime, origin.PrePublishTime)
+	item.Stage = origin.Stage
+	//item.Content = origin.Content
+	item.VideoUrl = origin.VideoUrl
+	item.VideoName = origin.VideoName
+	item.VideoPlaySeconds = origin.VideoPlaySeconds
+	item.ContentSub = origin.ContentSub
+	item.ReportCode = origin.ReportCode
+	item.Pv = origin.Pv
+	item.PvEmail = origin.PvEmail
+	item.UvEmail = origin.UvEmail
+	// 邮箱PV大于0的时候, 不展示最初版本的PV
+	if item.PvEmail > 0 {
+		item.Pv = 0
+	}
+	item.EmailState = origin.EmailState
+	if origin.EmailHasFail == 1 {
+		item.EmailHasFail = true
+	}
+	item.FromReportId = origin.FromReportId
+	item.AdminId = origin.AdminId
+	item.AdminRealName = origin.AdminRealName
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.DetailImgUrl = origin.DetailImgUrl
+	item.DetailPdfUrl = origin.DetailPdfUrl
+	return
+}

+ 11 - 1
models/permission.go

@@ -1,6 +1,9 @@
 package models
 
-import "github.com/beego/beego/v2/client/orm"
+import (
+	"eta/eta_mobile/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
 
 // ChartPermissionSearchKeyWordMapping 权限相关
 type ChartPermissionSearchKeyWordMapping struct {
@@ -124,3 +127,10 @@ func GetPermissionByClassifyId(classifyId int) (items []*ChartPermissionSearchKe
 	_, err = o.Raw(sql, classifyId).QueryRows(&items)
 	return
 }
+
+func GetClassifyIdsByPermissionId(chartPermissionIdList []string) (classifyIds []string, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := "SELECT classify_id FROM chart_permission_search_key_word_mapping WHERE `from` =  'rddp' and chart_permission_id IN (" + utils.GetOrmInReplace(len(chartPermissionIdList)) + ") and classify_id <> 0; "
+	_, err = o.Raw(sql, chartPermissionIdList).QueryRows(&classifyIds)
+	return
+}

+ 46 - 14
models/report.go

@@ -1695,14 +1695,28 @@ func GetReportListByCollectCountV2(classifyIdFirst, classifyIdSecond, classifyId
 		SELECT COUNT(DISTINCT t.id) AS totalCount
 		FROM (
 			SELECT b.id
-			FROM report AS b left join chart_permission_search_key_word_mapping c on
-b.classify_id_first = c.classify_id
-			WHERE 1 = 1
+			FROM report AS b WHERE 1 = 1
 	`
 	if len(chartPermissionIdList) > 0 {
-		sql += ` and c.chart_permission_id in (` + utils.GetOrmInReplace(len(chartPermissionIdList)) + `)`
-		for _, chartPermissionId := range chartPermissionIdList {
-			params = append(params, chartPermissionId)
+		classifyIds, e := GetClassifyIdsByPermissionId(chartPermissionIdList)
+		if e != nil {
+			err = e
+			return
+		}
+
+		if len(classifyIds) > 0 {
+			sql += ` AND ( (b.classify_id_first IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) AND b.classify_id_second = 0) 
+			OR (b.classify_id_second IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) AND b.classify_id_third = 0) 
+			OR b.classify_id_third IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) )`
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
 		}
 	}
 
@@ -1764,12 +1778,14 @@ func GetReportListByCollectListV2(classifyIdFirst, classifyIdSecond, classifyIdT
 
 	// SQL 主体
 	sql := `
-		SELECT DISTINCT t.id, t.title, t.author, t.modify_time, t.publish_time,t.classify_id_first,t.classify_name_first,t.classify_id_second,t.classify_name_second,t.classify_id_third,t.classify_name_third
+		SELECT DISTINCT t.id, t.title, t.author, t.modify_time, t.publish_time,t.classify_id_first,t.classify_name_first,
+t.classify_id_second,t.classify_name_second,t.classify_id_third,t.classify_name_third,
+t.abstract,t.admin_id,t.admin_real_name,t.last_modify_admin_id,t.last_modify_admin_name 
 		FROM (
-			SELECT b.id, b.title, b.author, b.modify_time, b.publish_time,b.classify_id_first,b.classify_name_first,b.classify_id_second,b.classify_name_second,b.classify_id_third,b.classify_name_third
-			FROM report AS b  left join chart_permission_search_key_word_mapping c on
-b.classify_id_first = c.classify_id 
-			WHERE 1 = 1
+			SELECT b.id, b.title, b.author, b.modify_time, b.publish_time,b.classify_id_first,b.classify_name_first,
+b.classify_id_second,b.classify_name_second,b.classify_id_third,b.classify_name_third,
+b.abstract,b.admin_id,b.admin_real_name,b.last_modify_admin_id,b.last_modify_admin_name 
+			FROM report AS b WHERE 1 = 1
 	`
 
 	// 处理关键词
@@ -1818,9 +1834,25 @@ b.classify_id_first = c.classify_id
 	}
 
 	if len(chartPermissionIdList) > 0 {
-		sql += ` and c.chart_permission_id in (` + utils.GetOrmInReplace(len(chartPermissionIdList)) + `)`
-		for _, chartPermissionId := range chartPermissionIdList {
-			params = append(params, chartPermissionId)
+		classifyIds, e := GetClassifyIdsByPermissionId(chartPermissionIdList)
+		if e != nil {
+			err = e
+			return nil, err
+		}
+
+		if len(classifyIds) > 0 {
+			sql += ` AND ( (b.classify_id_first IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) AND b.classify_id_second = 0) 
+			OR (b.classify_id_second IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) AND b.classify_id_third = 0) 
+			OR b.classify_id_third IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) )`
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
+			for _, classifyId := range classifyIds {
+				params = append(params, classifyId)
+			}
 		}
 	}
 

+ 27 - 0
routers/commentsRouter.go

@@ -763,6 +763,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "GetBalanceChartList",
+            Router: `/excel_info/balance/chart_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "BalanceVersionList",
@@ -5551,6 +5560,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "CancelPublishReportChapter",
+            Router: `/chapter/publish/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ReportController"],
         beego.ControllerComments{
             Method: "EditChapterTitle",
@@ -5911,6 +5929,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceAuthController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceAuthController"],
+        beego.ControllerComments{
+            Method: "FileDownload",
+            Router: `/file/download`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_mobile/controllers:ResourceController"],
         beego.ControllerComments{
             Method: "Upload",

+ 1 - 0
routers/router.go

@@ -145,6 +145,7 @@ func init() {
 		web.NSNamespace("/resource",
 			web.NSInclude(
 				&controllers.ResourceController{},
+				&controllers.ResourceAuthController{},
 			),
 		),
 		web.NSNamespace("/datamanage",

+ 122 - 0
services/data/edb_info_relation.go

@@ -0,0 +1,122 @@
+package data
+
+import (
+	"eta/eta_mobile/models/data_manage"
+	"fmt"
+)
+
+// 查询两个指标是否存在循环引用关系
+func CheckTwoEdbInfoRelation(edbInfoA, edbInfoB *data_manage.EdbInfo) (hasRelation bool, err error) {
+	//查询指标信息
+	allEdbMappingMap := make(map[int][]*data_manage.EdbInfoCalculateMappingInfo, 0)
+	allMappingList, e := data_manage.GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoA.EdbInfoId)
+	if e != nil {
+		err = fmt.Errorf("GetEdbInfoCalculateMappingListByEdbInfoIds err: %s", e.Error())
+		return
+	}
+	for _, v := range allMappingList {
+		if v.EdbInfoId == edbInfoB.EdbInfoId {
+			hasRelation = true
+			return
+		}
+		if _, ok := allEdbMappingMap[v.EdbInfoId]; !ok {
+			allEdbMappingMap[v.EdbInfoId] = make([]*data_manage.EdbInfoCalculateMappingInfo, 0)
+		}
+		allEdbMappingMap[v.EdbInfoId] = append(allEdbMappingMap[v.EdbInfoId], v)
+	}
+	//查询指标映射
+	//查询所有指标数据
+	//查询这个指标相关的mapping信息放到数组里,
+	//将得到的指标ID信息放到数组里
+	hasFindMap := make(map[int]struct{})
+	edbInfoIdMap := make(map[int]struct{})
+	edbMappingList := make([]*data_manage.EdbInfoCalculateMapping, 0)
+	edbInfoMappingRootIdsMap := make(map[int][]int, 0)
+	edbMappingMap := make(map[int]struct{})
+
+	if edbInfoA.EdbType == 2 {
+		edbInfoId := edbInfoA.EdbInfoId
+		edbMappingList, err = getCalculateEdbInfoByEdbInfoId(allEdbMappingMap, edbInfoId, hasFindMap, edbInfoIdMap, edbMappingList, edbMappingMap, edbInfoMappingRootIdsMap, edbInfoId)
+		if err != nil {
+			err = fmt.Errorf(" GetCalculateEdbInfoByEdbInfoId err: %s", err.Error())
+			return
+		}
+		// 判断其中是否包含指标B
+		if _, ok := edbInfoIdMap[edbInfoB.EdbInfoId]; ok { // 如果包含,则说明存在循环引用关系
+			hasRelation = true
+			return
+		}
+	}
+
+	return
+}
+
+// getCalculateEdbInfoByEdbInfoId 计算指标追溯
+func getCalculateEdbInfoByEdbInfoId(allEdbMappingMap map[int][]*data_manage.EdbInfoCalculateMappingInfo, edbInfoId int, hasFindMap map[int]struct{}, edbInfoIdMap map[int]struct{}, edbMappingList []*data_manage.EdbInfoCalculateMapping, edbMappingMap map[int]struct{}, edbInfoMappingRootIdsMap map[int][]int, rootEdbInfoId int) (newEdbMappingList []*data_manage.EdbInfoCalculateMapping, err error) {
+	newEdbMappingList = edbMappingList
+	_, ok := hasFindMap[edbInfoId]
+	if ok {
+		return
+	}
+
+	if _, ok1 := edbInfoIdMap[edbInfoId]; !ok1 {
+		edbInfoIdMap[edbInfoId] = struct{}{}
+	}
+	edbInfoMappingList := make([]*data_manage.EdbInfoCalculateMappingInfo, 0)
+	edbInfoMappingList, ok = allEdbMappingMap[edbInfoId]
+	if !ok {
+		edbInfoMappingList, err = data_manage.GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId)
+		if err != nil {
+			err = fmt.Errorf("GetEdbInfoCalculateMappingListByEdbInfoId err: %s", err.Error())
+			return
+		}
+	}
+	hasFindMap[edbInfoId] = struct{}{}
+	if len(edbInfoMappingList) > 0 {
+		fromEdbInfoIdList := make([]int, 0)
+		edbInfoMappingIdList := make([]int, 0)
+		for _, v := range edbInfoMappingList {
+			fromEdbInfoIdList = append(fromEdbInfoIdList, v.FromEdbInfoId)
+			edbInfoMappingIdList = append(edbInfoMappingIdList, v.EdbInfoCalculateMappingId)
+			if _, ok1 := edbInfoIdMap[v.FromEdbInfoId]; !ok1 {
+				edbInfoIdMap[v.FromEdbInfoId] = struct{}{}
+			}
+			if _, ok2 := edbMappingMap[v.EdbInfoCalculateMappingId]; !ok2 {
+				edbMappingMap[v.EdbInfoCalculateMappingId] = struct{}{}
+				tmp := &data_manage.EdbInfoCalculateMapping{
+					EdbInfoCalculateMappingId: v.EdbInfoCalculateMappingId,
+					EdbInfoId:                 v.EdbInfoId,
+					Source:                    v.Source,
+					SourceName:                v.SourceName,
+					EdbCode:                   v.EdbCode,
+					FromEdbInfoId:             v.FromEdbInfoId,
+					FromEdbCode:               v.FromEdbCode,
+					FromEdbName:               v.FromEdbName,
+					FromSource:                v.FromSource,
+					FromSourceName:            v.FromSourceName,
+					FromTag:                   v.FromTag,
+					Sort:                      v.Sort,
+					CreateTime:                v.CreateTime,
+					ModifyTime:                v.ModifyTime,
+				}
+				newEdbMappingList = append(newEdbMappingList, tmp)
+
+			}
+
+			if edbInfoId != v.FromEdbInfoId && (v.FromEdbType == 2 || v.FromEdbInfoType == 1) {
+				// 查过了就不查了
+				if _, ok2 := hasFindMap[v.FromEdbInfoId]; !ok2 {
+					newEdbMappingList, err = getCalculateEdbInfoByEdbInfoId(allEdbMappingMap, v.FromEdbInfoId, hasFindMap, edbInfoIdMap, newEdbMappingList, edbMappingMap, edbInfoMappingRootIdsMap, rootEdbInfoId)
+					if err != nil {
+						err = fmt.Errorf("traceEdbInfoByEdbInfoId err: %s", err.Error())
+						return
+					}
+				}
+			}
+			hasFindMap[v.FromEdbInfoId] = struct{}{}
+		}
+		edbInfoMappingRootIdsMap[rootEdbInfoId] = append(edbInfoMappingRootIdsMap[rootEdbInfoId], edbInfoMappingIdList...)
+	}
+
+	return
+}

+ 66 - 0
services/data/excel/balance_table.go

@@ -406,3 +406,69 @@ func GetBalanceExcelInfoOpButton(sysUserId, parentSysUserId int, haveOperaAuth b
 	}
 	return
 }
+
+func GetBalanceExcelChartList(excelInfo *excelModel.ExcelInfo, lang string) (list []*data_manage.ChartInfoView, mappingListMap map[int][]*excelModel.ExcelChartEdb, dataListMap map[int][]*data_manage.EdbDataList, err error, errMsg string) {
+	dataListMap = make(map[int][]*data_manage.EdbDataList)
+	// 相关联指标
+	mappingListTmp, err := excelModel.GetExcelChartEdbMappingByExcelInfoId(excelInfo.ExcelInfoId)
+	if err != nil {
+		errMsg = "获取失败"
+		err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", err.Error())
+		return
+	}
+	charInfoIds := make([]int, 0)
+	mappingListMap = make(map[int][]*excelModel.ExcelChartEdb, 0)
+	if excelInfo.BalanceType == 1 {
+		for _, mapping := range mappingListTmp {
+			mappingListMap[mapping.ChartInfoId] = append(mappingListMap[mapping.ChartInfoId], mapping)
+		}
+		//查询库里是否有值
+		chartDataList, e := excelModel.GetExcelChartDataByExcelInfoId(excelInfo.ExcelInfoId)
+		if e != nil {
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		if len(chartDataList) > 0 {
+			for _, v := range chartDataList {
+				tmp := &data_manage.EdbDataList{
+					EdbDataId:     v.ExcelChartDataId,
+					EdbInfoId:     v.ExcelChartEdbId,
+					DataTime:      v.DataTime,
+					DataTimestamp: v.DataTimestamp,
+					Value:         v.Value,
+				}
+				dataListMap[v.ExcelChartEdbId] = append(dataListMap[v.ExcelChartEdbId], tmp)
+			}
+		}
+	} else {
+		newExcelDataMap, excelAllRows, excelAllCols, e, msg := GetBalanceExcelData(excelInfo, lang)
+		if e != nil {
+			err = e
+			errMsg = msg
+			return
+		}
+		for _, mapping := range mappingListTmp {
+			mappingListMap[mapping.ChartInfoId] = append(mappingListMap[mapping.ChartInfoId], mapping)
+			er, ms := GetBalanceExcelEdbData(mapping, newExcelDataMap, dataListMap, excelAllRows, excelAllCols)
+			if er != nil {
+				utils.FileLog.Info(fmt.Sprintf(" 获取图表,指标信息失败 Err:%s, %s", er.Error(), ms))
+				continue
+			}
+		}
+	}
+
+	for k, _ := range mappingListMap {
+		charInfoIds = append(charInfoIds, k)
+	}
+	list = make([]*data_manage.ChartInfoView, 0)
+	if len(charInfoIds) > 0 {
+		chartInfoList, e := data_manage.GetChartInfoViewByIdList(charInfoIds)
+		if e != nil {
+			errMsg = "获取失败"
+			err = fmt.Errorf(" 获取图表,指标信息失败 Err:%s", e.Error())
+			return
+		}
+		list = chartInfoList
+	}
+	return
+}

+ 44 - 3
services/document_manage_service/document_manage_service.go

@@ -178,6 +178,23 @@ func DocumentReportList(userId, documentType int, chartPermissionIdList []string
 		condition = ` and t1.source!=3`
 	}
 	if len(classifyIdList) > 0 {
+		// 递归查询子分类
+		for _, classifyId := range classifyIdList {
+			id, err := strconv.Atoi(classifyId)
+			if err != nil {
+				return nil, err
+			}
+
+			if id == 0 {
+				classifyIdList = append(classifyIdList, classifyId)
+			} else {
+				childrenClassifyIdList, err := GetAllClassifyIdsByParentId(id)
+				if err != nil {
+					return nil, err
+				}
+				classifyIdList = append(classifyIdList, childrenClassifyIdList...)
+			}
+		}
 		condition += ` and t1.classify_id in (` + utils.GetOrmInReplace(len(classifyIdList)) + `)`
 		for _, classifyId := range classifyIdList {
 			pars = append(pars, classifyId)
@@ -190,7 +207,7 @@ func DocumentReportList(userId, documentType int, chartPermissionIdList []string
 		}
 	}
 	if keyword != "" {
-		condition += ` and (t1.title like ? or t1.sys_user_name like ? )`
+		condition += ` and (t1.title like ? or t1.sys_user_name like ?) `
 		pars = append(pars, "%"+keyword+"%", "%"+keyword+"%")
 	}
 
@@ -209,7 +226,7 @@ func DocumentReportList(userId, documentType int, chartPermissionIdList []string
 	if orderField != "" && orderType != "" {
 		condition += ` order by t1.` + orderField + ` ` + orderType
 	} else {
-		condition += ` order by t1.modify_time desc`
+		condition += ` order by t1.report_update_time desc`
 	}
 
 	outsideReportList, err := document_manage_model.GetOutsideReportListByCondition(condition, pars, startSize, pageSize)
@@ -240,6 +257,30 @@ func DocumentReportList(userId, documentType int, chartPermissionIdList []string
 	return &reportPage, nil
 }
 
+// GetAllClassifyIdsByParentId 递归查询子分类
+func GetAllClassifyIdsByParentId(parentId int) ([]string, error) {
+	var classifyIdList []string
+
+	// 获取子分类
+	classifyList, err := models.GetClassifyListByParentId(parentId)
+	if err != nil {
+		return nil, err
+	}
+
+	// 遍历子分类
+	for _, classify := range classifyList {
+		classifyIdList = append(classifyIdList, strconv.Itoa(classify.Id))
+		// 递归调用
+		subClassifyIds, err := GetAllClassifyIdsByParentId(classify.Id)
+		if err != nil {
+			return nil, err
+		}
+		classifyIdList = append(classifyIdList, subClassifyIds...)
+	}
+
+	return classifyIdList, nil
+}
+
 func RuiSiReportList(classifyIdFirst, classifyIdSecond, classifyIdThird []string, keyword, orderField, orderType string, startSize, pageSize int) (*models.ReportListResp, error) {
 	logs.Info("RuiSiReportList")
 
@@ -456,7 +497,7 @@ func RuiSiReportListV2(classifyIdList, chartPermissionIdList []string, keyword,
 		}
 	}
 	// 作者为 全球市场战略研究中心 PCI Research
-	author := "全球市场中心 PCI Research"
+	author := "战研中心 PCIR"
 	// 已发布的报告
 	state := 2
 

+ 220 - 0
services/excel/lucky_sheet.go

@@ -3,6 +3,7 @@ package excel
 import (
 	"encoding/json"
 	"errors"
+	"eta/eta_mobile/models/data_manage/excel"
 	"eta/eta_mobile/models/data_manage/excel/request"
 	"eta/eta_mobile/utils"
 	"fmt"
@@ -1724,3 +1725,222 @@ func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hide
 
 	return
 }
+
+// HandleRuleToTableCell 根据管理规则渲染单元格数据
+func HandleRuleToTableCell(excelInfoId int, oldTableData TableData) (newTableData TableData, err error) {
+	newTableData = oldTableData
+	excelRuleMappingList, err := excel.GetExcelRuleMappingByExcelInfoId(excelInfoId)
+	if err != nil {
+		return
+	}
+	if len(excelRuleMappingList) == 0 {
+		return
+	}
+	tableDataList := oldTableData.TableDataList
+	excelRuleMap := make(map[int]*excel.ExcelInfoRuleMappingView)
+	for _, v := range excelRuleMappingList {
+		excelRuleMap[v.ExcelInfoRuleMappingId] = v
+	}
+	ruleScopeMap := generateRuleScopeIndexMap(excelRuleMappingList)
+	for row, scopeValues := range ruleScopeMap {
+		for col, ruleId := range scopeValues {
+			if v, ok := excelRuleMap[ruleId]; ok {
+				if len(tableDataList) > row && len(tableDataList[row]) > col {
+					// 符合管理规则要求,则进行字体和背景颜色的渲染
+					if checkCellRule(v, tableDataList[row][col].Monitor, tableDataList) {
+						tableDataList[row][col].Background = v.BackgroundColor
+						tableDataList[row][col].FontColor = v.FontColor
+					}
+				} else {
+					continue
+				}
+			} else {
+				continue
+			}
+		}
+	}
+	return
+}
+
+func getCellValueByType(value string, valueType int, tableDataList [][]LuckySheetDataValue) (float64, bool) {
+	if valueType == 2 {
+		coords := strings.Split(value, ",")
+		var coordIntArr []int
+		for _, v := range coords {
+			t, _ := strconv.Atoi(v)
+			coordIntArr = append(coordIntArr, t)
+		}
+		if len(coordIntArr) == 2 {
+			x, y := coordIntArr[0]-1, coordIntArr[1]-1
+			conditionValue, err := strconv.ParseFloat(tableDataList[y][x].Monitor, 64)
+			if err != nil {
+				return 0, false
+			}
+			return conditionValue, true
+		}
+	} else {
+		conditionValue, err := strconv.ParseFloat(value, 64)
+		if err != nil {
+			return 0, false
+		}
+		return conditionValue, true
+	}
+	return 0, false
+}
+
+func checkCellRule(ruleInfo *excel.ExcelInfoRuleMappingView, value string, tableDataList [][]LuckySheetDataValue) bool {
+	var tableValue float64
+	var tableTime time.Time
+	var err error
+
+	if ruleInfo.RuleType == 5 {
+		tableTime, err = time.ParseInLocation(utils.FormatDate, value, time.Local)
+		if err != nil {
+			return false
+		}
+	} else {
+		tableValue, err = strconv.ParseFloat(value, 64)
+		if err != nil {
+			return false
+		}
+	}
+	switch ruleInfo.RuleType {
+	// 大于
+	case 1:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue > conditionValue {
+			return true
+		}
+	// 小于
+	case 2:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue < conditionValue {
+			return true
+		}
+	// 介于
+	case 3:
+		leftcondValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		rightcondValue, ok := getCellValueByType(ruleInfo.RightValueBack, ruleInfo.RightValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if leftcondValue <= tableValue && tableValue <= rightcondValue {
+			return true
+		}
+	// 等于
+	case 4:
+		conditionValue, ok := getCellValueByType(ruleInfo.LeftValueBack, ruleInfo.LeftValueType, tableDataList)
+		if !ok {
+			return false
+		}
+		if tableValue == conditionValue {
+			return true
+		}
+	// 发生日期
+	case 5:
+		// 发生日期
+		var dateStart, dataEnd time.Time
+		switch ruleInfo.LeftValueBack {
+		// 今天
+		case "today":
+			// 获得今天的零点零分零秒
+			dateStart = utils.Today()
+			if tableTime == dateStart {
+				return true
+			}
+		// 明天
+		case "tomorrow":
+			dateStart = utils.Tomorrow()
+			if tableTime == dateStart {
+				return true
+			}
+		// 最近7天
+		case "last7days":
+			dateStart, dataEnd = utils.Last7Days()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 上周
+		case "lastweek":
+			dateStart, dataEnd = utils.LastWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 本周
+		case "thisweek":
+			dateStart, dataEnd = utils.ThisWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 下周
+		case "nextweek":
+			dateStart, dataEnd = utils.NextWeek()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 上月
+		case "lastmonth":
+			dateStart, dataEnd = utils.LastMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 本月
+		case "thismonth":
+			dateStart, dataEnd = utils.ThisMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		// 下月
+		case "nextmonth":
+			dateStart, dataEnd = utils.NextMonth()
+			if tableTime.After(dateStart) && tableTime.Before(dataEnd) {
+				return true
+			}
+		default:
+			return false
+		}
+
+	}
+	return false
+}
+
+func generateRuleScopeIndexMap(items []*excel.ExcelInfoRuleMappingView) (ruleScopeMap map[int]map[int]int) {
+	ruleScopeMap = make(map[int]map[int]int)
+	for _, item := range items {
+		coords := strings.Split(item.ScopeCoord, ",")
+		var coordIntArr []int
+		for _, v := range coords {
+			t, _ := strconv.Atoi(v)
+			coordIntArr = append(coordIntArr, t)
+		}
+
+		if len(coords) == 4 {
+			xmin, ymin, xmax, ymax := coordIntArr[0]-1, coordIntArr[1]-1, coordIntArr[2]-1, coordIntArr[3]-1
+			for i := ymin; i <= ymax; i++ {
+				for j := xmin; j <= xmax; j++ {
+					if _, ok := ruleScopeMap[i]; !ok {
+						ruleScopeMap[i] = make(map[int]int)
+					}
+					ruleScopeMap[i][j] = item.ExcelInfoRuleMappingId
+				}
+			}
+		}
+		if len(coords) == 2 {
+			x, y := coordIntArr[0]-1, coordIntArr[1]-1
+			if _, ok := ruleScopeMap[y]; !ok {
+				ruleScopeMap[y] = make(map[int]int)
+			}
+			ruleScopeMap[y][x] = item.ExcelInfoRuleMappingId
+		}
+	}
+	return
+}

+ 74 - 0
utils/time.go

@@ -0,0 +1,74 @@
+package utils
+
+import "time"
+
+// 获取今天的日期(零点零分零秒)
+func Today() time.Time {
+	now := time.Now()
+	return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
+}
+
+// 获取明天的日期(零点零分零秒)
+func Tomorrow() time.Time {
+	return Today().AddDate(0, 0, 1)
+}
+
+// 获取最近7天的日期范围
+func Last7Days() (time.Time, time.Time) {
+	today := Today()
+	return today.AddDate(0, 0, -6), today.Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+}
+
+// 获取上周的日期范围
+func LastWeek() (time.Time, time.Time) {
+	start := Today().AddDate(0, 0, -7)
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取本周的日期范围
+func ThisWeek() (time.Time, time.Time) {
+	start := Today().Round(0).Local().AddDate(0, 0, 0-int(time.Now().Weekday())+1) // 0是本周的第一天,+1是因为AddDate是相对于当前时间的
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取下周的日期范围
+func NextWeek() (time.Time, time.Time) {
+	_, thisEnd := ThisWeek()
+	start := thisEnd.AddDate(0, 0, 1)
+	end := start.AddDate(0, 0, 6).Add(time.Hour * 23).Add(time.Minute * 59).Add(time.Second * 59)
+	return start, end
+}
+
+// 获取上月的日期范围
+func LastMonth() (time.Time, time.Time) {
+	today := time.Now()
+	year, month, _ := today.Date()
+	start := time.Date(year, month-1, 1, 0, 0, 0, 0, today.Location())
+	end := time.Date(year, month, 0, 23, 59, 59, 999999999, today.Location())
+	return start, end
+}
+
+// 获取本月的日期范围
+func ThisMonth() (time.Time, time.Time) {
+	today := time.Now()
+	year, month, _ := today.Date()
+	start := time.Date(year, month, 1, 0, 0, 0, 0, today.Location())
+	end := time.Date(year, month+1, 0, 23, 59, 59, 999999999, today.Location())
+	return start, end
+}
+
+// 获取下月的日期范围
+func NextMonth() (time.Time, time.Time) {
+	now := time.Now()
+	year, month, _ := now.Date()
+	nextMonth := month + 1
+	if nextMonth > 12 {
+		nextMonth = 1
+		year++
+	}
+	startOfNextMonth := time.Date(year, nextMonth, 1, 0, 0, 0, 0, now.Location())
+	endOfNextMonth := startOfNextMonth.AddDate(0, 1, -1).Add(time.Hour*23 + time.Minute*59 + time.Second*59)
+	return startOfNextMonth, endOfNextMonth
+}