Browse Source

Merge branch 'feature/eta1.5.6_excel' into debug

# Conflicts:
#	services/data/excel/mixed_table.go
#	utils/common.go
xyxie 1 year ago
parent
commit
f48f289c7e

+ 67 - 89
controllers/data_manage/excel/excel_classify.go

@@ -9,6 +9,7 @@ import (
 	response2 "eta/eta_api/models/data_manage/excel/response"
 	excel2 "eta/eta_api/services/data/excel"
 	"eta/eta_api/utils"
+	"fmt"
 	"strconv"
 	"time"
 )
@@ -173,7 +174,12 @@ func (this *ExcelClassifyController) AddExcelClassify() {
 		return
 	}
 	//获取该层级下最大的排序数
-	maxSort, err := excel.GetExcelClassifyMaxSort(req.ParentId)
+	maxSort, err := excel2.GetExcelClassifyMaxSort(req.ParentId, req.Source)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "查询排序信息失败,Err:" + err.Error()
+		return
+	}
 
 	// 入库
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
@@ -334,6 +340,33 @@ func (this *ExcelClassifyController) DeleteExcelClassifyCheck() {
 		if count > 0 {
 			deleteStatus = 1
 			tipsMsg = "该分类下关联表格不可删除"
+		} else {
+			childClassify, e := excel.GetChildClassifyById(req.ExcelClassifyId)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+				return
+			}
+			if len(childClassify) > 0 {
+				var classifyIds []int
+				for _, v := range childClassify {
+					classifyIds = append(classifyIds, v.ExcelClassifyId)
+				}
+				condition := fmt.Sprintf(` AND excel_classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+				var pars []interface{}
+				pars = append(pars, classifyIds)
+				childCount, err := excel.GetExcelInfoCountByCondition(condition, pars)
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					br.Msg = "删除失败"
+					br.ErrMsg = "查询分类下表格数量失败,Err:" + err.Error()
+					return
+				}
+
+				if childCount > 0 {
+					deleteStatus = 1
+					tipsMsg = "该分类下关联表格不可删除"
+				}
+			}
 		}
 	}
 
@@ -413,8 +446,35 @@ func (this *ExcelClassifyController) DeleteExcelClassify() {
 			br.Msg = "该目录下存在关联ETA表格,不可删除"
 			br.IsSendEmail = false
 			return
-		}
+		} else {
+			childClassify, e := excel.GetChildClassifyById(req.ExcelClassifyId)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+				return
+			}
+			if len(childClassify) > 0 {
+				var classifyIds []int
+				for _, v := range childClassify {
+					classifyIds = append(classifyIds, v.ExcelClassifyId)
+				}
+				condition := fmt.Sprintf(` AND excel_classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+				var pars []interface{}
+				pars = append(pars, classifyIds)
+				childCount, err := excel.GetExcelInfoCountByCondition(condition, pars)
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					br.Msg = "删除失败"
+					br.ErrMsg = "查询分类下表格数量失败,Err:" + err.Error()
+					return
+				}
 
+				if childCount > 0 {
+					br.Msg = "该目录下存在关联ETA表格,不可删除"
+					br.IsSendEmail = false
+					return
+				}
+			}
+		}
 		classifyItem, err := excel.GetExcelClassifyById(req.ExcelClassifyId)
 		if err != nil {
 			br.Msg = "删除失败"
@@ -561,101 +621,19 @@ func (this *ExcelClassifyController) ExcelClassifyMove() {
 		return
 	}
 
-	if req.ClassifyId <= 0 {
+	if req.ClassifyId <= 0 && req.ExcelInfoId <= 0 {
 		br.Msg = "参数错误"
-		br.ErrMsg = "分类id小于等于0"
-		br.IsSendEmail = false
+		br.ErrMsg = "请选择拖动目标,分类目录或者指标"
 		return
 	}
 	//判断分类是否存在
-	ExcelClassifyInfo, err := excel.GetExcelClassifyById(req.ClassifyId)
+	err, errMsg := excel2.MoveExcelClassify(req)
 	if err != nil {
-		br.Msg = "移动失败"
-		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
 		return
 	}
-	updateCol := make([]string, 0)
 
-	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
-	if ExcelClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
-		parentExcelClassifyInfo, err := excel.GetExcelClassifyById(req.ParentClassifyId)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
-			return
-		}
-		ExcelClassifyInfo.ParentId = parentExcelClassifyInfo.ExcelClassifyId
-		ExcelClassifyInfo.Level = parentExcelClassifyInfo.Level + 1
-		ExcelClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
-	}
-
-	//如果有传入 上一个兄弟节点分类id
-	if req.PrevClassifyId > 0 {
-		//上一个兄弟节点
-		prevClassify, err := excel.GetExcelClassifyById(req.PrevClassifyId)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
-			return
-		}
-
-		//如果是移动在两个兄弟节点之间
-		if req.NextClassifyId > 0 {
-			//下一个兄弟节点
-			nextClassify, err := excel.GetExcelClassifyById(req.NextClassifyId)
-			if err != nil {
-				br.Msg = "移动失败"
-				br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
-				return
-			}
-			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
-			if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == ExcelClassifyInfo.Sort {
-				//变更兄弟节点的排序
-				updateSortStr := `sort + 2`
-				_ = excel.UpdateExcelClassifySortByParentId(prevClassify.ParentId, prevClassify.ExcelClassifyId, prevClassify.Sort, updateSortStr)
-			} else {
-				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
-				if nextClassify.Sort-prevClassify.Sort == 1 {
-					//变更兄弟节点的排序
-					updateSortStr := `sort + 1`
-					_ = excel.UpdateExcelClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
-				}
-			}
-		}
-
-		ExcelClassifyInfo.Sort = prevClassify.Sort + 1
-		ExcelClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
-
-	} else {
-		firstClassify, err := excel.GetFirstExcelClassifyByParentId(ExcelClassifyInfo.ParentId)
-		if err != nil && err.Error() != utils.ErrNoRow() {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
-			return
-		}
-
-		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-		if firstClassify != nil && firstClassify.Sort == 0 {
-			updateSortStr := ` sort + 1 `
-			_ = excel.UpdateExcelClassifySortByParentId(firstClassify.ParentId, firstClassify.ExcelClassifyId-1, 0, updateSortStr)
-		}
-
-		ExcelClassifyInfo.Sort = 0 //那就是排在第一位
-		ExcelClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
-	}
-
-	//更新
-	if len(updateCol) > 0 {
-		err = ExcelClassifyInfo.Update(updateCol)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "修改失败,Err:" + err.Error()
-			return
-		}
-	}
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true

+ 27 - 3
controllers/data_manage/excel/excel_info.go

@@ -203,6 +203,14 @@ func (c *ExcelInfoController) Add() {
 		}
 	}
 
+	//获取该层级下最大的排序数
+	maxSort, err := excel2.GetExcelClassifyMaxSort(req.ExcelClassifyId, req.Source)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "查询排序信息失败,Err:" + err.Error()
+		return
+	}
+
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 	excelInfo := &excel3.ExcelInfo{
 		//ExcelInfoId:     0,
@@ -215,7 +223,7 @@ func (c *ExcelInfoController) Add() {
 		SysUserRealName: sysUser.RealName,
 		Content:         content,
 		ExcelImage:      req.ExcelImage,
-		Sort:            0,
+		Sort:            maxSort + 1,
 		IsDelete:        0,
 		ModifyTime:      time.Now(),
 		CreateTime:      time.Now(),
@@ -346,8 +354,24 @@ func (c *ExcelInfoController) List() {
 			br.ErrMsg = "获取信息失败,GetExcelClassify,Err:" + err.Error()
 			return
 		}
-		condition += " AND excel_classify_id = ? "
-		pars = append(pars, excelClassifyId)
+
+		childClassify, e := excel3.GetChildClassifyById(excelClassifyId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+			return
+		}
+		if len(childClassify) == 0 {
+			condition += " AND excel_classify_id = ? "
+			pars = append(pars, excelClassifyId)
+		} else {
+			classifyIds := []int{excelClassifyId}
+			for _, v := range childClassify {
+				classifyIds = append(classifyIds, v.ExcelClassifyId)
+			}
+			condition += fmt.Sprintf(` AND excel_classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+			pars = append(pars, classifyIds)
+		}
 	}
 	if keyword != "" {
 		condition += ` AND  ( excel_name LIKE ? )`

+ 98 - 9
controllers/data_manage/excel/mixed_table.go

@@ -7,7 +7,11 @@ import (
 	"eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/services/data"
 	excel2 "eta/eta_api/services/data/excel"
+	"eta/eta_api/utils"
+	"fmt"
 	"strconv"
+	"strings"
+	"time"
 )
 
 // GetSystemDate
@@ -38,7 +42,7 @@ func (c *ExcelInfoController) GetSystemDate() {
 		return
 	}
 
-	date, err, errMsg := excel2.HandleDate(req.DataTimeType, req.Value)
+	date, _, err, errMsg := excel2.HandleDate(req.DataTimeType, req.Value)
 	if err != nil {
 		br.Msg = "获取系统日期失败"
 		if errMsg != `` {
@@ -78,7 +82,7 @@ func (c *ExcelInfoController) CalculateData() {
 		br.Ret = 408
 		return
 	}
-
+	requestBody := string(c.Ctx.Input.RequestBody)
 	var req request.CalculateConf
 	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
 	if err != nil {
@@ -155,26 +159,45 @@ func (c *ExcelInfoController) CalculateData() {
 		num = lenDate
 	}
 
-	var currDate string // 当前日期
 	dataListResp := make([]*data_manage.EdbDataList, 0)
 
+	var newDate string
+	if req.DataTime == `` { //选择前移几期数
+		newDate, err = excel2.GetEdbDateByMoveForwardByDateList(requestBody, respItem.Data.DateList)
+		if err != nil {
+			return
+		}
+	} else {
+		newDate = req.DataTime //选择表格中的日期
+	}
+	// 开始做日期变换
+	newDate, err = excel2.HandleMixTableDateChange(newDate, requestBody)
+	if err != nil {
+		return
+	}
+	var finalVal float64
 	if req.DataTime == `` {
 		for i := 1; i <= num; i++ {
 			date := respItem.Data.DateList[lenDate-i]
+
 			val, ok := respItem.Data.DataMap[date]
 			if !ok {
 				continue
 			}
-
+			if date == newDate {
+				finalVal = val
+			}
 			dataListResp = append(dataListResp, &data_manage.EdbDataList{
 				Value:    val,
 				DataTime: date,
 			})
 		}
 	} else {
-		if val, ok := respItem.Data.DataMap[req.DataTime]; ok {
+		// todo 如果选择了表格中的日期应该如何处理
+		if val, ok := respItem.Data.DataMap[newDate]; ok {
+			finalVal = val
 			for i, tmpDate := range respItem.Data.DateList {
-				if tmpDate == req.DataTime {
+				if tmpDate == newDate {
 					if i+3 <= lenDate {
 						t1Date := respItem.Data.DateList[i+2]
 						if tmpVal, ok2 := respItem.Data.DataMap[t1Date]; ok2 {
@@ -200,7 +223,7 @@ func (c *ExcelInfoController) CalculateData() {
 					// 当前日期
 					dataListResp = append(dataListResp, &data_manage.EdbDataList{
 						Value:    val,
-						DataTime: req.DataTime,
+						DataTime: newDate,
 					})
 					if i >= 1 {
 						t1Date := respItem.Data.DateList[i-1]
@@ -227,11 +250,77 @@ func (c *ExcelInfoController) CalculateData() {
 		}
 	}
 	resp := data_manage.BeforeAndAfterDateDataResp{
-		List: dataListResp,
-		Date: currDate,
+		List:      dataListResp,
+		Date:      newDate,
+		ShowValue: utils.FormatTableDataShowValue(finalVal),
 	}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "计算成功"
 	br.Data = resp
 }
+
+// GetMixDateCalculate
+// @Title 获取混合表格日期计算
+// @Description 获取混合表格日期计算
+// @Param	request	body request.MixedTableCellDataReq true "type json string"
+// @router /excel_info/mixed/date_calculate [post]
+func (c *ExcelInfoController) GetMixDateCalculate() {
+	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
+	}
+
+	var req request.MixedDateCalculateReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	valMap := make(map[string]int)
+	for _, v := range req.DateList {
+		// 查找单元格数据
+		// 如果不是基础计算单元格,直接返回
+		_, err = time.ParseInLocation(utils.FormatDate, v.Date, time.Local)
+		if err != nil {
+			br.Msg = "日期计算失败!"
+			br.ErrMsg = fmt.Sprintf("%s 的单元格非日期类型, Err: %s", v.Date, err.Error())
+			return
+		}
+		// todo 把日期转换成excel里的天数
+		realDiffDay := utils.GetDaysDiff1900(v.Date)
+
+		valMap[strings.ToUpper(v.Tag)] = realDiffDay
+	}
+
+	// 计算
+	val, errMsg, err := excel2.DateCalculateFormula(valMap, strings.ToUpper(req.Formula))
+	if err != nil {
+		br.Msg = "日期计算失败"
+		br.ErrMsg = errMsg
+		return
+	}
+
+	showValue := utils.FormatTableDataShowValue(val)
+
+	type resp struct {
+		ShowValue string
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.Data = resp{
+		ShowValue: showValue,
+	}
+}

+ 3 - 2
models/data_manage/edb_info.go

@@ -1652,8 +1652,9 @@ type TraceEdbInfoResp struct {
 
 // BeforeAndAfterDateDataResp 前后几期数据
 type BeforeAndAfterDateDataResp struct {
-	List []*EdbDataList `description:"list"`
-	Date string         `description:"实际日期"`
+	List      []*EdbDataList `description:"list"`
+	Date      string         `description:"实际日期"`
+	ShowValue string         `description:"展示值"`
 }
 
 // GetEdbInfoAdminList

+ 7 - 0
models/data_manage/excel/excel_classify.go

@@ -49,6 +49,13 @@ func GetExcelClassifyById(classifyId int) (item *ExcelClassify, err error) {
 	return
 }
 
+func GetChildClassifyById(classifyId int) (items []*ExcelClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_classify WHERE parent_id=? AND is_delete=0 `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
 func GetExcelClassifyByParentId(parentId, source int) (items []*ExcelClassifyItems, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT * FROM excel_classify WHERE parent_id=? AND source = ? AND is_delete=0 order by sort asc,excel_classify_id asc`

+ 9 - 0
models/data_manage/excel/excel_info.go

@@ -256,6 +256,7 @@ func GetFirstExcelInfoByClassifyId(classifyId int) (item *ExcelInfo, err error)
 func UpdateExcelInfoSortByClassifyId(classifyId, nowSort, prevExcelInfoId int, updateSort string) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` update excel_info set sort = ` + updateSort + ` WHERE excel_classify_id=? AND is_delete=0 AND ( sort > ? `
+	// todo 前一个兄弟节点后移
 	if prevExcelInfoId > 0 {
 		sql += ` or (excel_info_id < ` + fmt.Sprint(prevExcelInfoId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
 	}
@@ -516,3 +517,11 @@ type BatchRefreshExcelReq struct {
 	ReportChapterId int      `description:"报告章节ID"`
 	Source          string   `description:"来源,枚举值:report、english_report、smart_report"`
 }
+
+// GetExcelMaxSortByClassifyId 获取当前分类下,且排序数最大的excel
+func GetExcelMaxSortByClassifyId(classifyId int, source int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT Max(sort) AS sort FROM excel_info WHERE excel_classify_id=? AND source = ? AND is_delete=0 order by sort desc,excel_info_id desc limit 1`
+	err = o.Raw(sql, classifyId, source).QueryRow(&sort)
+	return
+}

+ 6 - 1
models/data_manage/excel/request/excel_classify.go

@@ -17,9 +17,14 @@ type EditExcelClassifyReq struct {
 // MoveExcelClassifyReq 移动图表分类请求参数
 type MoveExcelClassifyReq struct {
 	ClassifyId       int `description:"分类id"`
-	ParentClassifyId int `description:"父级分类id" json:"-"`
+	ParentClassifyId int `description:"父级分类id"`
 	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
 	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+
+	ExcelInfoId     int `description:"excel表格ID"`
+	PrevExcelInfoId int `description:"上一个excel表格ID"`
+	NextExcelInfoId int `description:"下一个excel表格ID"`
+	Source          int
 }
 
 type DeleteExcelClassifyReq struct {

+ 66 - 2
models/data_manage/excel/request/mixed_table.go

@@ -9,6 +9,7 @@ const (
 	PopInsertDataDT                     // 5 弹框插值(在表格上选择日期,然后空白单元格选择弹框并选择指标,插入该指标与该日期的值)
 	FormulateCalculateDataDT            // 6 公式计算(A+B这种)
 	InsertEdbCalculateDataDT            // 7 插入指标系统计算公式生成的值
+	DateCalculateDataDT                 // 8 日期计算
 )
 
 // 单元格的日期类型类型
@@ -16,6 +17,7 @@ const (
 	CustomDateT = iota //手动输入日期
 	SystemDateT        // 系统日期
 	EdbDateDT          // 导入指标日期(指标库的最新日期)
+
 )
 
 // 单元格的日期类型类型
@@ -36,11 +38,13 @@ type MixedTableCellDataReq struct {
 	Uid          string `description:"单元格唯一标识"`
 	DataType     int    `description:"数据类型,1:日期,2:指标,3:自定义文本,4:插值"`
 	DataTime     string `description:"所属日期"`
-	DataTimeType int    `description:"日期类型:0:手动输入日期;1:导入系统日期;;3:导入指标日期(指标库的最新日期);"`
+	DataTimeType int    `description:"日期类型:0:手动输入日期(固定日期);1:导入系统日期;;3:导入指标日期(指标库的最新日期);"`
 	EdbInfoId    int    `description:"指标id"`
 	ShowValue    string `description:"展示值"`
-	Value        string `description:"实际值"`
+	Value        string `description:"实际值和配置"`
 	Extra        string `description:"额外参数"`
+	ShowStyle    string `description:"展示的样式配置"`
+	RealValue    string `description:"实际的值"`
 }
 
 // CellRelationConf
@@ -74,6 +78,64 @@ type SystemDateConf struct {
 // @Description: 导入指标日期配置
 type EdbDateConf struct {
 	EdbInfoId int `description:"指标id"`
+	EdbDateChangeConf
+	CustomerDate string `description:"自定义日期"`
+}
+
+// EdbDateExtraConf
+// @Description: 导入指标日期前移和日期变换
+type EdbDateChangeConf struct {
+	MoveForward int `description:"前移的期数"`
+	DateChange  []*EdbDateConfDateChange
+}
+
+type EdbDateConfDateChange struct {
+	DateCalculate   *EdbDateConfDateCalculate
+	FrequencyChange *EdbDateConfFrequencyChange
+}
+type EdbDateConfDateCalculate struct {
+	Year  int
+	Month int
+	Day   int
+}
+
+type EdbDateConfFrequencyChange struct {
+	Frequency    string `description:"频度变换"`
+	FrequencyDay string `description:"频度的固定日期"`
+}
+
+// MixedDateCalculateReq 混合表格日期计算
+type MixedDateCalculateReq struct {
+	Formula  string                   `description:"计算公式"`
+	DateList []MixDateCalculateTagReq `description:"表格中的单元格"`
+}
+
+// MixDateCalculateConf 混合表格中的日期计算
+type MixDateCalculateConf struct {
+	Formula          string                `description:"计算公式"`
+	RelationCellList []MixDateCalculateTag `description:"表格中的单元格"`
+}
+
+// MixDateCalculateTag 混合表格中的日期计算的关联单元格
+type MixDateCalculateTag struct {
+	Uid string `description:"单元格唯一值"`
+	Tag string `description:"指标对应标签"`
+}
+
+// MixDateCalculateTagReq 混合表格中的日期计算的关联单元格
+type MixDateCalculateTagReq struct {
+	Date string `description:"日期"`
+	Tag  string `description:"指标对应标签"`
+}
+
+// MixCellShowStyle 混合表格 单元格样式展示计算
+type MixCellShowStyle struct {
+	NumberStyle MixNumberShowStyle `description:"小数点位数增加或减少,百分比"`
+}
+
+type MixNumberShowStyle struct {
+	AddPointNum int `description:"显示的计算类型:小数点位数增加或减少,正数表述增加,负数表示减少"`
+	IsPercent   int `description:"是否是百分比1 是,0否, 将选中的单元格的样式设置成百分比"`
 }
 
 // CalculateConf
@@ -87,6 +149,8 @@ type CalculateConf struct {
 	MoveType      int         `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency string      `description:"移动频度"`
 	Source        int         `description:"1:累计值转月;2:累计值转季;3:同比值;4:同差值;5:N数值移动平均数计算;6:环比值;7:环差值;8:升频;9:降频;10:时间移位;11:超季节性;12:年化;13:累计值;14:累计值年初至今;15:指数修匀;16:日均值"`
+
+	EdbDateChangeConf
 }
 
 // BaseCalculateConf

+ 9 - 0
routers/commentsRouter.go

@@ -718,6 +718,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "GetMixDateCalculate",
+            Router: `/excel_info/mixed/date_calculate`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "Move",

+ 429 - 0
services/data/excel/excel_classify.go

@@ -0,0 +1,429 @@
+package excel
+
+import (
+	"errors"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/data_manage/excel/request"
+	"eta/eta_api/utils"
+	"time"
+)
+
+func GetExcelClassifyMaxSort(parentId int, source int) (maxSort int, err error) {
+	//获取该层级下最大的排序数
+	maxSort, err = excel.GetExcelClassifyMaxSort(parentId)
+	if err != nil {
+		return
+	}
+	edbMaxSort, err := excel.GetExcelMaxSortByClassifyId(parentId, source)
+	if err != nil {
+		return
+	}
+	if maxSort < edbMaxSort {
+		maxSort = edbMaxSort
+	}
+	return
+}
+
+// MoveExcelClassify 移动指标分类
+func MoveExcelClassify(req request.MoveExcelClassifyReq) (err error, errMsg string) {
+	classifyId := req.ClassifyId
+	parentClassifyId := req.ParentClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	excelInfoId := req.ExcelInfoId
+	prevExcelInfoId := req.PrevExcelInfoId
+	nextExcelInfoId := req.NextExcelInfoId
+	source := req.Source
+	//首先确定移动的对象是分类还是指标
+	//判断上一个节点是分类还是指标
+	//判断下一个节点是分类还是指标
+	//同时更新分类目录下的分类sort和指标sort
+	//更新当前移动的分类或者指标sort
+
+	var parentExcelClassifyInfo *excel.ExcelClassify
+	if parentClassifyId > 0 {
+		parentExcelClassifyInfo, err = excel.GetExcelClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		excelClassifyInfo *excel.ExcelClassify
+		prevClassify      *excel.ExcelClassify
+		nextClassify      *excel.ExcelClassify
+
+		excelInfo     *excel.ExcelInfo
+		prevExcelInfo *excel.ExcelInfo
+		nextExcelInfo *excel.ExcelInfo
+		prevSort      int
+		nextSort      int
+	)
+
+	// 移动对象为分类, 判断权限
+	if excelInfoId == 0 {
+		excelClassifyInfo, err = excel.GetExcelClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前分类不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+
+	} else {
+		excelInfo, err = excel.GetExcelInfoById(excelInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前表格不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId == 0 {
+			errMsg = "移动失败,表格必须挂在分类下"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	if prevClassifyId > 0 {
+		prevClassify, err = excel.GetExcelClassifyById(prevClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevClassify.Sort
+	} else if prevExcelInfoId > 0 {
+		prevExcelInfo, err = excel.GetExcelInfoById(prevExcelInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevExcelInfo.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = excel.GetExcelClassifyById(nextClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextClassify.Sort
+	} else if nextExcelInfoId > 0 {
+		//下一个兄弟节点
+		nextExcelInfo, err = excel.GetExcelInfoById(nextExcelInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextExcelInfo.Sort
+	}
+
+	err, errMsg = moveExcelClassify(parentExcelClassifyInfo, excelClassifyInfo, prevClassify, nextClassify, excelInfo, prevExcelInfo, nextExcelInfo, parentClassifyId, prevSort, nextSort, source)
+	return
+}
+
+// moveExcelClassify 移动表格分类
+func moveExcelClassify(parentExcelClassifyInfo, excelClassifyInfo, prevClassify, nextClassify *excel.ExcelClassify, excelInfo, prevExcelInfo, nextExcelInfo *excel.ExcelInfo, parentClassifyId int, prevSort, nextSort int, source int) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+
+	// 移动对象为分类, 判断分类是否存在
+	if excelClassifyInfo != nil {
+		oldParentId := excelClassifyInfo.ParentId
+		/*oldLevel := excelClassifyInfo.Level
+		var classifyIds []int*/
+		if oldParentId != parentClassifyId {
+			//todo 更新子分类对应的level
+			/*childList, e, m := GetChildClassifyByClassifyId(excelClassifyInfo.ClassifyId)
+			if e != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
+				return
+			}
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.ClassifyId == excelClassifyInfo.ClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.ClassifyId)
+				}
+			}*/
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if excelClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			if excelClassifyInfo.Level != parentExcelClassifyInfo.Level+1 { //禁止层级调整
+				errMsg = "移动失败"
+				err = errors.New("不支持目录层级变更")
+				return
+			}
+			excelClassifyInfo.ParentId = parentExcelClassifyInfo.ExcelClassifyId
+			excelClassifyInfo.Level = parentExcelClassifyInfo.Level + 1
+			excelClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		} else if excelClassifyInfo.ParentId != parentClassifyId && parentClassifyId == 0 {
+			errMsg = "移动失败"
+			err = errors.New("不支持目录层级变更")
+			return
+		}
+
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == excelClassifyInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, prevClassify.ExcelClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更表格
+					if prevExcelInfo != nil {
+						//变更兄弟节点的排序
+						_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, prevExcelInfo.ExcelInfoId, updateSortStr)
+					} else {
+						_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, prevClassify.ExcelClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更表格
+						if prevExcelInfo != nil {
+							//变更兄弟节点的排序
+							_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, prevExcelInfo.ExcelInfoId, updateSortStr)
+						} else {
+							_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+
+					}
+				}
+			}
+
+			excelClassifyInfo.Sort = prevSort + 1
+			excelClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevExcelInfo == nil && nextExcelInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetExcelClassifyMaxSort(parentClassifyId, source)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			excelClassifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			excelClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstEdbClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, firstClassify.ClassifyId-1, 0, updateSortStr)
+				//该分类下的所有表格也需要+1
+				_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在表格,且第一个表格的排序等于0,那么需要调整排序
+				firstExcel, tErr := excel.GetFirstExcelInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstExcel != nil && firstExcel.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, 0, firstExcel.ExcelInfoId-1, updateSortStr)
+					_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			excelClassifyInfo.Sort = 0 //那就是排在第一位
+			excelClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = excelClassifyInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+			//更新对应分类的root_id和层级
+			if oldParentId != parentClassifyId {
+				/*if len(classifyIds) > 0 {
+					levelStep := excelClassifyInfo.Level - oldLevel
+					err = data_manage.UpdateEdbClassifyChildByParentClassifyId(classifyIds, excelClassifyInfo.RootId, levelStep)
+					if err != nil {
+						errMsg = "移动失败"
+						err = errors.New("更新子分类失败,Err:" + err.Error())
+						return
+					}
+				}*/
+			}
+		}
+	} else {
+		if excelInfo == nil {
+			errMsg = "当前表格不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		//如果改变了分类,那么移动该表格数据
+		if excelInfo.ExcelClassifyId != parentClassifyId {
+			excelInfo.ExcelClassifyId = parentClassifyId
+			excelInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ClassifyId", "ModifyTime")
+		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == excelInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, prevClassify.ExcelClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更表格
+					if prevExcelInfo != nil {
+						//变更兄弟节点的排序
+						_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, prevExcelInfo.ExcelInfoId, updateSortStr)
+					} else {
+						_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, prevClassify.ExcelClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更表格
+						if prevExcelInfo != nil {
+							//变更兄弟节点的排序
+							_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, prevExcelInfo.ExcelInfoId, updateSortStr)
+						} else {
+							_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+					}
+				}
+			}
+
+			excelInfo.Sort = prevSort + 1
+			excelInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevExcelInfo == nil && nextExcelInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetExcelClassifyMaxSort(parentClassifyId, source)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			excelInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			excelInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := excel.GetFirstExcelClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, firstClassify.ExcelClassifyId-1, 0, updateSortStr)
+				//该分类下的所有表格也需要+1
+				_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在表格,且第一个表格的排序等于0,那么需要调整排序
+				firstExcel, tErr := excel.GetFirstExcelInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstExcel != nil && firstExcel.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = excel.UpdateExcelInfoSortByClassifyId(parentClassifyId, 0, firstExcel.ExcelInfoId-1, updateSortStr)
+					_ = excel.UpdateExcelClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			excelInfo.Sort = 0 //那就是排在第一位
+			excelInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = excelInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+	return
+}

+ 491 - 30
services/data/excel/mixed_table.go

@@ -119,9 +119,19 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 				if err != nil {
 					return
 				}
-				if edbInfo, ok := edbInfoMap[edbDateConfig.EdbInfoId]; ok {
-					cell.ShowValue = edbInfo.EndDate
-					cell.DataTime = edbInfo.EndDate
+				if dataList, ok := edbDataListMap[edbDateConfig.EdbInfoId]; ok {
+					// todo 获取配置信息,根据配置信息进行日期变换, 是否需要更新当前记录,将历史记录逐渐转换成新的记录
+					var newDate string
+					newDate, err = GetEdbDateByMoveForward(config[k][i].Value, dataList)
+					if err != nil {
+						return
+					}
+					newDate, err = HandleMixTableDateChange(newDate, config[k][i].Value)
+					if err != nil {
+						return
+					}
+					cell.ShowValue = newDate
+					cell.DataTime = newDate
 					config[k][i] = cell
 				}
 			}
@@ -133,7 +143,7 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 	}
 
 	// 指标计算的结果map
-	edbSourceDataMap := make(map[string]map[string]float64)
+	edbSourceDataMap := make(map[string]data.BaseCalculateDataResp)
 
 	// 单元格对应的key与他的值(只处理数据类型)
 	cellKeyVal := make(map[string]float64)
@@ -141,18 +151,28 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 	// 基础计算单元格的位置信息
 	calculateCellMap := make(map[string]Cell)
 	calculateChainList := make([]string, 0)
-
+	dateCalculateList := make([]string, 0)
+	showStyleList := make([]string, 0)
 	// 处理单元格中的数据类型(除去基础计算,因为这个是依赖于其他)
 	for k, row := range config {
 		for i, cell := range row {
+			cell.RealValue = cell.ShowValue
 			switch cell.DataType {
+			case request.DateDT: // 日期类型
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
 			case request.EdbDT: // 指标类型
 				if cell.Value == `` {
 					if edbInfo, ok := edbInfoMap[cell.EdbInfoId]; ok {
 						cell.ShowValue = edbInfo.EdbName
+						cell.RealValue = cell.ShowValue
 					}
 				} else {
 					cell.ShowValue = cell.Value
+					cell.RealValue = cell.ShowValue
 				}
 			case request.InsertDataDT, request.PopInsertDataDT: // 数据类型
 				// 数值先清空
@@ -160,7 +180,7 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 				//cell.Value = ``
 
 				// 日期关系配置不存在,则默认最新数据
-				if relationConf, ok := cellRelationConfMap[cell.Uid]; ok {
+				if relationConf, ok := cellRelationConfMap[cell.Uid]; ok { //表示表格日期
 					if relationConf.RelationDate.Key == `` {
 						// 日期关系配置未绑定
 						continue
@@ -178,11 +198,22 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 					if dateValMap, ok := edbDayDataListMap[cell.EdbInfoId]; ok {
 						tmpDateValMap = dateValMap
 					}
+					// todo 根据配置进行日期变换
+					relationDate := relationCell.DataTime
+					if strings.Contains(cell.Value, "{") {
+						relationDate, err = HandleMixTableDateChange(relationDate, cell.Value)
+						if err != nil {
+							return
+						}
+					} else {
+						cell.Value = ""
+					}
 
-					if val, ok2 := tmpDateValMap[relationCell.DataTime]; ok2 {
+					if val, ok2 := tmpDateValMap[relationDate]; ok2 {
 						//cell.ShowValue = fmt.Sprint(val)
 						cellKeyVal[cell.Uid] = val
 						cell.ShowValue = utils.FormatTableDataShowValue(val)
+						cell.RealValue = cell.ShowValue
 					}
 				} else {
 					// 如果不是取得一个关联的日期,那么就是指定日期
@@ -192,8 +223,29 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 						if dateValList, ok := edbDataListMap[cell.EdbInfoId]; ok {
 							tmpLenData := len(dateValList)
 							if tmpLenData > 0 {
-								cellKeyVal[cell.Uid] = dateValList[tmpLenData-1].Value
+								//做期数前移动和日期变换
+								if !strings.Contains(cell.Value, "{") {
+									cell.Value = ""
+								}
+								var newDate string
+								newDate, err = GetEdbDateByMoveForward(cell.Value, dateValList)
+								if err != nil {
+									return
+								}
+								newDate, err = HandleMixTableDateChange(newDate, cell.Value)
+								if err != nil {
+									return
+								}
+								var finalVal float64
+								for _, v := range dateValList {
+									if v.DataTime == newDate {
+										finalVal = v.Value
+										break
+									}
+								}
+								cellKeyVal[cell.Uid] = finalVal
 								cell.ShowValue = utils.FormatTableDataShowValue(dateValList[tmpLenData-1].Value)
+								cell.RealValue = cell.ShowValue
 							}
 						}
 					} else {
@@ -215,6 +267,7 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 							//cell.ShowValue = fmt.Sprint(val)
 							cellKeyVal[cell.Uid] = val
 							cell.ShowValue = utils.FormatTableDataShowValue(val)
+							cell.RealValue = cell.ShowValue
 						}
 					}
 				}
@@ -254,6 +307,7 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 				// 日期
 				var cellDateTime string
 				// 日期关系配置不存在,则默认最新数据
+				// 从绑定的单元格中获取数据的日期
 				if relationConf, ok := cellRelationConfMap[cell.Uid]; ok {
 					if relationConf.RelationDate.Key == `` {
 						// 日期关系配置未绑定
@@ -271,7 +325,7 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 				var tmpDataMap map[string]float64
 
 				key := utils.MD5(cell.Value)
-				tmpDataMap, ok := edbSourceDataMap[key]
+				respItemData, ok := edbSourceDataMap[key]
 				if !ok {
 					// 对应的配置值
 					var tmpConfig request.CalculateConf
@@ -318,21 +372,50 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 						continue
 					}
 
-					tmpDataMap = respItem.Data.DataMap
+					//tmpDataMap = respItem.Data.DataMap
 					// 计算结果存一份,万一存在重复的计算方式,那么省的重新计算一下
-					edbSourceDataMap[key] = tmpDataMap
-
-					lenDataList := len(respItem.Data.DateList)
-					if cellDateTime == `` && lenDataList > 0 {
-						cellDateTime = respItem.Data.DateList[lenDataList-1]
+					edbSourceDataMap[key] = respItem.Data
+				}
+				lenDataList := len(respItemData.DateList)
+				tmpDataMap = respItemData.DataMap
+				if cellDateTime == `` && lenDataList > 0 {
+					//判断是否需要做期数前移动
+					cellDateTime = respItemData.DateList[lenDataList-1]
+					cellDateTime, err = GetEdbDateByMoveForwardByDateList(cell.Value, respItemData.DateList)
+					if err != nil {
+						utils.FileLog.Error(fmt.Sprintf("日期前移失败,配置信息;%s", cell.Value))
+						continue
 					}
 				}
-
+				// 进行日期变换
+				cellDateTime, err = HandleMixTableDateChange(cellDateTime, cell.Value)
+				if err != nil {
+					utils.FileLog.Error(fmt.Sprintf("日期变换失败,配置信息;%s, 日期:%s", cell.Value, cellDateTime))
+					continue
+				}
 				val := tmpDataMap[cellDateTime]
 				cellKeyVal[cell.Uid] = val
 				cell.ShowValue = utils.FormatTableDataShowValue(val)
+				cell.RealValue = cell.ShowValue
+			case request.DateCalculateDataDT: //日期计算
+				// 把关联的单元格存在数组里
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
+				dateCalculateList = append(dateCalculateList, cell.Uid)
+				// 遍历数组,根据公式进行计算,并将得到的结果放到对应的单元格中
 			}
 
+			if cell.ShowValue != `` {
+				calculateCellMap[cell.Uid] = Cell{
+					Column:   k,
+					Row:      i,
+					CellInfo: cell,
+				}
+				showStyleList = append(showStyleList, cell.Uid)
+			}
 			row[i] = cell
 		}
 		config[k] = row
@@ -365,11 +448,24 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq) (newMixedTableCe
 
 			cellKeyVal[cell.Uid] = val
 			cell.ShowValue = utils.FormatTableDataShowValue(val)
+			cell.RealValue = cell.ShowValue
 			config[cellPosition.Column][cellPosition.Row] = cell
 
 		}
 	}
 
+	// 日期计算
+	config, err, errMsg = handlerDateCalculate(dateCalculateList, calculateCellMap, config)
+	if err != nil {
+		return
+	}
+
+	// todo 样式调整
+	config, err, errMsg = handleMixCellShowStyle(showStyleList, calculateCellMap, config)
+	if err != nil {
+		return
+	}
+
 	newMixedTableCellDataList = config
 
 	return
@@ -479,7 +575,7 @@ func handleConfig(configList [][]request.MixedTableCellDataReq) (newConfig [][]r
 				dataEdbInfoIdList = append(dataEdbInfoIdList, cell.EdbInfoId)
 
 			case request.DateDT: // 日期类型
-				date, tmpErr, tmpErrMsg := handleDate(cell.DataTimeType, cell.Value)
+				date, newVal, tmpErr, tmpErrMsg := handleDate(cell.DataTimeType, cell.Value)
 				if tmpErr != nil {
 					err = tmpErr
 					errMsg = tmpErrMsg
@@ -487,6 +583,7 @@ func handleConfig(configList [][]request.MixedTableCellDataReq) (newConfig [][]r
 				}
 				rowList[rk].DataTime = date
 				rowList[rk].ShowValue = date
+				rowList[rk].Value = newVal //兼容原有的历史数据中系统导入日期
 
 				// 指标日期类型的单元格需要额外将指标id取出来
 				if cell.DataTimeType == request.EdbDateDT {
@@ -516,7 +613,7 @@ func handleConfig(configList [][]request.MixedTableCellDataReq) (newConfig [][]r
 // @return date string
 // @return err error
 // @return errMsg string
-func HandleDate(dataTimeType int, val string) (date string, err error, errMsg string) {
+func HandleDate(dataTimeType int, val string) (date string, newVal string, err error, errMsg string) {
 	return handleDate(dataTimeType, val)
 }
 
@@ -529,17 +626,39 @@ func HandleDate(dataTimeType int, val string) (date string, err error, errMsg st
 // @return date string
 // @return err error
 // @return errMsg string
-func handleDate(dataTimeType int, val string) (date string, err error, errMsg string) {
+func handleDate(dataTimeType int, val string) (date string, newVal string, err error, errMsg string) {
+	newVal = val
 	if val == `` {
 		errMsg = "错误的日期数据"
 		err = errors.New(errMsg)
 		return
 	}
+
 	switch dataTimeType {
 	case request.CustomDateT: //手动输入日期
+		/*if !strings.Contains(val, "{") {
+			newVal, err, errMsg = handleOldCustomerDateT(val)
+			if err != nil {
+				return
+			}
+		}
+		date, err = HandleMixTableDateChange("", newVal)
+		if err != nil {
+			return
+		}*/
 		date = val
+		return
 	case request.SystemDateT: // 系统日期
-		date, err, errMsg = handleSystemDateT(val)
+		date = time.Now().Format(utils.FormatDate)
+		newVal, err, errMsg = handleOldSystemDateT(val)
+		if err != nil {
+			return
+		}
+		date, err = HandleMixTableDateChange(date, newVal)
+		if err != nil {
+			return
+		}
+
 	case request.EdbDateDT: // 导入指标日期(指标库的最新日期)
 	default:
 		errMsg = "错误的日期类型"
@@ -550,30 +669,154 @@ func handleDate(dataTimeType int, val string) (date string, err error, errMsg st
 	return
 }
 
-// handleSystemDateT
-// @Description: 处理导入系统日期
+func GetEdbDateByMoveForward(conf string, edbDataList []*data_manage.EdbDataList) (date string, err error) {
+	dateList := make([]string, 0)
+	for _, v := range edbDataList {
+		dateList = append(dateList, v.DataTime)
+	}
+
+	date, err = GetEdbDateByMoveForwardByDateList(conf, dateList)
+	return
+}
+
+func GetEdbDateByMoveForwardByDateList(conf string, dateList []string) (date string, err error) {
+	moveForward := 0
+	if conf != "" {
+		var edbDateConf request.EdbDateChangeConf
+		err = json.Unmarshal([]byte(conf), &edbDateConf)
+		if err != nil {
+			err = fmt.Errorf("日期变换配置json解析失败失败: %s", err.Error())
+			return
+		}
+		moveForward = edbDateConf.MoveForward
+	}
+	// 根据日期进行排序
+	index := len(dateList) - 1 - moveForward
+	for k, v := range dateList {
+		if k == index {
+			date = v
+			return
+		}
+	}
+	return
+}
+
+// HandleMixTableDateChange 处理表格中的日期变换
+func HandleMixTableDateChange(date, conf string) (newDate string, err error) {
+	newDate = date
+	if conf == "" {
+		return
+	}
+	var edbDateConf request.EdbDateConf
+	err = json.Unmarshal([]byte(conf), &edbDateConf)
+	if err != nil {
+		err = fmt.Errorf("日期变换配置json解析失败失败: %s, Err:%s", conf, err.Error())
+		return
+	}
+	if newDate == "" {
+		// 用户写死的固定日期
+		newDate = edbDateConf.CustomerDate
+	}
+	if newDate == "" {
+		err = fmt.Errorf("日期配置失败: %s", conf)
+		return
+	}
+	if len(edbDateConf.DateChange) > 0 {
+		var dateTime time.Time
+		dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+		if err != nil {
+			err = fmt.Errorf("日期解析失败: %s", err.Error())
+			return
+		}
+		for _, v := range edbDateConf.DateChange {
+			if v.DateCalculate != nil {
+				dateTime = dateTime.AddDate(v.DateCalculate.Year, v.DateCalculate.Month, v.DateCalculate.Day)
+				newDate = dateTime.Format(utils.FormatDate)
+			}
+
+			if v.FrequencyChange != nil {
+				newDate, err, _ = handleSystemAppointDateT(dateTime, v.FrequencyChange.FrequencyDay, v.FrequencyChange.Frequency)
+				if err != nil {
+					return
+				}
+				dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+				if err != nil {
+					err = fmt.Errorf("日期解析失败: %s", err.Error())
+					return
+				}
+			}
+		}
+	}
+	return
+}
+
+func handleOldCustomerDateT(confStr string) (newConf string, err error, errMsg string) {
+	date := confStr
+	dateConf := new(request.EdbDateConf)
+	dateConf.CustomerDate = date
+	newConfByte, e := json.Marshal(dateConf)
+	if e != nil {
+		err = fmt.Errorf("日期计算额外配置,json序列化失败: %s", e.Error())
+		return
+	}
+	newConf = string(newConfByte)
+	return
+}
+
+// handleOldSystemDateT
+// @Description: 历史数据中的导入系统日期
 // @author: Roc
 // @datetime2023-10-27 09:36:21
 // @param confStr string
 // @return date string
 // @return err error
 // @return errMsg string
-func handleSystemDateT(confStr string) (date string, err error, errMsg string) {
+func handleOldSystemDateT(confStr string) (newConf string, err error, errMsg string) {
 	var config request.SystemDateConf
 	err = json.Unmarshal([]byte(confStr), &config)
 	if err != nil {
 		return
 	}
+
+	newConfig := new(request.EdbDateConf)
+	dateChange := new(request.EdbDateConfDateChange)
+	dateCalculate := new(request.EdbDateConfDateCalculate)
+	frequencyChange := new(request.EdbDateConfFrequencyChange)
+	dateChangeList := make([]*request.EdbDateConfDateChange, 0)
+
 	switch config.Source {
 	case request.SystemCurrDateT:
-		date = time.Now().Format(utils.FormatDate)
+		return
 	case request.SystemCalculateDateT:
-		date, err, errMsg = handleSystemCalculateDateT(config.CalculateNum, config.CalculateFrequency)
+		// todo 是否直接更新该excel记录,
+		dateCalculate.Day = config.CalculateNum
+		dateChange.DateCalculate = dateCalculate
+		dateChangeList = append(dateChangeList, dateChange)
+		newConfig.DateChange = dateChangeList
+		newConfByte, e := json.Marshal(newConfig)
+		if e != nil {
+			err = fmt.Errorf("日期计算额外配置,json序列化失败: %s", e.Error())
+			return
+		}
+		newConf = string(newConfByte)
+		return
 	case request.SystemFrequencyDateT: // 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
-		date, err, errMsg = handleSystemAppointDateT(config.Day, config.Frequency)
+		frequencyChange.FrequencyDay = config.Day
+		frequencyChange.Frequency = config.Frequency
+
+		dateChange.FrequencyChange = frequencyChange
+		dateChangeList = append(dateChangeList, dateChange)
+		newConfig.DateChange = dateChangeList
+		newConfByte, e := json.Marshal(newConfig)
+		if e != nil {
+			err = fmt.Errorf("日期计算额外配置,json序列化失败: %s", e.Error())
+			return
+		}
+		newConf = string(newConfByte)
+		return
 	default:
-		errMsg = "错误的日期日期导入方式"
-		err = errors.New(fmt.Sprint("错误的日期日期导入方式:", config.Source))
+		//errMsg = "错误的日期日期导入方式"
+		//err = errors.New(fmt.Sprint("错误的日期日期导入方式:", config.Source))
 		return
 	}
 
@@ -615,8 +858,8 @@ func handleSystemCalculateDateT(num int, frequency string) (date string, err err
 // @return date string
 // @return err error
 // @return errMsg string
-func handleSystemAppointDateT(appointDay, frequency string) (date string, err error, errMsg string) {
-	currDate := time.Now()
+func handleSystemAppointDateT(currDate time.Time, appointDay, frequency string) (date string, err error, errMsg string) {
+	//currDate := time.Now()
 	switch frequency {
 	case "本周":
 		day := int(currDate.Weekday())
@@ -813,3 +1056,221 @@ func calculateByCellList(calculateFormula string, tagList []utils.CellPosition)
 
 	return
 }
+
+// handlerDateCalculate 处理日期计算
+func handlerDateCalculate(dateCalculateList []string, calculateCellMap map[string]Cell, oldConfig [][]request.MixedTableCellDataReq) (config [][]request.MixedTableCellDataReq, err error, errMsg string) {
+	config = oldConfig
+	if len(dateCalculateList) == 0 {
+		return
+	}
+	if len(dateCalculateList) > 0 {
+		for _, cellKey := range dateCalculateList {
+			// 查找这个单元格的位置,直接map找了,而不是遍历整个单元格
+			cellPosition, ok := calculateCellMap[cellKey]
+			if !ok {
+				utils.FileLog.Error("找不到单元格位置:", cellKey)
+				continue
+			}
+
+			cell := config[cellPosition.Column][cellPosition.Row]
+			if cell.DataType != request.DateCalculateDataDT { // 判断公式计算(A+B这种)类型,不是的话也过滤了
+				continue
+			}
+
+			val, tmpErr, tmpErrMsg := DateCalculatePrepare(calculateCellMap, cell.Value)
+			if tmpErr != nil {
+				errMsg = tmpErrMsg
+				err = tmpErr
+				return
+			}
+
+			cell.ShowValue = utils.FormatTableDataShowValue(val)
+			cell.RealValue = cell.ShowValue
+			config[cellPosition.Column][cellPosition.Row] = cell
+		}
+	}
+	return
+}
+
+// DateCalculatePrepare 单个单元格的日期计算
+func DateCalculatePrepare(calculateCellMap map[string]Cell, config string) (val float64, err error, errMsg string) {
+	var edbDateConf request.MixDateCalculateConf
+	err = json.Unmarshal([]byte(config), &edbDateConf)
+	if err != nil {
+		err = fmt.Errorf("日期计算配置json解析失败失败: %s, Err:%s", config, err.Error())
+		return
+	}
+	if len(edbDateConf.RelationCellList) == 0 {
+		err = fmt.Errorf("日期计算 未配置日期单元格失败: %s", config)
+		return
+	}
+	valMap := make(map[string]int)
+	for _, v := range edbDateConf.RelationCellList {
+		// 查找单元格数据
+		cell, ok := calculateCellMap[v.Uid]
+		if !ok {
+			err = fmt.Errorf("查找单元格:%s 的数据失败", v.Uid)
+			return
+		}
+		colData := cell.CellInfo
+
+		// 如果不是基础计算单元格,直接返回
+		_, err = time.ParseInLocation(utils.FormatDate, colData.ShowValue, time.Local)
+		if err != nil {
+			err = fmt.Errorf("%s 的单元格非日期类型, Err: %s", colData.ShowValue, err.Error())
+			return
+		}
+		// todo 把日期转换成excel里的天数
+		realDiffDay := utils.GetDaysDiff1900(colData.ShowValue)
+
+		valMap[strings.ToUpper(v.Tag)] = realDiffDay
+	}
+
+	// 计算
+	val, errMsg, err = DateCalculateFormula(valMap, strings.ToUpper(edbDateConf.Formula))
+	if err != nil {
+		return
+	}
+	return
+}
+
+func DateCalculateFormula(valTagMap map[string]int, calculateFormula string) (calVal float64, errMsg string, err error) {
+	if calculateFormula == "" {
+		errMsg = "公式异常"
+		err = errors.New(errMsg)
+		return
+	}
+
+	calculateFormula = strings.TrimPrefix(calculateFormula, "=")
+	calculateFormula = strings.Replace(calculateFormula, "(", "(", -1)
+	calculateFormula = strings.Replace(calculateFormula, ")", ")", -1)
+	calculateFormula = strings.Replace(calculateFormula, ",", ",", -1)
+	calculateFormula = strings.Replace(calculateFormula, "。", ".", -1)
+	calculateFormula = strings.Replace(calculateFormula, "%", "*0.01", -1)
+
+	formulaFormStr := utils.ReplaceFormulaByTagMap(valTagMap, calculateFormula)
+
+	if formulaFormStr == `` {
+		errMsg = "公式异常"
+		err = errors.New(errMsg)
+		return
+	}
+	fmt.Println("公式:" + formulaFormStr)
+	expression := formula.NewExpression(formulaFormStr)
+	calResult, err := expression.Evaluate()
+	if err != nil {
+		errMsg = "计算失败"
+		err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		// 分母为0的报错
+		if strings.Contains(err.Error(), "divide by zero") {
+			errMsg = "分母不能为0"
+			err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
+		}
+		return
+	}
+	// 如果计算结果是NAN,那么就提示报错
+	if calResult.IsNan() {
+		errMsg = "计算失败"
+		err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
+		return
+	}
+	calVal, err = calResult.Float64()
+	if err != nil {
+		return
+	}
+
+	// 转Decimal然后四舍五入
+	calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
+
+	return
+}
+
+// handleMixCellShowStyle 处理混合表格中,显示计算的逻辑
+func handleMixCellShowStyle(showStyleList []string, calculateCellMap map[string]Cell, oldConfig [][]request.MixedTableCellDataReq) (config [][]request.MixedTableCellDataReq, err error, errMsg string) {
+	config = oldConfig
+	if len(showStyleList) == 0 {
+		return
+	}
+	if len(showStyleList) > 0 {
+		for _, cellKey := range showStyleList {
+			// 查找这个单元格的位置,直接map找了,而不是遍历整个单元格
+			cellPosition, ok := calculateCellMap[cellKey]
+			if !ok {
+				utils.FileLog.Error("找不到单元格位置:", cellKey)
+				continue
+			}
+
+			cell := config[cellPosition.Column][cellPosition.Row]
+			_, e := strconv.ParseFloat(cell.ShowStyle, 64) // 将字符串转换成float类型
+			if e != nil {                                  // 如果没有错误发生则返回true,说明该字符串是一个合法的数字
+				continue
+			}
+			val := cell.ShowStyle
+			var styleConf request.MixCellShowStyle
+			err = json.Unmarshal([]byte(cell.ShowStyle), &styleConf)
+			if err != nil {
+				err = fmt.Errorf("日期计算配置json解析失败失败: %s, Err:%s", config, err.Error())
+				return
+			}
+
+			if styleConf.NumberStyle.AddPointNum != 0 {
+				//val = CountDecimalPlaces(val)
+				val = changePointDecimalPlaces(cell.ShowStyle, styleConf.NumberStyle.AddPointNum)
+			}
+			if styleConf.NumberStyle.IsPercent == 1 {
+				val = changeToPercent(cell.ShowStyle)
+			}
+			cell.ShowValue = val
+			config[cellPosition.Column][cellPosition.Row] = cell
+		}
+	}
+	return
+}
+
+// changePointDecimalPlaces 小数点位数加减
+func changePointDecimalPlaces(str string, changeNum int) (newStr string) {
+	newStr = str
+	if changeNum == 0 { //无需改变
+		return
+	}
+	dotIndex := strings.Index(str, ".") // 查找小数点的位置
+	if dotIndex == -1 {
+		return // 没有小数点,返回0
+	}
+	decimalPlaces := len(str) - dotIndex - 1 // 计算小数位数
+	numbers := strings.Split(str, ".")
+	number1 := numbers[0] //整数部分
+	number2 := numbers[1] //小数部分
+	if changeNum > 0 {    // 增加小数位数
+		newStr = number1 + "." + number2 + strings.Repeat("0", changeNum)
+	} else if changeNum < 0 { // 减少小数位数
+		newStr = number1 + "." + number2[:decimalPlaces+changeNum]
+	}
+	fmt.Println(newStr)
+	return
+}
+
+// changeToPercent 展示成百分比
+func changeToPercent(str string) (newStr string) {
+	newStr = str
+	dotIndex := strings.Index(str, ".") // 查找小数点的位置
+	if dotIndex == -1 {
+		newStr = newStr + "00%"
+		return // 没有小数点,返回0
+	}
+	decimalPlaces := len(str) - dotIndex - 1 // 计算小数位数
+	numbers := strings.Split(str, ".")
+	number1 := numbers[0] //整数部分
+	number2 := numbers[1] //小数部分
+	if decimalPlaces == 1 {
+		newStr = number1 + number2 + "0%"
+		return
+	}
+	if decimalPlaces == 2 {
+		newStr = number1 + number2 + "%"
+		return
+	}
+	newStr = number1 + number2[:2] + "." + number2[2:] + "%"
+	fmt.Println(newStr)
+	return
+}

+ 18 - 0
utils/calculate.go

@@ -221,6 +221,24 @@ func ReplaceFormulaByCellList(cellList []CellPosition, formulaStr string) string
 	return formulaStr
 }
 
+func ReplaceFormulaByTagMap(valTagMap map[string]int, formulaStr string) string {
+	funMap := getFormulaMap()
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, k, v, -1)
+	}
+
+	replaceCount := 0
+	for tag, val := range valTagMap {
+		dvStr := fmt.Sprintf("%v", val)
+		formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
+		replaceCount++
+	}
+	for k, v := range funMap {
+		formulaStr = strings.Replace(formulaStr, v, k, -1)
+	}
+	return formulaStr
+}
+
 func getFormulaMap() map[string]string {
 	funMap := make(map[string]string)
 	funMap["MAX"] = "[@@]"

+ 21 - 0
utils/common.go

@@ -637,6 +637,27 @@ func ConvertToFormatDay(excelDaysString string) string {
 	return resultTime
 }
 
+// GetDaysDiff1900 计算日期距离1900-01-01的天数
+func GetDaysDiff1900(date string) int {
+	// 将字符串转换为时间类型
+	//从1899年12月30日开始是因为Excel的日期计算是基于1900年1月1日的,而1900年并不是闰年,因此Excel日期计算存在一个错误。为了修正这个错误,
+	//我们将时间回溯到1899年12月30日,这样在进行日期计算时可以正确地处理Excel日期。
+	tStart, err := time.ParseInLocation(FormatDate, "1899-12-30", time.Local)
+	if err != nil {
+		return 0
+	}
+	tEnd, err := time.ParseInLocation(FormatDate, date, time.Local)
+	if err != nil {
+		return 0
+	}
+
+	// 计算两个日期之间的天数差值
+	duration := tEnd.Sub(tStart).Hours() / 24
+	days := int(duration)
+
+	return days
+}
+
 func CheckPwd(pwd string) bool {
 	compile := `([0-9a-z]+){6,12}|(a-z0-9]+){6,12}`
 	reg := regexp.MustCompile(compile)