Browse Source

add:菜单管理

zoie 6 months ago
parent
commit
bb71ef464e

+ 59 - 10
controllers/Data.go

@@ -171,11 +171,23 @@ func (c *DataController) Company_Device_Sensor_List() {
 		c.ServeJSON()
 		return
 	}
-	// 查询公司下面所有子公司id
-	T_pids := Account.ReadCompanyIds_T_path(Company_r.T_path)
+	T_company_name := c.GetString("T_company_name")
+	// 查询公司下面所有子公司id(支持公司名称过滤)
+	var T_pids []int
+	if len(T_company_name) > 0 {
+		T_pids = Account.ReadCompanyIds_T_path_ByName(Company_r.T_path, T_company_name)
+	} else {
+		T_pids = Account.ReadCompanyIds_T_path(Company_r.T_path)
+	}
 	var cnt int64
 	Account.Read_Company_All_Maps()
-	r_jsons.Data, cnt = Device.Read_CompanyDeviceSensor_List_For_Data(T_pids, T_name, T_Class_id, T_type, T_RealTime, T_MapShow, page, page_z)
+	if len(T_pids) == 0 {
+		// 名称过滤无结果,直接返回空
+		r_jsons.Data = nil
+		cnt = 0
+	} else {
+		r_jsons.Data, cnt = Device.Read_CompanyDeviceSensor_List_For_Data(T_pids, T_name, T_Class_id, T_type, T_RealTime, T_MapShow, page, page_z)
+	}
 
 	page_size := math.Ceil(float64(cnt) / float64(page_z))
 	r_jsons.Page = page
@@ -2803,18 +2815,13 @@ func (c *DataController) Docking_Note_Data() {
 		c.ServeJSON()
 		return
 	}
-	if st.Add(40 * 24 * time.Hour).Before(time.Now()) {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "查询时间范围超出限制(40天)"}
-		c.ServeJSON()
-		return
-	}
 	et, is := lib.TimeStrToTime(Time_end)
 	if !is {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "Time_start 格式不正确"}
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "Time_startCompany_Device_Sensor_List 格式不正确"}
 		c.ServeJSON()
 		return
 	}
-	if et.Add(40 * 24 * time.Hour).Before(time.Now()) {
+	if st.Add(40 * 24 * time.Hour).Before(et) {
 		c.Data["json"] = lib.JSONS{Code: 202, Msg: "查询时间范围超出限制(40天)"}
 		c.ServeJSON()
 		return
@@ -3426,3 +3433,45 @@ func (c *DataController) GetNewLocus_() {
 //		log.Println("发送消息失败:", err)
 //	}
 //}
+
+// Device_Data_Missing_Check 检测设备数据缺失情况
+func (c *DataController) Device_Data_Missing_Check() {
+	T_sn := c.GetString("T_sn")
+	T_id, _ := c.GetInt("T_id")
+	Time_start := c.GetString("Time_start")
+	Time_end := c.GetString("Time_end")
+
+	// 参数验证
+	if len(T_sn) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "设备序列号不能为空!"}
+		c.ServeJSON()
+		return
+	}
+	if T_id <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "传感器ID不能为空!"}
+		c.ServeJSON()
+		return
+	}
+	if len(Time_start) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "开始时间不能为空!"}
+		c.ServeJSON()
+		return
+	}
+	if len(Time_end) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "结束时间不能为空!"}
+		c.ServeJSON()
+		return
+	}
+
+	// 调用模型层方法检测数据缺失
+	result, err := Device.Read_DeviceData_Missing_Check(T_sn, T_id, Time_start, Time_end)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: err.Error()}
+		c.ServeJSON()
+		return
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: result}
+	c.ServeJSON()
+	return
+}

+ 362 - 0
controllers/Menu.go

@@ -0,0 +1,362 @@
+package controllers
+
+import (
+	"Cold_Api/controllers/lib"
+	"Cold_Api/models/Account"
+	"encoding/json"
+
+	"github.com/beego/beego/v2/core/logs"
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+type MenuController struct {
+	beego.Controller
+	Admin_r Account.Admin // 登陆的用户
+	T_pid   int           // 公司id
+}
+
+func (c *MenuController) Prepare() {
+	GetCookie := c.Ctx.GetCookie("User_tokey")
+	GetString := c.GetString("User_tokey")
+
+	User_tokey := GetString
+	if len(User_tokey) == 0 {
+		User_tokey = GetCookie
+	}
+	if Account.Admin_r == nil {
+		return
+	}
+	c.Admin_r = *Account.Admin_r
+	T_pid := c.Admin_r.T_pid
+	EntryPid, _ := Account.Redis_Tokey_T_pid_Get(User_tokey)
+	if EntryPid > 0 {
+		T_pid = EntryPid
+	}
+	c.T_pid = T_pid
+}
+
+// Menu_List 获取菜单列表(树形结构)
+func (c *MenuController) Menu_List() {
+	var r_jsons lib.R_JSONS
+
+	// 获取所有菜单
+	maps, _ := Account.Read_Menu_List_All()
+	r_jsons.Data = maps
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
+	c.ServeJSON()
+	return
+}
+
+// Menu_Get 根据ID获取菜单详情
+func (c *MenuController) Menu_Get() {
+	var r_jsons lib.R_JSONS
+
+	id, _ := c.GetInt("Id")
+
+	if id == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "参数错误:id格式不正确"}
+		c.ServeJSON()
+		return
+	}
+
+	menu, err := Account.Read_Menu_ById(id)
+	if err != nil {
+		logs.Error("获取菜单失败:", err)
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "获取菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 获取该菜单绑定的API列表
+	apis := Account.Read_API_List_ByMenuId(id)
+
+	result := map[string]interface{}{
+		"menu": menu,
+		"apis": apis,
+	}
+
+	r_jsons.Data = result
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
+	c.ServeJSON()
+	return
+}
+
+// Menu_Add 添加菜单
+func (c *MenuController) Menu_Add() {
+	// 从form-data中获取菜单信息
+	var menu Account.Menu
+
+	// 获取菜单基本信息
+	menu.T_mid, _ = c.GetInt("T_mid")
+
+	menu.T_name = c.GetString("T_name")
+	menu.T_permission = c.GetString("T_permission")
+	menu.T_icon = c.GetString("T_icon")
+	menu.T_type = c.GetString("T_type")
+
+	menu.T_sort, _ = c.GetInt("T_sort")
+
+	menu.T_pid, _ = c.GetInt("T_pid") // 1 进入公司后才能访问的菜单 0 公共菜单
+
+	// 验证必填字段
+	if len(menu.T_name) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "菜单名称不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	if len(menu.T_permission) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "权限标识不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	if len(menu.T_type) == 0 {
+		menu.T_type = Account.MenuType // 默认为菜单类型
+	}
+
+	menu.T_State = 1
+	// 添加菜单
+	id, err := Account.Add_Menu(menu)
+	if err != nil {
+		logs.Error("添加菜单失败:", err)
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "添加菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 处理API绑定
+	apisJsonStr := c.GetString("apis")
+	if len(apisJsonStr) > 0 {
+		var apis []Account.API
+		err := json.Unmarshal([]byte(apisJsonStr), &apis)
+		if err != nil {
+			logs.Error("解析API JSON字符串失败:", err)
+			c.Data["json"] = lib.JSONS{Code: 400, Msg: "API JSON格式错误"}
+			c.ServeJSON()
+			return
+		}
+
+		// 为每个API设置菜单ID并添加
+		for _, api := range apis {
+			api.T_Menu_Id = int(id)
+			api.T_enable = 1 // 默认启用
+			if len(api.T_method) == 0 {
+				api.T_method = "POST" // 默认GET方法
+			}
+			_, err := Account.Add_API(api)
+			if err != nil {
+				logs.Error("添加API绑定失败:", err)
+				// 继续处理其他API,不中断整个流程
+			}
+		}
+	}
+
+	// 清空所有菜单和API缓存
+	go func() {
+		Account.Redis_Menu_ClearAll()
+		Account.Redis_API_ClearAll()
+	}()
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "添加成功!", Data: id}
+	c.ServeJSON()
+	return
+}
+
+// Menu_Edit 编辑菜单(包含API绑定)
+func (c *MenuController) Menu_Edit() {
+
+	// 从form-data中获取菜单信息
+	var menu Account.Menu
+
+	// 获取菜单ID(必填)
+	id, _ := c.GetInt("Id")
+	if id == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "菜单ID不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	menu, err := Account.Read_Menu_ById(id)
+	if err != nil {
+		logs.Error("获取菜单失败:", err)
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "获取菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 获取菜单基本信息
+	menu.T_mid, _ = c.GetInt("T_mid")
+	menu.T_name = c.GetString("T_name")
+	menu.T_permission = c.GetString("T_permission")
+	menu.T_icon = c.GetString("T_icon")
+	menu.T_type = c.GetString("T_type")
+	menu.T_sort, _ = c.GetInt("T_sort")
+	menu.T_pid, _ = c.GetInt("T_pid")
+
+	// 验证必填字段
+	if len(menu.T_name) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "菜单名称不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	if len(menu.T_permission) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "权限标识不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	// 更新菜单信息
+	success := Account.Update_Menu(menu)
+	if !success {
+		logs.Error("更新菜单失败")
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "更新菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 处理API绑定
+	apisJsonStr := c.GetString("apis")
+	if len(apisJsonStr) > 0 {
+		var apis []Account.API
+		err = json.Unmarshal([]byte(apisJsonStr), &apis)
+		if err != nil {
+			logs.Error("解析API JSON字符串失败:", err)
+			c.Data["json"] = lib.JSONS{Code: 400, Msg: "API JSON格式错误"}
+			c.ServeJSON()
+			return
+		}
+
+		// 先删除该菜单下的所有API
+		existingAPIs := Account.Read_API_List_ByMenuId(menu.Id)
+		for _, api := range existingAPIs {
+			Account.Delete_API(api)
+		}
+
+		// 添加新的API绑定
+		for _, api := range apis {
+			api.T_Menu_Id = menu.Id
+			if len(api.T_method) == 0 {
+				api.T_method = "POST" // 默认GET方法
+			}
+			_, err := Account.Add_API(api)
+			if err != nil {
+				logs.Error("添加API绑定失败:", err)
+				// 继续处理其他API,不中断整个流程
+			}
+		}
+	}
+
+	// 清空所有菜单和API缓存
+	go func() {
+		Account.Redis_Menu_ClearAll()
+		Account.Redis_API_ClearAll()
+	}()
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "更新成功!", Data: id}
+	c.ServeJSON()
+	return
+}
+
+// Menu_Del 删除菜单
+func (c *MenuController) Menu_Del() {
+
+	// 获取菜单ID(必填)
+	id, _ := c.GetInt("T_id")
+	if id == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "菜单ID不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	// 检查是否有子菜单
+	maps, _ := Account.Read_Menu_List_All()
+	hasChildren := false
+	for _, menu := range maps {
+		if menu.T_mid == id {
+			hasChildren = true
+			break
+		}
+	}
+
+	if hasChildren {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "该菜单下还有子菜单,无法删除"}
+		c.ServeJSON()
+		return
+	}
+
+	// 删除菜单
+	menu := Account.Menu{Id: id}
+	success := Account.Delete_Menu(menu)
+	if !success {
+		logs.Error("删除菜单失败")
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "删除菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 删除该菜单下的所有API绑定
+	apis := Account.Read_API_List_ByMenuId(id)
+	for _, api := range apis {
+		Account.Delete_API(api)
+	}
+
+	// 清空所有菜单和API缓存
+	go func() {
+		Account.Redis_Menu_ClearAll()
+		Account.Redis_API_ClearAll()
+	}()
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "删除成功!", Data: id}
+	c.ServeJSON()
+	return
+}
+
+// Menu_Sort 更新菜单排序
+func (c *MenuController) Menu_Sort() {
+	var r_jsons lib.R_JSONS
+
+	var requestData struct {
+		MenuId int `json:"menu_id"`
+		Sort   int `json:"sort"`
+	}
+
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestData)
+	if err != nil {
+		logs.Error("解析数据失败:", err)
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "参数格式错误"}
+		c.ServeJSON()
+		return
+	}
+
+	if requestData.MenuId == 0 {
+		c.Data["json"] = lib.JSONS{Code: 400, Msg: "菜单ID不能为空"}
+		c.ServeJSON()
+		return
+	}
+
+	// 获取菜单信息
+	menu, err := Account.Read_Menu_ById(requestData.MenuId)
+	if err != nil {
+		logs.Error("获取菜单失败:", err)
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "获取菜单失败"}
+		c.ServeJSON()
+		return
+	}
+
+	// 更新排序
+	menu.T_sort = requestData.Sort
+	success := Account.Update_Menu(menu, "T_sort")
+	if !success {
+		logs.Error("更新菜单排序失败")
+		c.Data["json"] = lib.JSONS{Code: 500, Msg: "更新菜单排序失败"}
+		c.ServeJSON()
+		return
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "更新排序成功!", Data: r_jsons}
+	c.ServeJSON()
+	return
+}

+ 2 - 0
controllers/User.go

@@ -912,6 +912,7 @@ func (c *UserController) User_Home() {
 		NoticeRecord          NoticeRecord // 通知记录
 
 		CompanyKey            string
+		CompanyId             int
 		CompanyName           string
 		CompanyExpirationTime string
 	}
@@ -929,6 +930,7 @@ func (c *UserController) User_Home() {
 		}
 		info.CompanyKey = Company_r.T_key
 		info.CompanyName = Company_r.T_name
+		info.CompanyId = Company_r.Id
 		info.CompanyExpirationTime = Company_r.T_expirationTime
 	}
 

+ 21 - 0
models/Account/Company.go

@@ -404,6 +404,27 @@ func ReadCompanyIds_T_path(T_path string) (companyIds []int) {
 	return companyIds
 }
 
+// 通过T_path查询所有子公司id,并按公司名称模糊过滤
+func ReadCompanyIds_T_path_ByName(T_path string, T_name string) (companyIds []int) {
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(Company))
+	var CompanyList []Company
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_path__startswith", T_path).And("T_State", 1)
+	if len(T_name) > 0 {
+		cond1 = cond1.And("T_name__icontains", T_name)
+	}
+	_, err := qs.SetCond((*orm2.Condition)(cond1)).All(&CompanyList)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return companyIds
+	}
+	for _, v := range CompanyList {
+		companyIds = append(companyIds, v.Id)
+	}
+	return companyIds
+}
+
 func ReadCompanyWarningIds_T_pids(T_pids []int) []int {
 
 	var companyList []Company

+ 44 - 2
models/Account/Menu.go

@@ -5,13 +5,14 @@ import (
 	"Cold_Api/controllers/lib"
 	"encoding/json"
 	"fmt"
+	"strconv"
+	"time"
+
 	"github.com/astaxie/beego/cache"
 	_ "github.com/astaxie/beego/cache/redis"
 	"github.com/beego/beego/v2/adapter/orm"
 	orm2 "github.com/beego/beego/v2/client/orm"
 	"github.com/beego/beego/v2/core/logs"
-	"strconv"
-	"time"
 )
 
 const (
@@ -165,6 +166,47 @@ func Redis_Menu_Get(Power_Id, T_pid int) (r Menu_Permission, is bool) {
 	return r, false
 }
 
+// Redis_Menu_ClearAll 清空所有菜单缓存
+func Redis_Menu_ClearAll() (err error) {
+	// 由于Redis缓存键的格式是 Power_Id 或 Power_Id-Pid,我们需要遍历所有可能的权限ID
+	// 通过查询Power表获取所有权限ID
+	o := orm.NewOrm()
+	var powers []Power
+	_, err = o.QueryTable(new(Power)).All(&powers)
+	if err != nil {
+		logs.Error("获取权限列表失败:", err)
+		return err
+	}
+
+	// 清除每个权限的菜单缓存
+	for _, power := range powers {
+		Redis_Menu_DelK(power.Id)
+	}
+
+	logs.Info("已清空所有菜单缓存")
+	return nil
+}
+
+// Redis_API_ClearAll 清空所有API缓存
+func Redis_API_ClearAll() (err error) {
+	// 通过查询Power表获取所有权限ID
+	o := orm.NewOrm()
+	var powers []Power
+	_, err = o.QueryTable(new(Power)).All(&powers)
+	if err != nil {
+		logs.Error("获取权限列表失败:", err)
+		return err
+	}
+
+	// 清除每个权限的API缓存
+	for _, power := range powers {
+		Redis_API_DelK(strconv.Itoa(power.Id))
+	}
+
+	logs.Info("已清空所有API缓存")
+	return nil
+}
+
 func Redis_Menu_DelK(Power_Id int) (err error) {
 	err = redisCache_Menu.Delete(strconv.Itoa(Power_Id))
 	if err != nil {

+ 217 - 7
models/Device/DeviceData.go

@@ -374,7 +374,8 @@ func Add_DeviceData(SN string, v DeviceData_T) bool {
 	//	return true
 	//}
 
-	key_time := SN + "|" + strconv.Itoa(v.T_id) + "|" + v.T_time.Format("2006-01-02 15:04:05")
+	// 使用分钟级别的key进行去重判断,忽略秒数差异
+	key_time := SN + "|" + strconv.Itoa(v.T_id) + "|" + v.T_time.Format("2006-01-02 15:04")
 	logs.Debug(key_time)
 	if redis_DeviceData.IsExist(key_time) {
 		//println("找到key:",key)
@@ -386,9 +387,10 @@ func Add_DeviceData(SN string, v DeviceData_T) bool {
 	// 检查 超过时间,查询 数据库
 	logs.Info("Add_DeviceData 时间差s:", time.Now().Unix()-v.T_time.Unix())
 	if time.Now().Unix()-v.T_time.Unix() >= 60*40 {
-		// 查看是否 有记录
+		// 查看是否 有记录 - 按分钟查询,忽略秒数差异
 		var maps_z []orm2.ParamsList
-		sql_c := "SELECT COUNT(ID) FROM z_device_data_" + SN + " WHERE t_time = '" + v.T_time.Format("2006-01-02 15:04:05") + "' AND t_id = " + strconv.Itoa(v.T_id)
+		// 使用 DATE_FORMAT 截断到分钟进行比较
+		sql_c := "SELECT COUNT(*) FROM z_device_data_" + SN + " WHERE DATE_FORMAT(t_time,'%Y-%m-%d %H:%i') = '" + v.T_time.Format("2006-01-02 15:04") + "' AND t_id = " + strconv.Itoa(v.T_id)
 		logs.Info("检查 超过时间,查询 数据库 SQL:", sql_c)
 		_, err := o.Raw(sql_c).ValuesList(&maps_z)
 		if err != nil {
@@ -403,7 +405,7 @@ func Add_DeviceData(SN string, v DeviceData_T) bool {
 
 	// 开始插入数据
 	sql := "INSERT INTO z_device_data_" + SN + " ( `t_id`, `t_t`, `t_rh`, `t_site`, `t_time`) " +
-		"VALUES (" + strconv.Itoa(v.T_id) + ", " + lib.To_string(v.T_t) + ", " + lib.To_string(v.T_rh) + ", '" + v.T_Site + "'," + v.T_time.Format("2006-01-02 15:04:05") + "')"
+		"VALUES (" + strconv.Itoa(v.T_id) + ", " + lib.To_string(v.T_t) + ", " + lib.To_string(v.T_rh) + ", '" + v.T_Site + "', '" + v.T_time.Format("2006-01-02 15:04:05") + "')"
 	//  这里有时间优化  用于一次 prepare 多次 exec,以提高批量执行的速度
 	logs.Debug(sql)
 	res, err := o.Raw(sql).Exec()
@@ -1288,12 +1290,16 @@ func GetNewLocus(T_sn string, T_id int, list *[]DeviceData_) error {
 
 // 去重并追加设备数据
 func appendUniqueDeviceData(list []DeviceData_, newData []DeviceData_) []DeviceData_ {
-	uniqueMap := make(map[time.Time]bool)
+	// 使用分钟级别的时间戳进行去重,忽略秒数差异
+	uniqueMap := make(map[string]bool)
 	for _, d := range list {
-		uniqueMap[d.T_time] = true
+		// 截断到分钟级别
+		timeKey := d.T_time.Format("2006-01-02 15:04")
+		uniqueMap[timeKey] = true
 	}
 	for _, d := range newData {
-		if !uniqueMap[d.T_time] {
+		timeKey := d.T_time.Format("2006-01-02 15:04")
+		if !uniqueMap[timeKey] {
 			if len(d.T_site) > 0 && d.T_site != "0,0" {
 				split := strings.Split(d.T_site, ",")
 				defer func() {
@@ -1308,6 +1314,7 @@ func appendUniqueDeviceData(list []DeviceData_, newData []DeviceData_) []DeviceD
 				mLng, mLat := lib.Wgs84ToGcj02(Lngs, Lats)
 				d.T_site = fmt.Sprintf("%v,%v", mLng, mLat)
 				list = append(list, d)
+				uniqueMap[timeKey] = true // 标记该分钟的数据已添加
 			}
 		}
 	}
@@ -1317,3 +1324,206 @@ func appendUniqueDeviceData(list []DeviceData_, newData []DeviceData_) []DeviceD
 	})
 	return list
 }
+
+// 数据缺失检测结果结构体
+type DeviceDataMissingInfo struct {
+	T_sn           string          // 设备序列号
+	T_id           int             // 传感器ID
+	Time_start     string          // 开始时间
+	Time_end       string          // 结束时间
+	T_saveT        int             // 数据保存间隔(秒)
+	ExpectedCount  int             // 期望的数据条数
+	ActualCount    int             // 实际的数据条数
+	MissingMinutes []string        // 缺失的分钟点(忽略秒)
+	MissingPeriods []MissingPeriod // 缺失时间段列表
+}
+
+// 缺失时间段
+type MissingPeriod struct {
+	StartTime    string // 缺失开始时间
+	EndTime      string // 缺失结束时间
+	Duration     int    // 缺失时长(秒)
+	MissingCount int    // 该时间段缺失的数据条数
+}
+
+// Read_DeviceData_Missing_Check 检测设备数据缺失情况
+func Read_DeviceData_Missing_Check(T_sn string, T_id int, Time_start_ string, Time_end_ string) (DeviceDataMissingInfo, error) {
+	var result DeviceDataMissingInfo
+	result.T_sn = T_sn
+	result.T_id = T_id
+	result.Time_start = Time_start_
+	result.Time_end = Time_end_
+
+	// 1. 获取设备参数,获取 T_saveT
+	deviceParams := Read_DeviceParameter_SN(T_sn)
+	if len(deviceParams) == 0 {
+		return result, errors.New("未找到设备参数")
+	}
+	result.T_saveT = deviceParams[0].T_saveT
+	if result.T_saveT <= 0 {
+		result.T_saveT = 60 // 默认60秒
+	}
+
+	// 2. 解析时间
+	startTime, err := time.Parse("2006-01-02 15:04:05", Time_start_)
+	if err != nil {
+		return result, errors.New("开始时间格式错误")
+	}
+	endTime, err := time.Parse("2006-01-02 15:04:05", Time_end_)
+	if err != nil {
+		return result, errors.New("结束时间格式错误")
+	}
+	if startTime.After(endTime) {
+		return result, errors.New("开始时间不能晚于结束时间")
+	}
+
+	// 3. 计算期望的数据条数
+	duration := int(endTime.Sub(startTime).Seconds())
+	//按区间步长计算并包含起点(例如:0、5、10 分钟应为 3 条)
+	result.ExpectedCount = duration/result.T_saveT + 1
+
+	// 4. 查询实际数据,获取所有时间点
+	o := orm.NewOrm()
+	var timePoints []orm2.ParamsList
+	sql := "SELECT DISTINCT DATE_FORMAT(t_time,'%Y-%m-%d %H:%i:%s') AS t_time FROM z_device_data_" + T_sn +
+		" WHERE t_id = " + strconv.Itoa(T_id) +
+		" AND t_time >= '" + Time_start_ + "'" +
+		" AND t_time <= '" + Time_end_ + "'" +
+		" ORDER BY t_time ASC"
+
+	logs.Debug(sql)
+	_, err = o.Raw(sql).ValuesList(&timePoints)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return result, errors.New("查询数据失败")
+	}
+
+	result.ActualCount = len(timePoints)
+
+	// 4.1 计算缺失的分钟点(逐分钟对齐,忽略秒)
+	// 构建已存在的分钟集合
+	presentMinutes := make(map[string]struct{})
+	for _, tp := range timePoints {
+		if len(tp) > 0 {
+			if timeStr, ok := tp[0].(string); ok {
+				if t, err := time.Parse("2006-01-02 15:04:05", timeStr); err == nil {
+					minute := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, t.Location())
+					presentMinutes[minute.Format("2006-01-02 15:04")] = struct{}{}
+				}
+			}
+		}
+	}
+	// 构建 presentMinutes 已去重,不再需要对 actualMinuteTimes 进行去重收集
+
+	// 基于保存间隔(分钟)生成应有的时间点,再找缺失
+	startMinute := time.Date(startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(), 0, 0, startTime.Location())
+	endMinute := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), endTime.Hour(), endTime.Minute(), 0, 0, endTime.Location())
+	intervalMin := result.T_saveT / 60
+	if intervalMin <= 0 {
+		intervalMin = 1
+	}
+	// 将起点对齐到间隔边界(例如 5 分钟对齐到 00/05/10/...)
+	firstExpected := startMinute
+	if intervalMin > 1 {
+		rem := firstExpected.Minute() % intervalMin
+		if rem != 0 {
+			firstExpected = firstExpected.Add(time.Duration(intervalMin-rem) * time.Minute)
+		}
+	}
+	var missingMinutes []string
+	for t := firstExpected; !t.After(endMinute); t = t.Add(time.Duration(intervalMin) * time.Minute) {
+		mk := t.Format("2006-01-02 15:04")
+		if _, ok := presentMinutes[mk]; !ok {
+			missingMinutes = append(missingMinutes, mk)
+		}
+	}
+	result.MissingMinutes = missingMinutes
+
+	// 5. 计算缺失时间段
+	result.MissingPeriods = calculateMissingPeriods(startTime, endTime, timePoints, result.T_saveT)
+
+	return result, nil
+}
+
+// calculateMissingPeriods 计算缺失时间段
+func calculateMissingPeriods(startTime, endTime time.Time, timePoints []orm2.ParamsList, saveInterval int) []MissingPeriod {
+	var missingPeriods []MissingPeriod
+
+	if len(timePoints) == 0 {
+		// 完全没有数据
+		duration := int(endTime.Sub(startTime).Seconds())
+		missingPeriods = append(missingPeriods, MissingPeriod{
+			StartTime:    startTime.Format("2006-01-02 15:04:05"),
+			EndTime:      endTime.Format("2006-01-02 15:04:05"),
+			Duration:     duration,
+			MissingCount: duration / saveInterval,
+		})
+		return missingPeriods
+	}
+
+	// 解析实际数据时间点
+	var actualTimes []time.Time
+	for _, tp := range timePoints {
+		if len(tp) > 0 {
+			if timeStr, ok := tp[0].(string); ok {
+				if t, err := time.Parse("2006-01-02 15:04:05", timeStr); err == nil {
+					actualTimes = append(actualTimes, t)
+				}
+			}
+		}
+	}
+
+	// 检查开始时间之前是否有缺失
+	if len(actualTimes) > 0 && actualTimes[0].After(startTime) {
+		duration := int(actualTimes[0].Sub(startTime).Seconds())
+		if duration >= saveInterval {
+			missingPeriods = append(missingPeriods, MissingPeriod{
+				StartTime:    startTime.Format("2006-01-02 15:04:05"),
+				EndTime:      actualTimes[0].Format("2006-01-02 15:04:05"),
+				Duration:     duration,
+				MissingCount: duration / saveInterval,
+			})
+		}
+	}
+
+	// 检查中间缺失的时间段
+	for i := 0; i < len(actualTimes)-1; i++ {
+		currentTime := actualTimes[i]
+		nextTime := actualTimes[i+1]
+
+		// 截断到分钟级别进行比较,忽略秒数差异
+		currentMinute := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(),
+			currentTime.Hour(), currentTime.Minute(), 0, 0, currentTime.Location())
+		nextMinute := time.Date(nextTime.Year(), nextTime.Month(), nextTime.Day(),
+			nextTime.Hour(), nextTime.Minute(), 0, 0, nextTime.Location())
+
+		gap := int(nextMinute.Sub(currentMinute).Seconds())
+
+		// 如果时间间隔大于保存间隔的2倍,认为有缺失
+		if gap >= saveInterval*2 {
+			duration := gap - saveInterval // 减去正常间隔
+			missingPeriods = append(missingPeriods, MissingPeriod{
+				StartTime:    currentMinute.Add(time.Duration(saveInterval) * time.Second).Format("2006-01-02 15:04:05"),
+				EndTime:      nextMinute.Format("2006-01-02 15:04:05"),
+				Duration:     duration,
+				MissingCount: duration / saveInterval,
+			})
+		}
+	}
+
+	// 检查结束时间之前是否有缺失
+	if len(actualTimes) > 0 && actualTimes[len(actualTimes)-1].Before(endTime) {
+		lastTime := actualTimes[len(actualTimes)-1]
+		duration := int(endTime.Sub(lastTime).Seconds())
+		if duration >= saveInterval {
+			missingPeriods = append(missingPeriods, MissingPeriod{
+				StartTime:    lastTime.Add(time.Duration(saveInterval) * time.Second).Format("2006-01-02 15:04:05"),
+				EndTime:      endTime.Format("2006-01-02 15:04:05"),
+				Duration:     duration,
+				MissingCount: duration / saveInterval,
+			})
+		}
+	}
+
+	return missingPeriods
+}

+ 36 - 224
models/Device/DeviceSensor.go

@@ -1212,22 +1212,33 @@ func Read_DeviceSensor_List_For_Data(bindSN []string, T_pid int, T_name string,
 		var DeviceSensor_unusual []DeviceSensor_R
 		var DeviceSensor_monitor []DeviceSensor_R
 		var DeviceSensor_monitor2 []DeviceSensor_R
+		var DeviceSensor_not_online []DeviceSensor_R
 		for _, v := range r {
 			v_r := DeviceSensorToDeviceSensor_R(v)
 			data := v_r.T_DeviceSensorData
 			// 状态在监控中,温度或者湿度不在正常区间,排在最前面
-			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v.T_link == 1 && v_r.T_DeviceSensorParameter.T_free == 0 {
+			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v.T_link == 1 &&
+				v_r.T_DeviceSensorParameter.T_free == 0 && (v.T_online == 1 || v.T_online_s == 1) {
 				DeviceSensor_unusual = append(DeviceSensor_unusual, v_r)
-			} else if v.T_monitor == 1 && v.T_link == 1 && v_r.T_DeviceSensorParameter.T_free == 0 {
+
+			} else if v.T_monitor == 1 && v.T_link == 1 && v_r.T_DeviceSensorParameter.T_free == 0 && (v.T_online == 1 || v.T_online_s == 1) {
+				// 监控中,不是空库排第二
 				DeviceSensor_monitor = append(DeviceSensor_monitor, v_r)
 			} else if v.T_monitor == 1 {
-				DeviceSensor_monitor2 = append(DeviceSensor_monitor2, v_r)
+				// 状态在监控中,但是不在线,排在第四
+				if v.T_online == 0 && v.T_online_s == 0 {
+					DeviceSensor_not_online = append(DeviceSensor_not_online, v_r)
+				} else {
+					// 其他监控中状态排第三
+					DeviceSensor_monitor2 = append(DeviceSensor_monitor2, v_r)
+				}
 			} else {
 				DeviceSensor_r = append(DeviceSensor_r, v_r)
 			}
 		}
 		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_monitor...)
 		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_monitor2...)
+		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_not_online...)
 		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_r...)
 		return DeviceSensor_unusual[offset:offset_z], cnt
 	}
@@ -1244,114 +1255,6 @@ func Read_DeviceSensor_List_For_Data(bindSN []string, T_pid int, T_name string,
 	return DeviceSensor_r, cnt
 }
 
-// 与Read_DeviceSensor_List_For_Data功能一样(优化前)
-func Read_DeviceSensor_List_For_Data1(bindSN []string, T_pid int, T_name string, T_Class_id, T_type, T_RealTime, T_MapShow int,
-	page int, page_z int) (DeviceSensor_r []DeviceSensor_R, cnt int64) {
-
-	o := orm.NewOrm()
-	var maps_z []orm2.ParamsList
-
-	// 也可以直接使用 Model 结构体作为表名
-
-	var offset int
-	if page <= 1 {
-		offset = 0
-	} else {
-		offset = (page - 1) * page_z
-	}
-
-	var r []DeviceSensor_RealTime
-	sql_WHERE := "t_pid = " + strconv.Itoa(T_pid)
-	if len(bindSN) > 0 {
-		sql_WHERE += fmt.Sprintf(" AND t_sn in (%s) ", lib.StringListToDotStr(bindSN))
-
-	}
-
-	if T_Class_id > 0 {
-		T_calss := "C" + strconv.Itoa(T_Class_id) + "|"
-		sql_WHERE += " AND t__class like '%" + T_calss + "%'"
-	}
-
-	if T_type > 0 {
-		sql_WHERE += " AND t_type = " + strconv.Itoa(T_type)
-	}
-
-	if len(T_name) >= 16 {
-		sql_WHERE += " AND t_sn = '" + T_name + "'"
-	} else if len(T_name) > 0 && len(T_name) < 16 {
-		sql_WHERE += " AND (ds.t_sn like '%" + T_name + "%' OR ds.t_name like '%" + T_name + "%') AND ds.t__state = 1 AND t_datashow = 1"
-
-	} else {
-		sql_WHERE += " AND ds.t__state = 1 AND t_datashow = 1"
-	}
-
-	if T_MapShow == 1 {
-		sql_WHERE += " AND t__site is not null"
-	}
-	// -------------
-	sql := "SELECT COUNT(*) FROM " + "device_sensor ds WHERE " + sql_WHERE
-	logs.Debug(sql)
-	_, err := o.Raw(sql).ValuesList(&maps_z)
-	if err != nil {
-		logs.Error(lib.FuncName(), err)
-		return DeviceSensor_r, 0
-	}
-	if len(maps_z) == 0 {
-		return DeviceSensor_r, 0
-	}
-	cnt, _ = strconv.ParseInt(maps_z[0][0].(string), 10, 64)
-
-	sql = "SELECT *,ds.t_sn as t_sn,ds.t_id as t_id,ds.t_name as t_name,ds.t__state as t__state FROM device_sensor ds " +
-		"LEFT JOIN device_sensor_parameter dsp " +
-		"ON ds.t_sp=dsp.id " +
-		"WHERE " + sql_WHERE + " ORDER BY ds.t_sort, ds.id  ASC"
-	realTimeSql := sql
-	if page_z != 9999 {
-		sql = sql + " LIMIT " + strconv.Itoa(offset) + "," + strconv.Itoa(page_z)
-	}
-
-	logs.Debug(sql)
-	_, err = o.Raw(sql).QueryRows(&r)
-	if err != nil {
-		logs.Error(lib.FuncName(), err)
-		return
-	}
-
-	if T_RealTime == 1 {
-		offset_z := offset + page_z
-		if cnt < int64(offset_z) {
-			offset_z = int(cnt)
-		}
-		if offset > offset_z {
-			offset = offset_z
-		}
-		_, err = o.Raw(realTimeSql).QueryRows(&r)
-		if err != nil {
-			logs.Error(lib.FuncName(), err)
-			return
-		}
-		var DeviceSensor_unusual []DeviceSensor_R
-		for _, v := range r {
-			v_r := DeviceSensorRealTimeToDeviceSensor_R(v)
-			data := v_r.T_DeviceSensorData
-			// 温度或者湿度不在正常区间,排在最前面
-			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v_r.T_DeviceSensorParameter.T_free == 0 {
-				DeviceSensor_unusual = append(DeviceSensor_unusual, v_r)
-			} else {
-				DeviceSensor_r = append(DeviceSensor_r, v_r)
-			}
-		}
-		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_r...)
-		return DeviceSensor_unusual[offset:offset_z], cnt
-	}
-
-	for _, v := range r {
-		DeviceSensor_r = append(DeviceSensor_r, DeviceSensorRealTimeToDeviceSensor_R(v))
-	}
-
-	return DeviceSensor_r, cnt
-}
-
 // 公司管理 实时数据
 // 查询当前公司及其下面所有子公司的传感器列表
 func Read_CompanyDeviceSensor_List_For_Data(T_pids []int, T_name string, T_Class_id, T_type, T_RealTime, T_MapShow int, page int, page_z int) (DeviceSensor_r []DeviceSensor_R, cnt int64) {
@@ -1411,19 +1314,38 @@ func Read_CompanyDeviceSensor_List_For_Data(T_pids []int, T_name string, T_Class
 			logs.Error(lib.FuncName(), err)
 			return
 		}
+
 		var DeviceSensor_unusual []DeviceSensor_R
+		var DeviceSensor_monitor []DeviceSensor_R
+		var DeviceSensor_monitor2 []DeviceSensor_R
+		var DeviceSensor_not_online []DeviceSensor_R
 		for _, v := range r {
 			v_r := DeviceSensorToDeviceSensor_R(v)
 			data := v_r.T_DeviceSensorData
-			// 温度或者湿度不在正常区间,排在最前面
-			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v_r.T_DeviceSensorParameter.T_free == 0 {
+			// 状态在监控中,温度或者湿度不在正常区间,排在最前面
+			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v.T_link == 1 &&
+				v_r.T_DeviceSensorParameter.T_free == 0 && (v.T_online == 1 || v.T_online_s == 1) {
 				DeviceSensor_unusual = append(DeviceSensor_unusual, v_r)
+
+			} else if v.T_monitor == 1 && v.T_link == 1 && v_r.T_DeviceSensorParameter.T_free == 0 && (v.T_online == 1 || v.T_online_s == 1) {
+				// 监控中,不是空库排第二
+				DeviceSensor_monitor = append(DeviceSensor_monitor, v_r)
+			} else if v.T_monitor == 1 {
+				// 状态在监控中,但是不在线,排在第四
+				if v.T_online == 0 && v.T_online_s == 0 {
+					DeviceSensor_not_online = append(DeviceSensor_not_online, v_r)
+				} else {
+					// 其他监控中状态排第三
+					DeviceSensor_monitor2 = append(DeviceSensor_monitor2, v_r)
+				}
 			} else {
 				DeviceSensor_r = append(DeviceSensor_r, v_r)
 			}
 		}
+		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_monitor...)
+		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_monitor2...)
+		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_not_online...)
 		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_r...)
-
 		return DeviceSensor_unusual[offset:offset_z], cnt
 	}
 
@@ -1438,116 +1360,6 @@ func Read_CompanyDeviceSensor_List_For_Data(T_pids []int, T_name string, T_Class
 	}
 	return DeviceSensor_r, cnt
 }
-func Read_CompanyDeviceSensor_List_For_Data1(T_pids []int, T_name string, T_Class_id, T_type, T_RealTime, T_MapShow int,
-	page int, page_z int) (DeviceSensor_r []DeviceSensor_R, cnt int64) {
-
-	o := orm.NewOrm()
-	var maps_z []orm2.ParamsList
-
-	// 也可以直接使用 Model 结构体作为表名
-
-	var offset int
-	if page <= 1 {
-		offset = 0
-	} else {
-		offset = (page - 1) * page_z
-	}
-
-	var r []DeviceSensor_RealTime
-	sql_WHERE := ""
-	if len(T_pids) == 1 {
-		sql_WHERE += " AND t_pid = " + strconv.Itoa(T_pids[0])
-	} else if len(T_pids) > 1 {
-		sql_WHERE += fmt.Sprintf(" AND t_pid in (%s) ", lib.IntListToDotStr(T_pids))
-
-	}
-
-	if T_Class_id > 0 {
-		T_calss := "C" + strconv.Itoa(T_Class_id) + "|"
-		sql_WHERE += " AND t__class like '%" + T_calss + "%'"
-	}
-
-	if T_type > 0 {
-		sql_WHERE += " AND t_type = " + strconv.Itoa(T_type)
-	}
-
-	if len(T_name) >= 16 {
-		sql_WHERE += " AND t_sn = '" + T_name + "'"
-	} else if len(T_name) > 0 && len(T_name) < 16 {
-		sql_WHERE += " AND (ds.t_sn like '%" + T_name + "%' OR ds.t_name like '%" + T_name + "%') AND ds.t__state = 1 AND t_datashow = 1"
-
-	} else {
-		sql_WHERE += " AND ds.t__state = 1 AND t_datashow = 1"
-	}
-
-	if T_MapShow == 1 {
-		sql_WHERE += " AND t__site is not null"
-	}
-	sql_WHERE = strings.TrimLeft(sql_WHERE, " AND")
-	// -------------
-	sql := "SELECT COUNT(*) FROM " + "device_sensor ds WHERE " + sql_WHERE
-	logs.Debug(sql)
-	_, err := o.Raw(sql).ValuesList(&maps_z)
-	if err != nil {
-		logs.Error(lib.FuncName(), err)
-		return DeviceSensor_r, 0
-	}
-	if len(maps_z) == 0 {
-		return DeviceSensor_r, 0
-	}
-	cnt, _ = strconv.ParseInt(maps_z[0][0].(string), 10, 64)
-
-	sql = "SELECT *,ds.t_sn as t_sn,ds.t_id as t_id,ds.t_name as t_name,ds.t__state as t__state FROM device_sensor ds " +
-		"LEFT JOIN device_sensor_parameter dsp " +
-		"ON ds.t_sp=dsp.id " +
-		"WHERE " + sql_WHERE + " ORDER BY ds.t_sort, ds.id  ASC"
-	realTimeSql := sql
-	if page_z != 9999 {
-		sql = sql + " LIMIT " + strconv.Itoa(offset) + "," + strconv.Itoa(page_z)
-	}
-
-	logs.Debug(sql)
-	_, err = o.Raw(sql).QueryRows(&r)
-	if err != nil {
-		logs.Error(lib.FuncName(), err)
-		return
-	}
-
-	if T_RealTime == 1 {
-		offset_z := offset + page_z
-		if cnt < int64(offset_z) {
-			offset_z = int(cnt)
-		}
-		if offset > offset_z {
-			offset = offset_z
-		}
-		_, err = o.Raw(realTimeSql).QueryRows(&r)
-		if err != nil {
-			logs.Error(lib.FuncName(), err)
-			return
-		}
-		var DeviceSensor_unusual []DeviceSensor_R
-		for _, v := range r {
-			v_r := DeviceSensorRealTimeToDeviceSensor_R(v)
-			data := v_r.T_DeviceSensorData
-			// 温度或者湿度不在正常区间,排在最前面
-			if (data.T_t > data.T_tu || data.T_t < data.T_tl || data.T_rh > data.T_rhu || data.T_rh < data.T_rhl) && v.T_monitor == 1 && v_r.T_DeviceSensorParameter.T_free == 0 {
-				DeviceSensor_unusual = append(DeviceSensor_unusual, v_r)
-			} else {
-				DeviceSensor_r = append(DeviceSensor_r, v_r)
-			}
-		}
-		DeviceSensor_unusual = append(DeviceSensor_unusual, DeviceSensor_r...)
-		return DeviceSensor_unusual[offset:offset_z], cnt
-	}
-
-	for _, v := range r {
-		DeviceSensor_r = append(DeviceSensor_r, DeviceSensorRealTimeToDeviceSensor_R(v))
-	}
-
-	return DeviceSensor_r, cnt
-}
-
 func Read_CompanyDeviceSensor_List_For_Data_ByKey(T_pids []int, T_sn string) (DeviceSensor_r []DeviceSensor_R, cnt int64) {
 
 	o := orm.NewOrm()

+ 2 - 0
routers/Data.go

@@ -36,6 +36,8 @@ func init() {
 
 			beego.NSRouter("/DeviceSensor_Data_Print", &controllers.DataController{}, "*:DeviceSensor_Data_Print"), // 小程序 打印
 
+			beego.NSRouter("/Device_Data_Missing_Check", &controllers.DataController{}, "*:Device_Data_Missing_Check"), // 检测设备数据缺失情况
+
 			// 执行 SQL
 			beego.NSRouter("/Raw", &controllers.RawSqlController{}, "*:Raw"), // 执行 SQL
 		),

+ 23 - 0
routers/Menu.go

@@ -0,0 +1,23 @@
+package routers
+
+import (
+	"Cold_Api/conf"
+	"Cold_Api/controllers"
+
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+func init() {
+	ns := beego.NewNamespace(conf.Version,
+		//---------------------------------- 菜单管理
+		// 菜单管理
+		//beego.NSRouter("/Menu/List", &controllers.MenuController{}, "*:Menu_List"), // 获取菜单列表(树形结构)
+		beego.NSRouter("/Menu/Get", &controllers.MenuController{}, "*:Menu_Get"),   // 根据ID获取菜单详情
+		beego.NSRouter("/Menu/Add", &controllers.MenuController{}, "*:Menu_Add"),   // 添加菜单
+		beego.NSRouter("/Menu/Edit", &controllers.MenuController{}, "*:Menu_Edit"), // 编辑菜单
+		beego.NSRouter("/Menu/Del", &controllers.MenuController{}, "*:Menu_Del"),   // 删除菜单
+		beego.NSRouter("/Menu/Sort", &controllers.MenuController{}, "*:Menu_Sort"), // 更新菜单排序
+	)
+
+	beego.AddNamespace(ns)
+}