Browse Source

Merge branch 'bzq/excel_rule_master' of eta_server/eta_chart_lib into master

鲍自强 5 months ago
parent
commit
be817b19ab

+ 6 - 1
controllers/excel_info.go

@@ -11,9 +11,10 @@ import (
 	"eta/eta_chart_lib/services/excel"
 	"eta/eta_chart_lib/utils"
 	"fmt"
-	"github.com/shopspring/decimal"
 	"strings"
 	"time"
+
+	"github.com/shopspring/decimal"
 )
 
 // ExcelInfoController excel表格
@@ -179,6 +180,10 @@ func (this *ExcelInfoController) GetTableDetail() {
 	}
 
 	tableData = excel.HandleTableCell(tableData)
+	tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
+	if err != nil {
+		utils.FileLog.Info("表格管理规则处理失败,HandleRuleToTableCell err:", err.Error())
+	}
 	config := response.ExcelTableDetailConfigResp{
 		FontSize: 9,
 	}

+ 82 - 0
models/excel_info_rule_mapping.go

@@ -0,0 +1,82 @@
+package models
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type ExcelInfoRuleMapping struct {
+	ExcelInfoRuleMappingId int       `orm:"pk" description:"主键"`
+	ExcelInfoId            int       `description:"Excel信息ID"`
+	RuleType               int       `description:"规则类型"`
+	LeftValue              string    `description:"左值"`
+	LeftValueShow          string    `description:"左值前端显示"`
+	LeftValueType          int       `description:"左值类型"`
+	RightValue             string    `description:"右值"`
+	RightValueShow         string    `description:"右值前端显示"`
+	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             time.Time `description:"创建时间"`
+}
+
+type ExcelInfoRuleMappingView struct {
+	ExcelInfoRuleMappingId int    `orm:"pk" description:"主键"`
+	ExcelInfoId            int    `description:"Excel信息ID"`
+	RuleType               int    `description:"规则类型:1-大于,2-小于,3-介于,4-等于,5-发生日期"`
+	LeftValue              string `description:"左值"`
+	LeftValueBack          string `description:"左值前端显示"`
+	LeftValueType          int    `description:"左值类型"`
+	RightValue             string `description:"右值"`
+	RightValueBack         string `description:"右值前端显示"`
+	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:"创建时间"`
+}
+
+func (e *ExcelInfoRuleMapping) Insert() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(e)
+	return
+}
+
+func (e *ExcelInfoRuleMapping) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(e, cols...)
+	return
+}
+
+// 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
+}
+
+// GetExcelRuleMappingById 根据主键Id获取规则映射信息
+func GetExcelRuleMappingById(id int) (item *ExcelInfoRuleMappingView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_rule_mapping WHERE excel_info_rule_mapping_id = ?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func DeleteExcelRuleMappingById(id int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM excel_info_rule_mapping WHERE excel_info_rule_mapping_id = ?`
+	_, err = o.Raw(sql, id).Exec()
+	return
+}

+ 6 - 2
models/request/mixed_table.go

@@ -160,8 +160,12 @@ type MixDateCalculateTagReq struct {
 
 // MixCellShowStyle 混合表格 单元格样式展示计算
 type MixCellShowStyle struct {
-	Pn int    `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
-	Nt string `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
+	Pn              int    `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
+	Nt              string `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
+	Decimal         *int   `description:"小数点位数"`
+	Last            string `description:"起始操作:nt|decimal"`
+	Color           string `description:"颜色值,#RRG" json:"color"`
+	BackgroundColor string `description:"背景颜色值,#RRG" json:"background-color"`
 }
 
 type DateDataBeforeAfterReq struct {

+ 58 - 3
services/data/excel/time_table.go

@@ -8,12 +8,13 @@ import (
 	"eta/eta_chart_lib/services/data"
 	"eta/eta_chart_lib/utils"
 	"fmt"
-	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/shopspring/decimal"
+	"github.com/yidane/formula"
 )
 
 // TableDataConfig
@@ -27,6 +28,13 @@ type TableDataConfig struct {
 	ManualDate       []string                  `description:"手动配置的日期(未来的日期)"`
 	TableEdbInfoList []TableEdbInfo            `description:"表格内指标信息"`
 	TextRowData      [][]request.ManualDataReq `description:"文本列表"`
+	DecimalConfig    []DecimalConfig           `description:"小数位数配置"`
+}
+
+type DecimalConfig struct {
+	Row     string `description:"行上的索引, 目前仅保存指标的日期"`
+	Col     int    `description:"列上的索引, 目前仅保存edbInfoId"`
+	Decimal int    `description:"小数位数, 从左至右,从上到下的顺序"`
 }
 
 // TableEdbInfo
@@ -329,7 +337,7 @@ func GetDataByTableDataConfig(tableDataConfig TableDataConfig) (resultResp reque
 			d[k2].ShowValue = utils.FormatTableDataShowValue(vf)
 		}
 	}
-
+	data = SetExcelShowValueByDecimalConfig(data, tableDataConfig.DecimalConfig)
 	resultResp = request.TableDataReq{
 		EdbInfoIdList: edbInfoIdList,
 		Sort:          tableDataConfig.Sort,
@@ -340,6 +348,53 @@ func GetDataByTableDataConfig(tableDataConfig TableDataConfig) (resultResp reque
 	return
 }
 
+func SetExcelShowValueByDecimalConfig(edbData []request.EdbInfoData, decimalConfig []DecimalConfig) []request.EdbInfoData {
+	if len(decimalConfig) <= 0 || len(edbData) <= 0 {
+		return edbData
+	}
+	edbInfoIndexMap := make(map[int]int)
+	dateIndexMap := make(map[string]int)
+	for i, edbInfo := range edbData {
+		edbInfoIndexMap[edbInfo.EdbInfoId] = i
+	}
+	for i, date := range edbData[0].Data {
+		dateIndexMap[date.DataTime] = i
+	}
+	for _, conf := range decimalConfig {
+		if conf.Col > 0 {
+			if edbIndex, ok := edbInfoIndexMap[conf.Col]; ok {
+				for i := 0; i < len(edbData[edbIndex].Data); i++ {
+					f, err := strconv.ParseFloat(edbData[edbIndex].Data[i].Value, 64)
+					if err != nil {
+						continue
+					}
+					if conf.Decimal == -1 {
+						continue
+					}
+					formatStr := fmt.Sprintf("%%.%df", conf.Decimal)
+					edbData[edbIndex].Data[i].ShowValue = fmt.Sprintf(formatStr, f)
+				}
+			}
+		}
+		if conf.Row != `` {
+			if dateIndex, ok := dateIndexMap[conf.Row]; ok {
+				for i := 0; i < len(edbData); i++ {
+					f, err := strconv.ParseFloat(edbData[i].Data[dateIndex].Value, 64)
+					if err != nil {
+						continue
+					}
+					if conf.Decimal == -1 {
+						continue
+					}
+					formatStr := fmt.Sprintf("%%.%df", conf.Decimal)
+					edbData[i].Data[dateIndex].ShowValue = fmt.Sprintf(formatStr, f)
+				}
+			}
+		}
+	}
+	return edbData
+}
+
 // GetTableDataConfig 根据TableDataReq配置获取相关数据配置
 func GetTableDataConfig(reqData request.TableDataReq) (tableDataConfig TableDataConfig, err error) {
 	// 指标数据

+ 228 - 0
services/excel/luck_sheet_table.go

@@ -0,0 +1,228 @@
+package excel
+
+import (
+	"eta/eta_chart_lib/models"
+	"eta/eta_chart_lib/utils"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// HandleRuleToTableCell 根据管理规则渲染单元格数据
+func HandleRuleToTableCell(excelInfoId int, oldTableData TableData) (newTableData TableData, err error) {
+	newTableData = oldTableData
+	excelRuleMappingList, err := models.GetExcelRuleMappingByExcelInfoId(excelInfoId)
+	if err != nil {
+		return
+	}
+	if len(excelRuleMappingList) == 0 {
+		return
+	}
+	tableDataList := oldTableData.TableDataList
+	excelRuleMap := make(map[int]*models.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 *models.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.RightValue, 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 []*models.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
+}

+ 45 - 0
services/excel/lucky_sheet.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_chart_lib/models/request"
 	"eta/eta_chart_lib/utils"
 	"fmt"
+	"math"
 	"reflect"
 	"sort"
 	"strconv"
@@ -1211,7 +1212,51 @@ func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hide
 					tmp.MergeCell.Column = cell.MerData.Mer.Col
 				}
 				if cell.ShowStyle != "" {
+					var styleConfig request.MixCellShowStyle
+					if err := json.Unmarshal([]byte(cell.ShowStyle), &styleConfig); err != nil {
+						utils.FileLog.Info("表格样式showStyle解析失败", err.Error())
+					}
+					if styleConfig.BackgroundColor != "" {
+						tmp.Background = styleConfig.BackgroundColor
+					}
+					if styleConfig.Color != "" {
+						tmp.FontColor = styleConfig.Color
+					}
 					tmp.Monitor = cell.ShowFormatValue
+					tmpShowValue, err := strconv.ParseFloat(cell.Value, 64)
+					if err == nil {
+						switch styleConfig.Last {
+						case "nt":
+							// 先进行数字的百分比计算,然后保留小数点位数
+							percent := tmpShowValue * 100
+							if styleConfig.Decimal == nil {
+								continue
+							}
+							factor := math.Pow(10, float64(*styleConfig.Decimal))
+							rounded := math.Round(percent*factor) / factor
+							tmp.Monitor = fmt.Sprintf("%g%%", rounded)
+						case "decimal":
+							// 先保留小数点位数,再进行百分比计算
+							if styleConfig.Decimal == nil {
+								continue
+							}
+							factor := math.Pow(10, float64(*styleConfig.Decimal))
+							rounded := math.Round(tmpShowValue*factor) / factor
+							if styleConfig.Nt == "percent" {
+								percent := rounded * 100
+								var precisionStr string
+								if *styleConfig.Decimal > 2 {
+									precision := *styleConfig.Decimal - 2
+									precisionStr = strconv.FormatFloat(rounded, 'f', precision, 64)
+								} else {
+									precisionStr = strconv.FormatFloat(math.Round(percent), 'f', -1, 64)
+								}
+								tmp.Monitor = fmt.Sprintf("%s%%", precisionStr)
+							} else {
+								tmp.Monitor = fmt.Sprintf("%g", rounded)
+							}
+						}
+					}
 				}
 				dataCol = append(dataCol, tmp)
 			}

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