浏览代码

add:新增公司一键绑定解绑,图表展示接口与最大最小值、平均值计算

zoie 1 周之前
父节点
当前提交
9dbae12747

+ 3 - 0
conf/app.conf

@@ -70,3 +70,6 @@ jcCron = "0 */2 * * * *"
 
 
 # 文件管理默认大小限制(字节),默认1GB = 1073741824字节
 # 文件管理默认大小限制(字节),默认1GB = 1073741824字节
 DefaultFileSizeLimit = 1073741824
 DefaultFileSizeLimit = 1073741824
+
+# 文件管理默认大小限制(字节),500MB
+DefaultUploadFileSizeLimit = 1024*1024*500

+ 9 - 0
conf/app_prod.conf

@@ -64,3 +64,12 @@ maxlines = 10000
 
 
 # 使用新的告警查询方式,修改为true后弃用设备报禁信息将在弃用菜单内才能查询
 # 使用新的告警查询方式,修改为true后弃用设备报禁信息将在弃用菜单内才能查询
 UseNewWarningQuery = false
 UseNewWarningQuery = false
+# 对接精创数据
+apiurl = "http://192.168.0.5:8081/jc/api/"
+jcCron = "0 */2 * * * *"
+
+# 文件管理默认大小限制(字节),默认1GB = 1073741824字节
+DefaultFileSizeLimit = 1073741824
+
+# 文件管理默认大小限制(字节),500MB
+DefaultUploadFileSizeLimit = 1024*1024*500

+ 6 - 0
conf/app_test.conf

@@ -68,3 +68,9 @@ UseNewWarningQuery = false
 # 对接精创数据
 # 对接精创数据
 apiurl = "http://192.168.0.5:8081/jc/api/"
 apiurl = "http://192.168.0.5:8081/jc/api/"
 jcCron = "0 */2 * * * *"
 jcCron = "0 */2 * * * *"
+
+# 文件管理默认大小限制(字节),默认1GB = 1073741824字节
+DefaultFileSizeLimit = 1073741824
+
+# 文件管理默认大小限制(字节),500MB
+DefaultUploadFileSizeLimit = 1024*1024*500

+ 1 - 0
conf/config.go

@@ -50,3 +50,4 @@ var UseNewWarningQuery, _ = beego.AppConfig.Bool("UseNewWarningQuery")
 
 
 // 文件管理默认大小限制(字节)
 // 文件管理默认大小限制(字节)
 var DefaultFileSizeLimit, _ = beego.AppConfig.Int64("DefaultFileSizeLimit")
 var DefaultFileSizeLimit, _ = beego.AppConfig.Int64("DefaultFileSizeLimit")
+var DefaultUploadFileSizeLimit, _ = beego.AppConfig.Int64("DefaultUploadFileSizeLimit")

+ 170 - 0
controllers/Data.go

@@ -380,6 +380,176 @@ func (c *DataController) Device_Sensor_Data_More() {
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
 	c.ServeJSON()
 	c.ServeJSON()
 	return
 	return
+} // 设备数据
+
+// 图表展示列表数据
+func (c *DataController) Device_Sensor_Data_ChartShow() {
+	T_snid := c.GetString("T_snid")
+	Time_start := c.GetString("Time_start")
+	Time_end := c.GetString("Time_end")
+
+	if len(T_snid) < 10 {
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+		c.ServeJSON()
+		return
+	}
+
+	type R_JSONS struct {
+		Data struct {
+			TemperatureData [][]interface{} `json:"temperature_data"` // 温度数据: [时间戳, 温度, 上限, 下限]
+			HumidityData    [][]interface{} `json:"humidity_data"`    // 湿度数据: [时间戳, 湿度, 上限, 下限]
+		}
+		Num int64 `json:"num"`
+	}
+	var r_jsons R_JSONS
+
+	// 取消分页,获取所有数据(page=1, page_z=9999)
+	dataList, num := Device.Read_DeviceData_By_T_snid_List_ChartShow(T_snid, Time_start, Time_end)
+
+	// 整理数据格式:同时生成温度和湿度两组数据
+	temperatureData := make([][]interface{}, 0, len(dataList))
+	humidityData := make([][]interface{}, 0, len(dataList))
+
+	for _, item := range dataList {
+		// 将时间字符串转换为时间戳(毫秒)
+		var timestamp int64
+		if t, err := time.ParseInLocation("2006-01-02 15:04:05", item.T_time, time.Local); err == nil {
+			timestamp = t.Unix() * 1000 // 转换为毫秒时间戳
+		} else {
+			// 如果解析失败,尝试其他格式或使用0
+			timestamp = 0
+		}
+
+		// 构建温度数据行:[名称,sn, id, 时间戳, 温度, 下限, 上限]
+		tempRow := []interface{}{
+			timestamp,
+			item.T_t,
+			item.T_tl,
+			item.T_tu,
+			item.T_name,
+			item.T_sn,
+			item.T_id,
+		}
+		temperatureData = append(temperatureData, tempRow)
+
+		// 构建湿度数据行:[名称,sn, id, 时间戳, 湿度, 下限, 上限]
+		humidityRow := []interface{}{
+			timestamp,
+			item.T_rh,
+			item.T_rhl,
+			item.T_rhu,
+			item.T_name,
+			item.T_sn,
+			item.T_id,
+		}
+		humidityData = append(humidityData, humidityRow)
+	}
+
+	r_jsons.Data.TemperatureData = temperatureData
+	r_jsons.Data.HumidityData = humidityData
+	r_jsons.Num = num
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
+	c.ServeJSON()
+	return
+}
+
+// 图表展示数据统计(所有传感器汇总的最大值、最小值、平均值)
+func (c *DataController) Device_Sensor_Data_ChartShow_calculate() {
+	T_snid := c.GetString("T_snid")
+	Time_start := c.GetString("Time_start")
+	Time_end := c.GetString("Time_end")
+
+	if len(T_snid) < 10 {
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+		c.ServeJSON()
+		return
+	}
+
+	// 解析 T_snid 参数:格式 2025249072841233,1,1|2025249072841233,2,1|
+	T_snid_list := strings.Split(strings.Trim(T_snid, "|"), "|")
+
+	// 存储所有传感器的统计数据
+	statisticsList := make([]Device.DeviceDataStatistics, 0, len(T_snid_list))
+
+	// 遍历每个传感器,计算统计数据
+	for _, v := range T_snid_list {
+		sn_id := strings.Split(v, ",")
+		if len(sn_id) < 2 {
+			continue
+		}
+
+		// 获取 SN 和传感器 ID
+		sn := sn_id[0]
+		sensorId := lib.To_int(sn_id[1])
+
+		// 获取统计数据
+		stats, err := Device.Read_DeviceData_Statistics(sn, sensorId, Time_start, Time_end)
+		if err != nil {
+			logs.Debug("统计数据获取失败:", sn, sensorId, err)
+			continue
+		}
+
+		statisticsList = append(statisticsList, stats)
+	}
+
+	// 如果没有获取到任何统计数据
+	if len(statisticsList) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: Device.DeviceDataStatisticsTotal{}}
+		c.ServeJSON()
+		return
+	}
+
+	// 汇总所有传感器的统计数据
+	var totalStats Device.DeviceDataStatisticsTotal
+	var tempSum, humiditySum float64
+	var totalDataCount int64
+
+	// 初始化最大最小值
+	totalStats.TempMax = statisticsList[0].TempMax
+	totalStats.TempMin = statisticsList[0].TempMin
+	totalStats.HumidityMax = statisticsList[0].HumidityMax
+	totalStats.HumidityMin = statisticsList[0].HumidityMin
+
+	// 遍历所有统计数据,计算汇总值
+	for _, stats := range statisticsList {
+		// 温度最大值
+		if stats.TempMax > totalStats.TempMax {
+			totalStats.TempMax = stats.TempMax
+		}
+		// 温度最小值
+		if stats.TempMin < totalStats.TempMin {
+			totalStats.TempMin = stats.TempMin
+		}
+		// 湿度最大值
+		if stats.HumidityMax > totalStats.HumidityMax {
+			totalStats.HumidityMax = stats.HumidityMax
+		}
+		// 湿度最小值
+		if stats.HumidityMin < totalStats.HumidityMin {
+			totalStats.HumidityMin = stats.HumidityMin
+		}
+
+		// 累加用于计算平均值(按数据条数加权)
+		tempSum += float64(stats.TempAvg) * float64(stats.DataCount)
+		humiditySum += float64(stats.HumidityAvg) * float64(stats.DataCount)
+		totalDataCount += stats.DataCount
+	}
+
+	// 计算加权平均值,并保留一位小数
+	if totalDataCount > 0 {
+		tempAvg := tempSum / float64(totalDataCount)
+		humidityAvg := humiditySum / float64(totalDataCount)
+
+		// 保留一位小数
+		totalStats.TempAvg = float32(math.Round(tempAvg*10) / 10)
+		totalStats.HumidityAvg = float32(math.Round(humidityAvg*10) / 10)
+	}
+	totalStats.DataCount = totalDataCount
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: totalStats}
+	c.ServeJSON()
+	return
 }
 }
 
 
 // GetSecondaryPositioning 获取APP辅助定位
 // GetSecondaryPositioning 获取APP辅助定位

+ 21 - 13
controllers/Device.go

@@ -826,6 +826,13 @@ func (c *DeviceController) DeviceSensor_Edit() {
 		Device.Update_DeviceSensor(DeviceSensor, "T_3dview")
 		Device.Update_DeviceSensor(DeviceSensor, "T_3dview")
 		System.Add_UserLogs_T(c.Admin_r.T_uuid, "设备管理", "传感器 3D视图订阅", "SN:"+Sn+" ["+strconv.Itoa(Id)+"]"+v)
 		System.Add_UserLogs_T(c.Admin_r.T_uuid, "设备管理", "传感器 3D视图订阅", "SN:"+Sn+" ["+strconv.Itoa(Id)+"]"+v)
 	}
 	}
+
+	// 数据视图3D视图订阅
+	if v := c.GetString("T_number"); len(v) > 0 {
+		DeviceSensor.T_number = v
+		Device.Update_DeviceSensor(DeviceSensor, "T_number")
+		System.Add_UserLogs_T(c.Admin_r.T_uuid, "设备管理", "传感器 探头编号", "SN:"+Sn+" ["+strconv.Itoa(Id)+"]"+v)
+	}
 	//金卫信对接数据
 	//金卫信对接数据
 	ProbeIdNo := c.GetString("probeIdNo")
 	ProbeIdNo := c.GetString("probeIdNo")
 	if len(ProbeIdNo) > 0 {
 	if len(ProbeIdNo) > 0 {
@@ -1227,7 +1234,7 @@ func (c *DeviceController) DeviceSensor_Manage_List_Excel() {
 	f.SetCellValue("Sheet1", "A1", "设备管理")
 	f.SetCellValue("Sheet1", "A1", "设备管理")
 	f.SetCellStyle("Sheet1", "A1", "T1", Style1)
 	f.SetCellStyle("Sheet1", "A1", "T1", Style1)
 	// 写入表头
 	// 写入表头
-	headers := []string{"序号", "公司名称", "SN", "编号", "分类", "规格", "传感器名称", "传感器类型", "启/停", "空库", "温度范围(℃)",
+	headers := []string{"序号", "公司名称", "SN", "分类", "规格", "传感器名称", "探头编号", "传感器类型", "启/停", "空库", "温度范围(℃)",
 		"湿度范围(%)", "预警", "预警温度范围", "预警湿度范围", "数据展示",
 		"湿度范围(%)", "预警", "预警温度范围", "预警湿度范围", "数据展示",
 		"验证日期", "验证到期时间", "校准日期", "校准到期时间"}
 		"验证日期", "验证到期时间", "校准日期", "校准到期时间"}
 	for col, header := range headers {
 	for col, header := range headers {
@@ -1261,7 +1268,6 @@ func (c *DeviceController) DeviceSensor_Manage_List_Excel() {
 		f.SetCellValue("Sheet1", fmt.Sprintf("B%d", line), Company_r.T_name) // 公司名称
 		f.SetCellValue("Sheet1", fmt.Sprintf("B%d", line), Company_r.T_name) // 公司名称
 
 
 		f.SetCellValue("Sheet1", fmt.Sprintf("C%d", line), v.T_sn)
 		f.SetCellValue("Sheet1", fmt.Sprintf("C%d", line), v.T_sn)
-		f.SetCellValue("Sheet1", fmt.Sprintf("D%d", line), v.T_id)
 
 
 		classList, specList := []string{}, []string{}
 		classList, specList := []string{}, []string{}
 		for _, r := range v.T_CompanyClassList {
 		for _, r := range v.T_CompanyClassList {
@@ -1272,10 +1278,12 @@ func (c *DeviceController) DeviceSensor_Manage_List_Excel() {
 				specList = append(specList, r.T_spec)
 				specList = append(specList, r.T_spec)
 			}
 			}
 		}
 		}
-		f.SetCellValue("Sheet1", fmt.Sprintf("E%d", line), strings.Join(classList, "/")) // 分类
-		f.SetCellValue("Sheet1", fmt.Sprintf("F%d", line), strings.Join(specList, "/"))  // 规格
+		f.SetCellValue("Sheet1", fmt.Sprintf("D%d", line), strings.Join(classList, "/")) // 分类
+		f.SetCellValue("Sheet1", fmt.Sprintf("E%d", line), strings.Join(specList, "/"))  // 规格
+
+		f.SetCellValue("Sheet1", fmt.Sprintf("F%d", line), v.T_name)
+		f.SetCellValue("Sheet1", fmt.Sprintf("G%d", line), v.T_number)
 
 
-		f.SetCellValue("Sheet1", fmt.Sprintf("G%d", line), v.T_name)
 		f.SetCellValue("Sheet1", fmt.Sprintf("H%d", line), v.T_type_name)
 		f.SetCellValue("Sheet1", fmt.Sprintf("H%d", line), v.T_type_name)
 
 
 		T_en_str := "启用"
 		T_en_str := "启用"
@@ -1291,16 +1299,16 @@ func (c *DeviceController) DeviceSensor_Manage_List_Excel() {
 		f.SetCellValue("Sheet1", fmt.Sprintf("J%d", line), T_free_str)
 		f.SetCellValue("Sheet1", fmt.Sprintf("J%d", line), T_free_str)
 		T_Tlower, T_Tupper, T_RHlower, T_RHupper := "0", "0", "0", "0"
 		T_Tlower, T_Tupper, T_RHlower, T_RHupper := "0", "0", "0", "0"
 		if v.T_Tlower != nil {
 		if v.T_Tlower != nil {
-			T_Tlower = fmt.Sprintf("%.0f", *v.T_Tlower)
+			T_Tlower = fmt.Sprintf("%.1f", *v.T_Tlower)
 		}
 		}
 		if v.T_Tupper != nil {
 		if v.T_Tupper != nil {
-			T_Tupper = fmt.Sprintf("%.0f", *v.T_Tupper)
+			T_Tupper = fmt.Sprintf("%.1f", *v.T_Tupper)
 		}
 		}
 		if v.T_RHlower != nil {
 		if v.T_RHlower != nil {
-			T_RHlower = fmt.Sprintf("%.0f", *v.T_RHlower)
+			T_RHlower = fmt.Sprintf("%.1f", *v.T_RHlower)
 		}
 		}
 		if v.T_RHupper != nil {
 		if v.T_RHupper != nil {
-			T_RHupper = fmt.Sprintf("%.0f", *v.T_RHupper)
+			T_RHupper = fmt.Sprintf("%.1f", *v.T_RHupper)
 		}
 		}
 		f.SetCellValue("Sheet1", fmt.Sprintf("K%d", line), fmt.Sprintf("%s ~ %s", T_Tlower, T_Tupper))
 		f.SetCellValue("Sheet1", fmt.Sprintf("K%d", line), fmt.Sprintf("%s ~ %s", T_Tlower, T_Tupper))
 		f.SetCellValue("Sheet1", fmt.Sprintf("L%d", line), fmt.Sprintf("%s ~ %s", T_RHlower, T_RHupper))
 		f.SetCellValue("Sheet1", fmt.Sprintf("L%d", line), fmt.Sprintf("%s ~ %s", T_RHlower, T_RHupper))
@@ -1312,16 +1320,16 @@ func (c *DeviceController) DeviceSensor_Manage_List_Excel() {
 		f.SetCellValue("Sheet1", fmt.Sprintf("M%d", line), T_enprel_str)
 		f.SetCellValue("Sheet1", fmt.Sprintf("M%d", line), T_enprel_str)
 		T_tprel, T_tpreu, T_hprel, T_hpreu := "0", "0", "0", "0"
 		T_tprel, T_tpreu, T_hprel, T_hpreu := "0", "0", "0", "0"
 		if v.T_tprel != nil {
 		if v.T_tprel != nil {
-			T_tprel = fmt.Sprintf("%.0f", *v.T_tprel)
+			T_tprel = fmt.Sprintf("%.1f", *v.T_tprel)
 		}
 		}
 		if v.T_tpreu != nil {
 		if v.T_tpreu != nil {
-			T_tpreu = fmt.Sprintf("%.0f", *v.T_tpreu)
+			T_tpreu = fmt.Sprintf("%.1f", *v.T_tpreu)
 		}
 		}
 		if v.T_hprel != nil {
 		if v.T_hprel != nil {
-			T_hprel = fmt.Sprintf("%.0f", *v.T_hprel)
+			T_hprel = fmt.Sprintf("%.1f", *v.T_hprel)
 		}
 		}
 		if v.T_hpreu != nil {
 		if v.T_hpreu != nil {
-			T_hpreu = fmt.Sprintf("%.0f", *v.T_hpreu)
+			T_hpreu = fmt.Sprintf("%.1f", *v.T_hpreu)
 		}
 		}
 		f.SetCellValue("Sheet1", fmt.Sprintf("N%d", line), fmt.Sprintf("%s ~ %s", T_tprel, T_tpreu))
 		f.SetCellValue("Sheet1", fmt.Sprintf("N%d", line), fmt.Sprintf("%s ~ %s", T_tprel, T_tpreu))
 		f.SetCellValue("Sheet1", fmt.Sprintf("O%d", line), fmt.Sprintf("%s ~ %s", T_hprel, T_hpreu))
 		f.SetCellValue("Sheet1", fmt.Sprintf("O%d", line), fmt.Sprintf("%s ~ %s", T_hprel, T_hpreu))

+ 3 - 2
controllers/UpFile.go

@@ -1,6 +1,7 @@
 package controllers
 package controllers
 
 
 import (
 import (
+	"Cold_Api/conf"
 	"Cold_Api/controllers/lib"
 	"Cold_Api/controllers/lib"
 	"Cold_Api/models/Account"
 	"Cold_Api/models/Account"
 	"fmt"
 	"fmt"
@@ -85,8 +86,8 @@ func (c *UpFileController) UploadFile() {
 	defer file.Close()
 	defer file.Close()
 
 
 	// 验证文件大小(500M限制)
 	// 验证文件大小(500M限制)
-	if fileHeader.Size > 1024*1024*500 {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "文件大小超过500M限制"}
+	if fileHeader.Size > conf.DefaultUploadFileSizeLimit {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("文件大小超过%dM限制", conf.DefaultUploadFileSizeLimit/1024/1024)}
 		c.ServeJSON()
 		c.ServeJSON()
 		return
 		return
 	}
 	}

+ 76 - 3
controllers/User.go

@@ -56,7 +56,11 @@ func (c *UserController) Prepare() {
 func (c *UserController) User_WxQRCode() {
 func (c *UserController) User_WxQRCode() {
 
 
 	T_name := c.GetString("T_name")
 	T_name := c.GetString("T_name")
-	WxQRCode_str, _ := NatsServer.Wx_GenerateQRCode("-|" + lib.AesEncryptCBC(c.Admin_r.T_uuid+"/"+T_name, "0123456789012345") + "|- @宝智达冷链 微信公众号通知")
+	T_uuid := c.GetString("T_uuid")
+	if len(T_uuid) == 0 {
+		T_uuid = c.Admin_r.T_uuid
+	}
+	WxQRCode_str, _ := NatsServer.Wx_GenerateQRCode("-|" + lib.AesEncryptCBC(T_uuid+"/"+T_name, "0123456789012345") + "|- @宝智达冷链 微信公众号通知")
 	WxQRCode_str = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + WxQRCode_str
 	WxQRCode_str = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + WxQRCode_str
 
 
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: WxQRCode_str}
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: WxQRCode_str}
@@ -1313,6 +1317,32 @@ func (c *UserController) AdminCompanyUnBind_List() {
 	return
 	return
 }
 }
 
 
+func (c *UserController) AdminCompanyBind_Add_All() {
+	T_uuid := c.GetString("T_uuid")
+	if len(T_uuid) < 8 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_uuid Err!"}
+		c.ServeJSON()
+		return
+	}
+
+	r, err := Account.Read_Admin_ByUuid(T_uuid)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_uuid Err!"}
+		c.ServeJSON()
+		return
+	}
+
+	if err = Account.Add_Company_bind_All(r); err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "公司绑定失败"}
+		c.ServeJSON()
+		return
+	}
+
+	System.Add_UserLogs(c.Admin_r.T_uuid, "内部用户管理", "公司绑定全部", T_uuid)
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+	return
+}
 func (c *UserController) AdminCompanyBind_Add() {
 func (c *UserController) AdminCompanyBind_Add() {
 	T_uuid := c.GetString("T_uuid")
 	T_uuid := c.GetString("T_uuid")
 	if len(T_uuid) < 8 {
 	if len(T_uuid) < 8 {
@@ -1375,9 +1405,19 @@ func (c *UserController) AdminCompanyBind_Del() {
 	}
 	}
 
 
 	r, err := Account.Read_Admin_ByUuid(T_uuid)
 	r, err := Account.Read_Admin_ByUuid(T_uuid)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "获取用户信息失败!"}
+		c.ServeJSON()
+		return
+	}
+	if r.T_pids == "*" {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "绑定全部公司的用户不能单个解绑!"}
+		c.ServeJSON()
+		return
+	}
 	Company_r, err := Account.Read_Company_ById(T_pid)
 	Company_r, err := Account.Read_Company_ById(T_pid)
 	if err != nil {
 	if err != nil {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_pid Err!"}
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "获取公司信息失败!"}
 		c.ServeJSON()
 		c.ServeJSON()
 		return
 		return
 	}
 	}
@@ -1390,7 +1430,7 @@ func (c *UserController) AdminCompanyBind_Del() {
 	}
 	}
 
 
 	if err = Account.Delete_Company_bind(r, T_pids); err != nil {
 	if err = Account.Delete_Company_bind(r, T_pids); err != nil {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "公司绑定失败"}
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "解绑公司失败"}
 		c.ServeJSON()
 		c.ServeJSON()
 		return
 		return
 	}
 	}
@@ -1403,6 +1443,39 @@ func (c *UserController) AdminCompanyBind_Del() {
 	c.ServeJSON()
 	c.ServeJSON()
 	return
 	return
 }
 }
+func (c *UserController) AdminCompanyBind_Del_All() {
+	T_uuid := c.GetString("T_uuid")
+	if len(T_uuid) < 8 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_uuid Err!"}
+		c.ServeJSON()
+		return
+	}
+
+	r, err := Account.Read_Admin_ByUuid(T_uuid)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "获取用户信息失败!"}
+		c.ServeJSON()
+		return
+	}
+	var T_pids []int
+	if len(r.T_pids) > 0 && r.T_pids != "*" {
+		T_pids = lib.SplitStringToIntIds(r.T_pids, "P")
+	}
+
+	if err = Account.Delete_Company_bind(r, T_pids); err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "解绑公司失败"}
+		c.ServeJSON()
+		return
+	}
+	if is := Company.Delete_CompanyNotice_Bind_By_T_uuid_T_pid(T_uuid, T_pids); !is {
+		logs.Info("删除内部用户公司绑定 => 删除内部用户绑定的公司关联报警通知失败", T_uuid)
+	}
+
+	System.Add_UserLogs(c.Admin_r.T_uuid, "内部用户管理", "公司绑定删除全部", T_uuid)
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+	return
+}
 
 
 // 权限管理
 // 权限管理
 func (c *UserController) Power_List() {
 func (c *UserController) Power_List() {

+ 1 - 1
main.go

@@ -32,7 +32,7 @@ func init() {
 	orm.RegisterDataBase("default", "mysql",
 	orm.RegisterDataBase("default", "mysql",
 		conf.MysqlServer_Username+":"+conf.MysqlServer_Password+"@tcp("+conf.MysqlServer_UrlPort+")/"+conf.MysqlServer_Database+"?charset=utf8mb4&loc=Local&parseTime=True",
 		conf.MysqlServer_Username+":"+conf.MysqlServer_Password+"@tcp("+conf.MysqlServer_UrlPort+")/"+conf.MysqlServer_Database+"?charset=utf8mb4&loc=Local&parseTime=True",
 		conf.MysqlServer_MaxIdleConnections, conf.MysqlServer_MaxOpenConnections)
 		conf.MysqlServer_MaxIdleConnections, conf.MysqlServer_MaxOpenConnections)
-	orm.RunSyncdb("default", false, false) // 创建数据库
+	orm.RunSyncdb("default", false, true) // 创建数据库
 	println(conf.MysqlServer_Username + ":" + conf.MysqlServer_Password + "@tcp(" + conf.MysqlServer_UrlPort + ")/" + conf.MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True")
 	println(conf.MysqlServer_Username + ":" + conf.MysqlServer_Password + "@tcp(" + conf.MysqlServer_UrlPort + ")/" + conf.MysqlServer_Database + "?charset=utf8mb4&loc=Local&parseTime=True")
 	orm2.Debug = conf.MysqlServer_Debug
 	orm2.Debug = conf.MysqlServer_Debug
 
 

+ 15 - 1
models/Account/Admin.go

@@ -478,6 +478,18 @@ func Add_Company_bind(Admin_r Admin, T_pids []int) (err error) {
 	Redis_Admin_Set(Admin_r) // Redis 更新缓存
 	Redis_Admin_Set(Admin_r) // Redis 更新缓存
 	return nil
 	return nil
 }
 }
+func Add_Company_bind_All(Admin_r Admin) (err error) {
+
+	o := orm.NewOrm()
+	Admin_r.T_pids = "*"
+	_, err = o.Update(&Admin_r, "T_pids")
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return err
+	}
+	Redis_Admin_Set(Admin_r) // Redis 更新缓存
+	return nil
+}
 
 
 func Delete_Company_bind(Admin_r Admin, T_pids []int) (err error) {
 func Delete_Company_bind(Admin_r Admin, T_pids []int) (err error) {
 
 
@@ -486,7 +498,9 @@ func Delete_Company_bind(Admin_r Admin, T_pids []int) (err error) {
 		T_Pid_bind := "P" + strconv.Itoa(T_pid) + "|"
 		T_Pid_bind := "P" + strconv.Itoa(T_pid) + "|"
 		Admin_r.T_pids = strings.Replace(Admin_r.T_pids, T_Pid_bind, "", -1)
 		Admin_r.T_pids = strings.Replace(Admin_r.T_pids, T_Pid_bind, "", -1)
 	}
 	}
-
+	if Admin_r.T_pids == "*" {
+		Admin_r.T_pids = ""
+	}
 	_, err = o.Update(&Admin_r, "T_pids")
 	_, err = o.Update(&Admin_r, "T_pids")
 	if err != nil {
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 		logs.Error(lib.FuncName(), err)

+ 4 - 1
models/Company/CompanyNotice.go

@@ -451,13 +451,16 @@ func Delete_CompanyNotice_Bind_By_T_uuid_T_pid(T_uuid string, T_pids []int) bool
 
 
 	cond := orm.NewCondition()
 	cond := orm.NewCondition()
 
 
-	cond1 := cond.And("T_State", 1).And("T_pid__in", T_pids).AndCond(
+	cond1 := cond.And("T_State", 1).AndCond(
 		cond.Or("T_Notice_wx__icontains", T_uuid).
 		cond.Or("T_Notice_wx__icontains", T_uuid).
 			Or("T_Notice_wx2__icontains", T_uuid).
 			Or("T_Notice_wx2__icontains", T_uuid).
 			Or("T_Notice_app__icontains", T_uuid).
 			Or("T_Notice_app__icontains", T_uuid).
 			Or("T_Notice_phone__icontains", T_uuid).
 			Or("T_Notice_phone__icontains", T_uuid).
 			Or("T_Notice_message__icontains", T_uuid).
 			Or("T_Notice_message__icontains", T_uuid).
 			Or("T_Notice_mailbox__icontains", T_uuid))
 			Or("T_Notice_mailbox__icontains", T_uuid))
+	if len(T_pids) > 0 {
+		cond1 = cond1.And("T_pid__in", T_pids)
+	}
 
 
 	_, err := qs.SetCond((*orm2.Condition)(cond1)).All(&map_r)
 	_, err := qs.SetCond((*orm2.Condition)(cond1)).All(&map_r)
 	if err != nil {
 	if err != nil {

+ 380 - 5
models/Device/DeviceData.go

@@ -6,16 +6,17 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
 	"github.com/astaxie/beego/cache"
 	"github.com/astaxie/beego/cache"
 	_ "github.com/astaxie/beego/cache/redis"
 	_ "github.com/astaxie/beego/cache/redis"
 	"github.com/astaxie/beego/logs"
 	"github.com/astaxie/beego/logs"
 	"github.com/beego/beego/v2/adapter/orm"
 	"github.com/beego/beego/v2/adapter/orm"
 	orm2 "github.com/beego/beego/v2/client/orm"
 	orm2 "github.com/beego/beego/v2/client/orm"
 	_ "github.com/go-sql-driver/mysql"
 	_ "github.com/go-sql-driver/mysql"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
 )
 )
 
 
 // 建表
 // 建表
@@ -79,6 +80,20 @@ type DeviceData_R2 struct {
 }
 }
 
 
 // 模板
 // 模板
+type DeviceData_ChartShow struct {
+	T_sn   string  // sn
+	T_id   int     // 传感器id
+	T_name string  // 传感器名称
+	T_t    float32 // 温度
+	T_rh   float32 // 湿度
+	T_tl   float32 // 温度下限
+	T_tu   float32 // 温度上限
+	T_rhl  float32 // 湿度下限
+	T_rhu  float32 // 湿度上限
+	T_time string  // 采集时间
+}
+
+// 模板
 type DeviceData_Docking struct {
 type DeviceData_Docking struct {
 	T_monitor  int     // 监控状态 0 未监控 1 监控
 	T_monitor  int     // 监控状态 0 未监控 1 监控
 	T_online   int     // 在线状态 0 未启用  1 在线  2 离线
 	T_online   int     // 在线状态 0 未启用  1 在线  2 离线
@@ -181,6 +196,22 @@ func DeviceDataToDeviceData_R2(d Device, sp DeviceSensorParameter_R, r DeviceDat
 	return t
 	return t
 }
 }
 
 
+func DeviceData_ToDeviceData_ChartShow(d Device, r DeviceData_) (t DeviceData_ChartShow) {
+
+	t.T_id = r.T_id
+	t.T_t = r.T_t
+	t.T_rh = r.T_rh
+	t.T_time = r.T_time.Format("2006-01-02 15:04:05")
+	sp := Read_DeviceSensorParameter_Map_Get(r.T_sp)
+	t.T_name = sp.T_name
+	t.T_tl = sp.T_Tlower
+	t.T_tu = sp.T_Tupper
+	t.T_rhl = sp.T_RHlower
+	t.T_rhu = sp.T_RHupper
+
+	return t
+}
+
 // ---------------- Redis -------------------
 // ---------------- Redis -------------------
 // Redis_Device_Set(m.T_sn,m) // Redis 更新缓存
 // Redis_Device_Set(m.T_sn,m) // Redis 更新缓存
 func RedisDeviceData_Set(key string, r DeviceData_) (err error) {
 func RedisDeviceData_Set(key string, r DeviceData_) (err error) {
@@ -260,6 +291,46 @@ func AssistedPositioning_Get(T_sn string, T_id int) (r DeviceData_R1, is bool) {
 	return DeviceData_R1{}, false
 	return DeviceData_R1{}, false
 }
 }
 
 
+// Redis_DeviceDataStatistics_Set 设置统计数据缓存
+func Redis_DeviceDataStatistics_Set(SN string, T_id int, Time_start string, Time_end string, stats DeviceDataStatistics) error {
+	// 构建缓存key: device_data_stats:SN:T_id:Time_start:Time_end
+	key := fmt.Sprintf("device_data_stats:%s:%d:%s:%s", SN, T_id, Time_start, Time_end)
+
+	// json序列化
+	str, err := json.Marshal(stats)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return err
+	}
+
+	// 缓存10分钟
+	err = redis_DeviceData.Put(key, str, 10*time.Minute)
+	if err != nil {
+		logs.Error("Redis_DeviceDataStatistics_Set", "set key:", key, err)
+		return err
+	}
+	return nil
+}
+
+// Redis_DeviceDataStatistics_Get 获取统计数据缓存
+func Redis_DeviceDataStatistics_Get(SN string, T_id int, Time_start string, Time_end string) (stats DeviceDataStatistics, found bool) {
+	// 构建缓存key
+	key := fmt.Sprintf("device_data_stats:%s:%d:%s:%s", SN, T_id, Time_start, Time_end)
+
+	if !redis_DeviceData.IsExist(key) {
+		return DeviceDataStatistics{}, false
+	}
+
+	v := redis_DeviceData.Get(key)
+	err := json.Unmarshal(v.([]byte), &stats)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return DeviceDataStatistics{}, false
+	}
+
+	return stats, true
+}
+
 // -------------------------------------------------------
 // -------------------------------------------------------
 // 创建数据库  Device.CREATE_DeviceData("")
 // 创建数据库  Device.CREATE_DeviceData("")
 func CREATE_DeviceData(SN string) bool {
 func CREATE_DeviceData(SN string) bool {
@@ -445,6 +516,116 @@ func Read_DeviceData_ById_List(SN string, T_id int, Time_start_ string, Time_end
 
 
 	return r, key
 	return r, key
 }
 }
+
+func Read_DeviceData_ById_List_ChartShow(SN string, T_id int, Time_start_ string, Time_end_ string) ([]DeviceData_ChartShow, int) {
+	o := orm.NewOrm()
+	var maps []DeviceData_
+
+	sql_time := ""
+
+	if len(Time_start_) > 1 {
+		sql_time += " t_time >= '" + Time_start_ + "' AND "
+	}
+
+	if len(Time_end_) > 1 {
+		sql_time += " t_time <= '" + Time_end_ + "' AND "
+	}
+
+	// 优化:移除不必要的 COUNT 查询,直接查询数据
+	sql := "SELECT t_id,t_sp,t_t,t_rh,t_site,DATE_FORMAT(t_time,'%Y-%m-%d %H:%i:%s') AS t_time,t_time AS t_time1 FROM z_device_data_" + SN + " WHERE " + sql_time + " t_id = " + strconv.Itoa(T_id) + " ORDER BY t_time1 DESC,t_id DESC "
+
+	logs.Debug(sql)
+	_, err := o.Raw(sql).QueryRows(&maps)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return []DeviceData_ChartShow{}, 0
+	}
+
+	// 如果没有数据,直接返回
+	if len(maps) == 0 {
+		return []DeviceData_ChartShow{}, 0
+	}
+
+	// 优化:预分配结果切片容量,减少内存重新分配
+	r := make([]DeviceData_ChartShow, 0, len(maps))
+
+	// 获取传感器名称和参数(只查询一次)
+	deviceSensor, _ := Read_DeviceSensor_ByT_sn(SN, T_id)
+	sp, _ := Read_DeviceSensorParameter(deviceSensor.T_sn, deviceSensor.T_id)
+
+	// 转换数据
+	for _, v := range maps {
+		item := DeviceData_ChartShow{
+			T_sn:   SN,
+			T_id:   v.T_id,
+			T_t:    v.T_t,
+			T_rh:   v.T_rh,
+			T_time: v.T_time.Format("2006-01-02 15:04:05"),
+			T_name: deviceSensor.T_name,
+			T_tl:   sp.T_Tlower,
+			T_tu:   sp.T_Tupper,
+			T_rhl:  sp.T_RHlower,
+			T_rhu:  sp.T_RHupper,
+		}
+		r = append(r, item)
+	}
+
+	return r, len(r)
+}
+
+// Read_DeviceData_ById_List_ChartShow_WithCache 优化版本:接受预加载的传感器信息,避免重复查询
+func Read_DeviceData_ById_List_ChartShow_WithCache(SN string, T_id int, Time_start_ string, Time_end_ string, deviceSensor DeviceSensor, sp DeviceSensorParameter_R) ([]DeviceData_ChartShow, int) {
+	o := orm.NewOrm()
+	var maps []DeviceData_
+
+	sql_time := ""
+
+	if len(Time_start_) > 1 {
+		sql_time += " t_time >= '" + Time_start_ + "' AND "
+	}
+
+	if len(Time_end_) > 1 {
+		sql_time += " t_time <= '" + Time_end_ + "' AND "
+	}
+
+	// 优化:移除不必要的 COUNT 查询,直接查询数据
+	sql := "SELECT t_id,t_sp,t_t,t_rh,t_site,DATE_FORMAT(t_time,'%Y-%m-%d %H:%i:%s') AS t_time,t_time AS t_time1 FROM z_device_data_" + SN + " WHERE " + sql_time + " t_id = " + strconv.Itoa(T_id) + " ORDER BY t_time1 DESC,t_id DESC "
+
+	logs.Debug(sql)
+	_, err := o.Raw(sql).QueryRows(&maps)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return []DeviceData_ChartShow{}, 0
+	}
+
+	// 如果没有数据,直接返回
+	if len(maps) == 0 {
+		return []DeviceData_ChartShow{}, 0
+	}
+
+	// 优化:预分配结果切片容量,减少内存重新分配
+	r := make([]DeviceData_ChartShow, 0, len(maps))
+
+	// 使用传入的传感器信息,无需再次查询
+	for _, v := range maps {
+		item := DeviceData_ChartShow{
+			T_sn:   SN,
+			T_id:   v.T_id,
+			T_t:    v.T_t,
+			T_rh:   v.T_rh,
+			T_time: v.T_time.Format("2006-01-02 15:04:05"),
+			T_name: deviceSensor.T_name,
+			T_tl:   sp.T_Tlower,
+			T_tu:   sp.T_Tupper,
+			T_rhl:  sp.T_RHlower,
+			T_rhu:  sp.T_RHupper,
+		}
+		r = append(r, item)
+	}
+
+	return r, len(r)
+}
+
 func Read_DeviceData_ById_List_OrderByTimeAes(SN string, T_id int, Time_start_ string, Time_end_ string, page int, page_z int) ([]DeviceData_R, int) {
 func Read_DeviceData_ById_List_OrderByTimeAes(SN string, T_id int, Time_start_ string, Time_end_ string, page int, page_z int) ([]DeviceData_R, int) {
 	o := orm.NewOrm()
 	o := orm.NewOrm()
 	var maps []DeviceData_
 	var maps []DeviceData_
@@ -623,7 +804,7 @@ func Read_DeviceData_By_T_snid_List(T_snid string, Time_start_ string, Time_end_
 		if len(sn_id) == 3 {
 		if len(sn_id) == 3 {
 			Read_DeviceSensorParameter_All_Map(sn_id[0], lib.To_int(sn_id[1]))
 			Read_DeviceSensorParameter_All_Map(sn_id[0], lib.To_int(sn_id[1]))
 			r_maps, r_maps_num := Read_DeviceData_ById_List(sn_id[0], lib.To_int(sn_id[1]), Time_start_, Time_end_, 0, 9999)
 			r_maps, r_maps_num := Read_DeviceData_ById_List(sn_id[0], lib.To_int(sn_id[1]), Time_start_, Time_end_, 0, 9999)
-			for i, _ := range r_maps {
+			for i := range r_maps {
 				atoi, _ := strconv.Atoi(sn_id[2])
 				atoi, _ := strconv.Atoi(sn_id[2])
 				r_maps[i].Sorts = atoi
 				r_maps[i].Sorts = atoi
 			}
 			}
@@ -675,6 +856,200 @@ func Read_DeviceData_By_T_snid_List(T_snid string, Time_start_ string, Time_end_
 	return maps[offset:offset_z], maps_num
 	return maps[offset:offset_z], maps_num
 }
 }
 
 
+func Read_DeviceData_By_T_snid_List_ChartShow(T_snid string, Time_start_ string, Time_end_ string) ([]DeviceData_ChartShow, int64) {
+	T_snid_list := strings.Split(strings.Trim(T_snid, "|"), "|")
+
+	// 优化:批量预加载所有传感器信息和参数,避免 N+1 查询问题
+	sensorMap := make(map[string]DeviceSensor)               // key: "SN_T_id"
+	parameterMap := make(map[string]DeviceSensorParameter_R) // key: "SN_T_id"
+
+	for _, v := range T_snid_list {
+		sn_id := strings.Split(v, ",")
+		if len(sn_id) < 2 {
+			continue
+		}
+		sn := sn_id[0]
+		sensorId := lib.To_int(sn_id[1])
+		key := fmt.Sprintf("%s_%d", sn, sensorId)
+
+		// 预加载传感器信息
+		if sensor, ok := Read_DeviceSensor_ByT_sn(sn, sensorId); ok {
+			sensorMap[key] = sensor
+			// 预加载传感器参数
+			if param, ok := Read_DeviceSensorParameter(sn, sensorId); ok {
+				parameterMap[key] = param
+			}
+		}
+	}
+
+	// 预估容量以减少内存重新分配
+	estimatedCapacity := len(T_snid_list) * 100
+	maps := make([]DeviceData_ChartShow, 0, estimatedCapacity)
+	var maps_num int64
+
+	// 遍历所有传感器,加载数据
+	for _, v := range T_snid_list {
+		sn_id := strings.Split(v, ",")
+		if len(sn_id) < 2 {
+			continue
+		}
+
+		// 获取 SN 和传感器 ID
+		sn := sn_id[0]
+		sensorId := lib.To_int(sn_id[1])
+
+		// 使用优化后的函数,传入预加载的传感器信息
+		key := fmt.Sprintf("%s_%d", sn, sensorId)
+		sensor, hasSensor := sensorMap[key]
+		param, hasParam := parameterMap[key]
+
+		if !hasSensor || !hasParam {
+			logs.Debug("跳过传感器(未找到信息):", sn, sensorId)
+			continue
+		}
+
+		// 获取数据列表(使用新的优化函数)
+		r_maps, r_maps_num := Read_DeviceData_ById_List_ChartShow_WithCache(sn, sensorId, Time_start_, Time_end_, sensor, param)
+
+		maps = append(maps, r_maps...)
+		maps_num += int64(r_maps_num)
+
+		logs.Debug("加载数据:", sn, sensorId, r_maps_num)
+	}
+
+	// 如果没有数据,直接返回
+	if maps_num == 0 {
+		return maps, maps_num
+	}
+
+	// 按时间倒序排序
+	sort.Slice(maps, func(i, j int) bool {
+		return maps[i].T_time > maps[j].T_time
+	})
+
+	logs.Debug("总数据:", maps_num, " 导出全部")
+	return maps, maps_num
+}
+
+// DeviceDataStatistics 设备数据统计结构(单个传感器)
+type DeviceDataStatistics struct {
+	SN          string  `json:"sn"`           // 设备SN
+	SensorId    int     `json:"sensor_id"`    // 传感器ID
+	SensorName  string  `json:"sensor_name"`  // 传感器名称
+	TempMax     float32 `json:"temp_max"`     // 最高温度
+	TempMin     float32 `json:"temp_min"`     // 最低温度
+	TempAvg     float32 `json:"temp_avg"`     // 平均温度
+	HumidityMax float32 `json:"humidity_max"` // 最高湿度
+	HumidityMin float32 `json:"humidity_min"` // 最低湿度
+	HumidityAvg float32 `json:"humidity_avg"` // 平均湿度
+	DataCount   int64   `json:"data_count"`   // 数据条数
+}
+
+// DeviceDataStatisticsTotal 所有传感器数据统计汇总结构
+type DeviceDataStatisticsTotal struct {
+	TempMax     float32 `json:"temp_max"`     // 所有传感器中的最高温度
+	TempMin     float32 `json:"temp_min"`     // 所有传感器中的最低温度
+	TempAvg     float32 `json:"temp_avg"`     // 所有传感器温度平均值
+	HumidityMax float32 `json:"humidity_max"` // 所有传感器中的最高湿度
+	HumidityMin float32 `json:"humidity_min"` // 所有传感器中的最低湿度
+	HumidityAvg float32 `json:"humidity_avg"` // 所有传感器湿度平均值
+	DataCount   int64   `json:"data_count"`   // 总数据条数
+}
+
+// Read_DeviceData_Statistics 获取设备数据统计信息
+func Read_DeviceData_Statistics(SN string, T_id int, Time_start_ string, Time_end_ string) (DeviceDataStatistics, error) {
+	// 先尝试从缓存获取
+	if cachedStats, found := Redis_DeviceDataStatistics_Get(SN, T_id, Time_start_, Time_end_); found {
+		logs.Debug("从缓存获取统计数据: SN=%s, T_id=%d", SN, T_id)
+		return cachedStats, nil
+	}
+
+	o := orm.NewOrm()
+	var stats DeviceDataStatistics
+	var result []orm2.ParamsList
+
+	// 构建时间条件
+	sql_time := ""
+	if len(Time_start_) > 1 {
+		sql_time += " t_time >= '" + Time_start_ + "' AND "
+	}
+	if len(Time_end_) > 1 {
+		sql_time += " t_time <= '" + Time_end_ + "' AND "
+	}
+
+	// 查询统计数据:最大值、最小值、平均值、数据条数
+	sql := "SELECT " +
+		"MAX(t_t) AS temp_max, " +
+		"MIN(t_t) AS temp_min, " +
+		"AVG(t_t) AS temp_avg, " +
+		"MAX(t_rh) AS humidity_max, " +
+		"MIN(t_rh) AS humidity_min, " +
+		"AVG(t_rh) AS humidity_avg, " +
+		"COUNT(*) AS data_count " +
+		"FROM z_device_data_" + SN + " " +
+		"WHERE " + sql_time + " t_id = " + strconv.Itoa(T_id)
+
+	logs.Debug(sql)
+	_, err := o.Raw(sql).ValuesList(&result)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return stats, err
+	}
+
+	if len(result) == 0 || result[0][0] == nil {
+		return stats, errors.New("没有查询到数据")
+	}
+
+	// 填充统计数据
+	stats.SN = SN
+	stats.SensorId = T_id
+
+	// 解析统计结果
+	if result[0][0] != nil {
+		if val, err := strconv.ParseFloat(result[0][0].(string), 32); err == nil {
+			stats.TempMax = float32(val)
+		}
+	}
+	if result[0][1] != nil {
+		if val, err := strconv.ParseFloat(result[0][1].(string), 32); err == nil {
+			stats.TempMin = float32(val)
+		}
+	}
+	if result[0][2] != nil {
+		if val, err := strconv.ParseFloat(result[0][2].(string), 32); err == nil {
+			stats.TempAvg = float32(val)
+		}
+	}
+	if result[0][3] != nil {
+		if val, err := strconv.ParseFloat(result[0][3].(string), 32); err == nil {
+			stats.HumidityMax = float32(val)
+		}
+	}
+	if result[0][4] != nil {
+		if val, err := strconv.ParseFloat(result[0][4].(string), 32); err == nil {
+			stats.HumidityMin = float32(val)
+		}
+	}
+	if result[0][5] != nil {
+		if val, err := strconv.ParseFloat(result[0][5].(string), 32); err == nil {
+			stats.HumidityAvg = float32(val)
+		}
+	}
+	if result[0][6] != nil {
+		if val, err := strconv.ParseInt(result[0][6].(string), 10, 64); err == nil {
+			stats.DataCount = val
+		}
+	}
+
+	// 将统计数据存入缓存(10分钟有效期)
+	if err := Redis_DeviceDataStatistics_Set(SN, T_id, Time_start_, Time_end_, stats); err != nil {
+		logs.Error("缓存统计数据失败: ", err)
+		// 即使缓存失败也继续返回数据
+	}
+
+	return stats, nil
+}
+
 func Read_DeviceData_ById_Year_List(SN string) []orm2.ParamsList {
 func Read_DeviceData_ById_Year_List(SN string) []orm2.ParamsList {
 	o := orm.NewOrm()
 	o := orm.NewOrm()
 
 

+ 3 - 1
models/Device/DeviceSensor.go

@@ -45,6 +45,7 @@ type DeviceSensor struct {
 	T_online   int    `orm:"size(2);default(1)"` // 在线状态 0 未启用  1 在线  2 离线
 	T_online   int    `orm:"size(2);default(1)"` // 在线状态 0 未启用  1 在线  2 离线
 	T_online_s int    `orm:"size(2);default(0)"` // 在线状态-备用  0 未启用  1 在线  2 离线
 	T_online_s int    `orm:"size(2);default(0)"` // 在线状态-备用  0 未启用  1 在线  2 离线
 
 
+	T_number  string `orm:"size(256);null"`                  //传感器探头编号
 	ProbeIdNo string `json:"probeIdNo" orm:"size(256);null"` //传感器探头序号
 	ProbeIdNo string `json:"probeIdNo" orm:"size(256);null"` //传感器探头序号
 
 
 	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
 	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
@@ -151,6 +152,7 @@ type DeviceSensor_P struct {
 	T_type      int    // 类型
 	T_type      int    // 类型
 	T_type_name string // 类型
 	T_type_name string // 类型
 	T_Class     string // 分类原始字符串 C1|C2|
 	T_Class     string // 分类原始字符串 C1|C2|
+	T_number    string // 探头编号
 
 
 	T_sort     int // 排序
 	T_sort     int // 排序
 	T_datashow int // 0 屏蔽数据展示  1 正常数据展示
 	T_datashow int // 0 屏蔽数据展示  1 正常数据展示
@@ -1106,7 +1108,7 @@ func Read_DeviceSensorManageList(admin_r *Account.Admin, bindSN []string, T_pid
 	}
 	}
 	//"LEFT JOIN (SELECT id,t_en,t_free,t__tlower,t__tupper,t__r_hlower,t__r_hupper,t_enprel,t_tprel,t_tpreu,t_hprel,t_hpreu " +
 	//"LEFT JOIN (SELECT id,t_en,t_free,t__tlower,t__tupper,t__r_hlower,t__r_hupper,t_enprel,t_tprel,t_tpreu,t_hprel,t_hpreu " +
 	//"FROM device_sensor_parameter) dsp " +
 	//"FROM device_sensor_parameter) dsp " +
-	sql = "SELECT ds.t_sn,ds.t_id,ds.t_name,t_type,t_sort,t_datashow,ds.t__state,t_en,t_free,t__tlower,t__tupper,t__r_hlower,t__r_hupper,t_enprel,t_tprel,t_tpreu,t_hprel,t_hpreu,ds.t__class FROM device_sensor ds " +
+	sql = "SELECT ds.t_sn,ds.t_id,ds.t_name,t_number,t_type,t_sort,t_datashow,ds.t__state,t_en,t_free,t__tlower,t__tupper,t__r_hlower,t__r_hupper,t_enprel,t_tprel,t_tpreu,t_hprel,t_hpreu,ds.t__class FROM device_sensor ds " +
 		"LEFT JOIN device_sensor_parameter dsp " +
 		"LEFT JOIN device_sensor_parameter dsp " +
 		"ON ds.t_sp=dsp.id " +
 		"ON ds.t_sp=dsp.id " +
 		"WHERE " + sql_WHERE + sql_ORDER
 		"WHERE " + sql_WHERE + sql_ORDER

+ 1 - 1
models/FileManager/FileManager.go

@@ -23,7 +23,7 @@ type FileManager struct {
 	T_uuid string `orm:"size(128);unique"`          // 文件唯一标识
 	T_uuid string `orm:"size(128);unique"`          // 文件唯一标识
 	T_pid  int    `orm:"index;size(11);default(0)"` // 公司ID,同公司用户可互相查看
 	T_pid  int    `orm:"index;size(11);default(0)"` // 公司ID,同公司用户可互相查看
 	T_name string `orm:"size(255)"`                 // 文件名
 	T_name string `orm:"size(255)"`                 // 文件名
-	T_path string `orm:"size(1000);index"`          // 文件绝对路径
+	T_path string `orm:"type(text)"`                // 文件绝对路径
 	T_url  string `orm:"size(500);null"`            // 七牛云文件链接
 	T_url  string `orm:"size(500);null"`            // 七牛云文件链接
 	T_size int64  `orm:"size(20);default(0)"`       // 文件大小(字节)
 	T_size int64  `orm:"size(20);default(0)"`       // 文件大小(字节)
 	T_type string `orm:"size(50);null"`             // 文件类型(文件夹/文件后缀+文件)
 	T_type string `orm:"size(50);null"`             // 文件类型(文件夹/文件后缀+文件)

+ 9 - 6
routers/Data.go

@@ -3,6 +3,7 @@ package routers
 import (
 import (
 	"Cold_Api/conf"
 	"Cold_Api/conf"
 	"Cold_Api/controllers"
 	"Cold_Api/controllers"
+
 	beego "github.com/beego/beego/v2/server/web"
 	beego "github.com/beego/beego/v2/server/web"
 )
 )
 
 
@@ -24,12 +25,14 @@ func init() {
 			beego.NSRouter("/Device_Sensor_Data_Excel", &controllers.DataController{}, "*:Device_Sensor_Data_Excel"),               // 导出传感器数据
 			beego.NSRouter("/Device_Sensor_Data_Excel", &controllers.DataController{}, "*:Device_Sensor_Data_Excel"),               // 导出传感器数据
 			beego.NSRouter("/Device_Sensor_Data_Excel_m", &controllers.DataController{}, "*:Device_Sensor_Data_Excel_m"),           // 导出传感器数据
 			beego.NSRouter("/Device_Sensor_Data_Excel_m", &controllers.DataController{}, "*:Device_Sensor_Data_Excel_m"),           // 导出传感器数据
 
 
-			beego.NSRouter("/List", &controllers.DataController{}, "*:Device_Sensor_Data_More"),                   // 设备数据列表
-			beego.NSRouter("/Excel", &controllers.DataController{}, "*:Device_Sensor_Data_Excel"),                 // 导出设备数据列表excel
-			beego.NSRouter("/PDF", &controllers.DataController{}, "*:Device_Sensor_Data_PDF"),                     // 设备数据列表pdf
-			beego.NSRouter("/ChartShow_PDF", &controllers.DataController{}, "*:Device_Sensor_Data_ChartShow_PDF"), // 图表展示下载pdf
-			beego.NSRouter("/BackUp", &controllers.DataController{}, "*:Device_Sensor_Data_BackUp"),               // 设备数据备份列表
-			beego.NSRouter("/BackUp_PDF", &controllers.DataController{}, "*:Device_Sensor_Data_BackUp_PDF"),       // 设备数据备份列表PDF
+			beego.NSRouter("/List", &controllers.DataController{}, "*:Device_Sensor_Data_More"),                               // 设备数据列表
+			beego.NSRouter("/ChartShow_List", &controllers.DataController{}, "*:Device_Sensor_Data_ChartShow"),                // 图片展示数据列表
+			beego.NSRouter("/ChartShow_Calculate", &controllers.DataController{}, "*:Device_Sensor_Data_ChartShow_calculate"), // 图表展示数据统计
+			beego.NSRouter("/Excel", &controllers.DataController{}, "*:Device_Sensor_Data_Excel"),                             // 导出设备数据列表excel
+			beego.NSRouter("/PDF", &controllers.DataController{}, "*:Device_Sensor_Data_PDF"),                                 // 设备数据列表pdf
+			beego.NSRouter("/ChartShow_PDF", &controllers.DataController{}, "*:Device_Sensor_Data_ChartShow_PDF"),             // 图表展示下载pdf
+			beego.NSRouter("/BackUp", &controllers.DataController{}, "*:Device_Sensor_Data_BackUp"),                           // 设备数据备份列表
+			beego.NSRouter("/BackUp_PDF", &controllers.DataController{}, "*:Device_Sensor_Data_BackUp_PDF"),                   // 设备数据备份列表PDF
 
 
 			beego.NSRouter("/DeviceSensor_Data_Print", &controllers.DataController{}, "*:DeviceSensor_Data_Print"), // 小程序 打印
 			beego.NSRouter("/DeviceSensor_Data_Print", &controllers.DataController{}, "*:DeviceSensor_Data_Print"), // 小程序 打印
 
 

+ 6 - 4
routers/User.go

@@ -36,10 +36,12 @@ func init() {
 		beego.NSRouter("/Admin/Edit", &controllers.UserController{}, "*:Admin_Edit"), // 编辑内部用户
 		beego.NSRouter("/Admin/Edit", &controllers.UserController{}, "*:Admin_Edit"), // 编辑内部用户
 		beego.NSRouter("/Admin/Del", &controllers.UserController{}, "*:Admin_Del"),   // 删除内部用户
 		beego.NSRouter("/Admin/Del", &controllers.UserController{}, "*:Admin_Del"),   // 删除内部用户
 		// 内部用户公司绑定
 		// 内部用户公司绑定
-		beego.NSRouter("/Admin/CompanyBind_List", &controllers.UserController{}, "*:AdminCompanyBind_List"),     // 内部用户公司绑定列表
-		beego.NSRouter("/Admin/CompanyUnBind_List", &controllers.UserController{}, "*:AdminCompanyUnBind_List"), // 内部用户未绑定公司列表
-		beego.NSRouter("/Admin/CompanyBind_Add", &controllers.UserController{}, "*:AdminCompanyBind_Add"),       // 添加内部用户公司绑定
-		beego.NSRouter("/Admin/CompanyBind_Del", &controllers.UserController{}, "*:AdminCompanyBind_Del"),       // 删除内部用户公司绑定
+		beego.NSRouter("/Admin/CompanyBind_List", &controllers.UserController{}, "*:AdminCompanyBind_List"),       // 内部用户公司绑定列表
+		beego.NSRouter("/Admin/CompanyUnBind_List", &controllers.UserController{}, "*:AdminCompanyUnBind_List"),   // 内部用户未绑定公司列表
+		beego.NSRouter("/Admin/CompanyBind_Add", &controllers.UserController{}, "*:AdminCompanyBind_Add"),         // 添加内部用户公司绑定
+		beego.NSRouter("/Admin/CompanyBind_Del", &controllers.UserController{}, "*:AdminCompanyBind_Del"),         // 删除内部用户公司绑定
+		beego.NSRouter("/Admin/CompanyBind_Add_All", &controllers.UserController{}, "*:AdminCompanyBind_Add_All"), // 删除内部用户公司绑定
+		beego.NSRouter("/Admin/CompanyBind_Del_All", &controllers.UserController{}, "*:AdminCompanyBind_Del_All"), // 删除内部用户公司绑定
 
 
 		// 权限
 		// 权限
 		beego.NSRouter("/Power/List", &controllers.UserController{}, "*:Power_List"),         // 权限列表
 		beego.NSRouter("/Power/List", &controllers.UserController{}, "*:Power_List"),         // 权限列表