소스 검색

add:文件管理

zoie 1 개월 전
부모
커밋
868a2c9322

+ 1 - 2
ColdVerify_server.go

@@ -1,7 +1,6 @@
 package main
 
 import (
-	"ColdVerify_server/TimeTask"
 	"ColdVerify_server/conf"
 	_ "ColdVerify_server/routers"
 	"fmt"
@@ -49,7 +48,7 @@ func main() {
 	beego.BConfig.RunMode = "dev"                           //  应用的运行模式
 	beego.BConfig.Listen.HTTPPort = HTTPPort_int            //监听端口  本地:8518  线上:8528
 
-	go TimeTask.Init() // 定时任务
+	//go TimeTask.Init() // 定时任务
 
 	beego.Run()
 

+ 183 - 1
controllers/DeviceClass.go

@@ -3,6 +3,7 @@ package controllers
 import (
 	"ColdVerify_server/conf"
 	"ColdVerify_server/lib"
+	"ColdVerify_server/logs"
 	"ColdVerify_server/models/Account"
 	"ColdVerify_server/models/Certificate"
 	"ColdVerify_server/models/Device"
@@ -10,10 +11,11 @@ import (
 	"ColdVerify_server/models/Task"
 	"ColdVerify_server/models/VerifyTemplate"
 	"fmt"
-	beego "github.com/beego/beego/v2/server/web"
 	"math"
 	"strconv"
 	"strings"
+
+	beego "github.com/beego/beego/v2/server/web"
 )
 
 type DeviceClassController struct {
@@ -442,6 +444,9 @@ func (c *DeviceClassController) List_Add() {
 	succesId := []string{}
 	for _, sn_id := range list {
 		T_sn := strings.Split(sn_id, ",")[0]
+		if strings.HasPrefix(T_sn, "03") && strings.HasSuffix(T_sn, "000001") {
+			T_sn = strings.TrimSuffix(strings.TrimPrefix(T_sn, "03"), "000001")
+		}
 		T_id := strings.Split(sn_id, ",")[1]
 		if len(T_sn) == 0 || len(T_id) == 0 {
 			errList = append(errList, sn_id)
@@ -592,6 +597,18 @@ func (c *DeviceClassController) List_Copy() {
 
 	}
 
+	// 自动填写备注
+	err := AutoFillDeviceClassRemark(T_paste_task_id)
+	if err != nil {
+		logs.Error("自动填写设备备注失败", err)
+	}
+
+	// 自动填写布点
+	err = AutoFillDeploy(T_paste_task_id, 0)
+	if err != nil {
+		logs.Error("自动填写布点失败", err)
+	}
+
 	if len(errList) == 0 {
 		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: cnt}
 		c.ServeJSON()
@@ -610,6 +627,171 @@ func (c *DeviceClassController) List_Copy() {
 	return
 }
 
+func (c *DeviceClassController) List_Add_batch() {
+	// 验证登录 User_is, User_r
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	T_class, _ := c.GetInt("T_class")
+
+	// 删除之前的设备列表
+	pasteList, _ := Device.Read_DeviceClassList_OrderList(T_class, "", "", "", 0, 9999)
+	for _, ds := range pasteList {
+		Device.Delete_DeviceClassList_(ds)
+	}
+
+	T_sn_id_list := c.GetString("T_sn_id_list")
+
+	// 根据 | 分割数据
+	items := strings.Split(T_sn_id_list, "|")
+
+	// 创建一个 map 来去重
+	uniqueItems := make(map[string]struct{})
+
+	for _, item := range items {
+		// 如果不为空,则添加到 map 中
+		if item != "" {
+			uniqueItems[item] = struct{}{}
+		}
+	}
+
+	// 将唯一项拼接成字符串
+	result := ""
+
+	for item := range uniqueItems {
+		result += item + "|"
+	}
+
+	// 移除最后的 | 字符
+	if len(result) > 0 {
+		result = result[:len(result)-1]
+	}
+	T_sn_id_list = result
+
+	list := strings.Split(strings.TrimRight(T_sn_id_list, "|"), "|")
+	T_remark := c.GetString("T_remark")
+
+	_, is := Device.Read_DeviceClass_ById(T_class)
+	if !is {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_class 错误"}
+		c.ServeJSON()
+		return
+	}
+
+	var successNum int // 成功数量
+	errList := []string{}
+	errList2 := []string{}
+	succesId := []string{}
+	for _, sn_id := range list {
+		T_sn := strings.Split(sn_id, ",")[0]
+		if strings.HasPrefix(T_sn, "03") && strings.HasSuffix(T_sn, "000001") {
+			T_sn = strings.TrimSuffix(strings.TrimPrefix(T_sn, "03"), "000001")
+		}
+		T_id := strings.Split(sn_id, ",")[1]
+		if len(T_sn) == 0 || len(T_id) == 0 {
+			errList = append(errList, sn_id)
+			continue
+		}
+		// 判断是否已存在sn
+		dc, is := Device.Read_DeviceClassList_T_class_T_sn(T_class, T_sn)
+		// 添加的id和数据库已存在id相同
+		if is && dc.T_id == T_id {
+			successNum += 1
+			continue
+		}
+		// 添加的id和数据库已存在id不同
+		if is && dc.T_id != T_id {
+			_, is = Device.Read_DeviceClassList_T_class_T_id(T_class, T_id)
+			if is {
+				errList2 = append(errList2, sn_id)
+				continue
+			}
+			dc.T_id = T_id
+			dc.T_remark = T_remark
+			if !Device.Update_DeviceClassList(dc, "T_id", "T_remark") {
+				errList = append(errList, sn_id)
+			} else {
+				successNum += 1
+			}
+			continue
+		}
+
+		if !is {
+			_, is = Device.Read_DeviceClassList_T_class_T_id(T_class, T_id)
+			if is {
+				errList2 = append(errList2, sn_id)
+				continue
+			}
+		}
+
+		var pdf Certificate.CertificatePdf
+		//pdfList, _ := Certificate.Read_CertificatePdf_Newest(T_sn)
+		pdfList, _ := Certificate.Read_CertificatePdf_T_layout_no(T_id, "")
+		if len(pdfList) > 0 {
+			pdf = pdfList[0]
+		}
+
+		var_ := Device.DeviceClassList{
+			T_class:          T_class,
+			T_id:             T_id,
+			T_sn:             T_sn,
+			T_failure_time:   pdf.T_failure_time,
+			T_pdf:            pdf.T_pdf,
+			T_Certificate_sn: pdf.T_Certificate_sn,
+			T_remark:         T_remark,
+			T_State:          1,
+		}
+
+		_, is = Device.Add_DeviceClassList(var_)
+		if !is {
+			errList = append(errList, sn_id)
+			continue
+		}
+		successNum += 1
+		succesId = append(succesId, sn_id)
+		System.Add_UserLogs_T(User_r.T_uuid, "分类设备管理", "添加", var_)
+	}
+	// 通过 T_class 查找任务 id
+	task, is := Task.Read_Task_ByT_class(T_class)
+
+	if len(errList) == 0 && len(errList2) == 0 {
+		if is && len(task.T_task_id) > 0 {
+			// 自动填写备注
+			err := AutoFillDeviceClassRemark(task.T_task_id)
+			if err != nil {
+				logs.Error("自动填写设备备注失败", err)
+			}
+
+			// 自动填写布点
+			err = AutoFillDeploy(task.T_task_id, 0)
+			if err != nil {
+				logs.Error("自动填写布点失败", err)
+			}
+		}
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: successNum}
+		c.ServeJSON()
+		return
+	}
+	var errStr string
+	if len(errList2) > 0 {
+		errStr += strings.Join(errList2, ",") + "编号已存在"
+	}
+	if len(errList) > 0 {
+		if len(errStr) > 0 {
+			errStr += ","
+		}
+		errStr += strings.Join(errList, ",") + "添加失败"
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 210, Msg: errStr, Data: successNum}
+	c.ServeJSON()
+	return
+}
+
 // 修改-
 func (c *DeviceClassController) List_Up() {
 	// 验证登录 User_is, User_r

+ 207 - 0
controllers/FileManagement.go

@@ -0,0 +1,207 @@
+package controllers
+
+import (
+	"ColdVerify_server/conf"
+	"ColdVerify_server/lib"
+	"ColdVerify_server/models/Account"
+	FileManagementModel "ColdVerify_server/models/FileManagement"
+	"ColdVerify_server/models/System"
+	"math"
+	"strings"
+
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+type FileManagementController struct {
+	beego.Controller
+}
+
+func (c *FileManagementController) List() {
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	page, _ := c.GetInt("page")
+	if page < 1 {
+		page = 1
+	}
+	pageSize, _ := c.GetInt("page_z")
+	if pageSize < 1 {
+		pageSize = conf.Page_size
+	}
+
+	fileName := strings.TrimSpace(c.GetString("file_name"))
+
+	list, cnt := FileManagementModel.ReadFileManagementList(fileName, page, pageSize)
+	pageCount := math.Ceil(float64(cnt) / float64(pageSize))
+
+	var response lib.R_JSONS
+	response.List = list
+	response.Page = page
+	response.Page_size = int(pageCount)
+	response.Pages = lib.Func_page(int64(page), int64(pageCount))
+	response.Num = int(cnt)
+
+	System.Add_UserLogs_T(User_r.T_uuid, "文件管理", "查询", map[string]interface{}{
+		"file_name": fileName,
+		"page":      page,
+		"page_z":    pageSize,
+	})
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: response}
+	c.ServeJSON()
+}
+
+func (c *FileManagementController) Get() {
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	id, err := c.GetInt("Id")
+	if err != nil || id <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "Id 错误!"}
+		c.ServeJSON()
+		return
+	}
+
+	file, ok := FileManagementModel.ReadFileManagementById(id)
+	if !ok {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "未找到文件信息!"}
+		c.ServeJSON()
+		return
+	}
+
+	System.Add_UserLogs_T(User_r.T_uuid, "文件管理", "详情", map[string]interface{}{
+		"id": id,
+	})
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: FileManagementModel.ToFileManagementR(file)}
+	c.ServeJSON()
+}
+
+func (c *FileManagementController) Add() {
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	fileName := strings.TrimSpace(c.GetString("file_name"))
+	filePath := strings.TrimSpace(c.GetString("file_path"))
+	if len(fileName) == 0 || len(filePath) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "文件名称和文件路径不能为空!"}
+		c.ServeJSON()
+		return
+	}
+
+	file := FileManagementModel.FileManagement{
+		FileName: fileName,
+		FilePath: filePath,
+	}
+
+	id, ok := FileManagementModel.AddFileManagement(file)
+	if !ok {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "添加失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	System.Add_UserLogs_T(User_r.T_uuid, "文件管理", "添加", file)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: id}
+	c.ServeJSON()
+}
+
+func (c *FileManagementController) Up() {
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	id, err := c.GetInt("Id")
+	if err != nil || id <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "Id 错误!"}
+		c.ServeJSON()
+		return
+	}
+
+	file, ok := FileManagementModel.ReadFileManagementById(id)
+	if !ok {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "未找到文件信息!"}
+		c.ServeJSON()
+		return
+	}
+
+	fileName := strings.TrimSpace(c.GetString("file_name"))
+	filePath := strings.TrimSpace(c.GetString("file_path"))
+	updateCols := make([]string, 0, 2)
+
+	if len(fileName) > 0 {
+		file.FileName = fileName
+		updateCols = append(updateCols, "FileName")
+	}
+	if len(filePath) > 0 {
+		file.FilePath = filePath
+		updateCols = append(updateCols, "FilePath")
+	}
+
+	if len(updateCols) == 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "没有可更新的内容!"}
+		c.ServeJSON()
+		return
+	}
+
+	if !FileManagementModel.UpdateFileManagement(file, updateCols...) {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "修改失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	System.Add_UserLogs_T(User_r.T_uuid, "文件管理", "修改", file)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+}
+
+func (c *FileManagementController) Del() {
+	User_r, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	id, err := c.GetInt("Id")
+	if err != nil || id <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "Id 错误!"}
+		c.ServeJSON()
+		return
+	}
+
+	file, ok := FileManagementModel.ReadFileManagementById(id)
+	if !ok {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "未找到文件信息!"}
+		c.ServeJSON()
+		return
+	}
+
+	if !FileManagementModel.DeleteFileManagement(file) {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "删除失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	System.Add_UserLogs_T(User_r.T_uuid, "文件管理", "删除", file)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+}

+ 500 - 1
controllers/Task.go

@@ -7,6 +7,7 @@ import (
 	"ColdVerify_server/lib/wx"
 	"ColdVerify_server/logs"
 	"ColdVerify_server/models/Account"
+	"ColdVerify_server/models/Certificate"
 	"ColdVerify_server/models/Device"
 	"ColdVerify_server/models/InfoCollection"
 	"ColdVerify_server/models/System"
@@ -36,6 +37,343 @@ type TaskController struct {
 	beego.Controller
 }
 
+// 导入Excel创建任务并写入模版数据
+func (c *TaskController) Import_Tasks() {
+	file, _, err := c.GetFile("file")
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "读取上传文件失败!"}
+		c.ServeJSON()
+		return
+	}
+	defer file.Close()
+
+	xlsx, err := excelize.OpenReader(file)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "Excel 文件解析失败!"}
+		c.ServeJSON()
+		return
+	}
+	defer func() {
+		_ = xlsx.Close()
+	}()
+
+	sheetName := c.GetString("sheet")
+	if len(sheetName) == 0 {
+		sheets := xlsx.GetSheetList()
+		if len(sheets) == 0 {
+			c.Data["json"] = lib.JSONS{Code: 203, Msg: "Excel 文件为空!"}
+			c.ServeJSON()
+			return
+		}
+		sheetName = sheets[0]
+	}
+
+	rows, err := xlsx.GetRows(sheetName)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "读取 Excel 内容失败!"}
+		c.ServeJSON()
+		return
+	}
+	if len(rows) < 2 {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "Excel 中缺少数据行!"}
+		c.ServeJSON()
+		return
+	}
+
+	headers := rows[0]
+	successIDs := make([]string, 0)
+	skippedRows := make([]int, 0)
+	failedRows := make([]string, 0)
+
+	for idx := 1; idx < len(rows); idx++ {
+		rowCells := rows[idx]
+		rowData := make(map[string]string, len(headers))
+		nonEmpty := false
+		for colIdx, header := range headers {
+			header = strings.TrimSpace(header)
+			if len(header) == 0 {
+				continue
+			}
+			value := ""
+			if colIdx < len(rowCells) {
+				value = strings.TrimSpace(rowCells[colIdx])
+			}
+			if len(value) > 0 {
+				nonEmpty = true
+			}
+			rowData[header] = value
+		}
+		if !nonEmpty {
+			continue
+		}
+		companyName := strings.TrimSpace(rowData["公司名称"])
+		taskName := strings.TrimSpace(rowData["任务名称"])
+		if len(companyName) == 0 || len(taskName) == 0 {
+			skippedRows = append(skippedRows, idx+1)
+			continue
+		}
+		if rowData["布点图片"] == "" {
+			skippedRows = append(skippedRows, idx+1)
+			continue
+		}
+
+		taskID, err := c.importTaskRow(rowData)
+		if err != nil {
+			failedRows = append(failedRows, fmt.Sprintf("第%d行: %v", idx+1, err))
+			continue
+		}
+		successIDs = append(successIDs, taskID)
+	}
+
+	msg := fmt.Sprintf("导入完成, 成功 %d 条, 跳过 %d 条, 失败 %d 条", len(successIDs), len(skippedRows), len(failedRows))
+	respData := map[string]interface{}{
+		"success_ids":  successIDs,
+		"skipped_rows": skippedRows,
+	}
+	if len(failedRows) > 0 {
+		respData["failed_rows"] = failedRows
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: msg, Data: respData}
+	c.ServeJSON()
+}
+
+func (c *TaskController) importTaskRow(row map[string]string) (string, error) {
+	templateName := strings.TrimSpace(row["模版名称"])
+	lastOpenBracket := strings.LastIndex(templateName, "[")
+	lastCloseBracket := strings.LastIndex(templateName, "]")
+	if lastOpenBracket == -1 || lastCloseBracket == -1 || lastOpenBracket >= lastCloseBracket {
+		return "", fmt.Errorf("模版名称中未找到模版ID")
+	}
+	templateID := templateName[lastOpenBracket+1 : lastCloseBracket]
+	if len(templateID) == 0 {
+		return "", fmt.Errorf("模版ID为空")
+	}
+
+	vt, ok := VerifyTemplate.Read_VerifyTemplate(templateID)
+	if !ok {
+		return "", fmt.Errorf("模版不存在: %s", templateID)
+	}
+
+	taskUID := strings.TrimSpace(row["唯一标识"])
+	if len(taskUID) == 0 {
+		return "", fmt.Errorf("唯一标识缺失")
+	}
+	existingTask, taskExists := Task.Read_Task_ByUid(taskUID)
+
+	companyName := strings.TrimSpace(row["公司名称"])
+	if len(companyName) == 0 {
+		return "", fmt.Errorf("公司名称缺失")
+	}
+
+	cachedUser, found, err := Account.GetCompanyFromCache(companyName)
+	if err != nil {
+		return "", fmt.Errorf("公司数据读取失败: %w", err)
+	}
+	var user Account.User
+	if !found {
+		newUser := Account.User{
+			T_name:    companyName,
+			T_pass:    Account.DefaultCompanyPasswordHash,
+			T_passstr: Account.DefaultCompanyPasswordPlain,
+			T_Show:    1,
+			T_State:   1,
+			T_pid:     0,
+		}
+		newID, ok := Account.Add_User(newUser)
+		if !ok {
+			return "", fmt.Errorf("自动创建公司失败")
+		}
+		if err := Account.UpdateUserPath(int(newID)); err != nil {
+			logs.Error("更新新公司路径失败:", err)
+		}
+		createdUser, is := Account.Read_User_ById(int(newID))
+		if !is {
+			return "", fmt.Errorf("获取新公司信息失败")
+		}
+		user = createdUser
+		Account.UpsertCompanyCache(user)
+	} else {
+		user = cachedUser
+	}
+
+	resolveAdminUUID := func(fieldName, fallback string, strict bool) (string, error) {
+		value, ok := row[fieldName]
+		if !ok || len(strings.TrimSpace(value)) == 0 {
+			return fallback, nil
+		}
+		admin, err := Account.EnsureAdminByName(strings.TrimSpace(value), "")
+		if err != nil {
+			if strict {
+				return "", err
+			}
+			logs.Error(fieldName+" 负责人处理失败:", err)
+			return fallback, nil
+		}
+		return admin.T_uuid, nil
+	}
+
+	taskName := strings.TrimSpace(row["任务名称"])
+	if len(taskName) == 0 {
+		return "", fmt.Errorf("任务名称缺失")
+	}
+	snValue := strings.TrimSpace(row["SN"])
+
+	classPath := ""
+	if vt.T_class > 0 {
+		if cls, err := VerifyTemplate.Read_VerifyTemplateClass_ById(vt.T_class); err == nil {
+			p := cls.T_path
+			p = strings.Trim(p, "/")
+			p = strings.TrimPrefix(p, "0/")
+			classPath = p
+		}
+	}
+
+	schemeFallback := ""
+	collectionFallback := ""
+	reportingFallback := ""
+	deliveryFallback := ""
+	projectFallback := ""
+	categoryFallback := ""
+	deviceTypeFallback := ""
+	verifyTypeFallback := ""
+	if taskExists {
+		schemeFallback = existingTask.T_scheme
+		collectionFallback = existingTask.T_collection
+		reportingFallback = existingTask.T_reporting
+		deliveryFallback = existingTask.T_delivery
+		projectFallback = existingTask.T_project
+		categoryFallback = existingTask.T_category
+		deviceTypeFallback = existingTask.T_device_type
+		verifyTypeFallback = existingTask.T_verify_type
+	}
+
+	schemeUUID, err := resolveAdminUUID("实施方案", schemeFallback, false)
+	if err != nil {
+		return "", fmt.Errorf("实施方案负责人创建失败: %w", err)
+	}
+	collectionUUID, err := resolveAdminUUID("数据采集", collectionFallback, true)
+	if err != nil {
+		return "", fmt.Errorf("数据采集负责人创建失败: %w", err)
+	}
+	reportingUUID, err := resolveAdminUUID("报告编辑", reportingFallback, true)
+	if err != nil {
+		return "", fmt.Errorf("报告编辑负责人创建失败: %w", err)
+	}
+	deliveryUUID, err := resolveAdminUUID("交付审核", deliveryFallback, false)
+	if err != nil {
+		return "", fmt.Errorf("交付审核负责人创建失败: %w", err)
+	}
+	projectUUID, _ := resolveAdminUUID("项目负责人", projectFallback, false)
+
+	categoryValue := strings.TrimSpace(row["类别"])
+	if len(categoryValue) == 0 {
+		categoryValue = categoryFallback
+	}
+
+	deviceTypeValue := strings.TrimSpace(row["设备类型"])
+	if len(deviceTypeValue) == 0 {
+		deviceTypeValue = deviceTypeFallback
+	} else {
+		if mapped, ok := Task.DeviceTypeMap[deviceTypeValue]; ok {
+			deviceTypeValue = mapped
+		} else {
+			deviceTypeValue = deviceTypeValue
+		}
+	}
+
+	verifyTypeValue := strings.TrimSpace(row["验证类型"])
+	if len(verifyTypeValue) == 0 {
+		verifyTypeValue = verifyTypeFallback
+	}
+
+	taskRecord := Task.Task{
+		T_Distributor_id:       user.T_Distributor_id,
+		T_uuid:                 user.T_uuid,
+		T_name:                 taskName,
+		T_VerifyTemplate_id:    templateID,
+		T_VerifyTemplate_class: classPath,
+		T_sn:                   snValue,
+		T_uid:                  taskUID,
+		T_scheme:               schemeUUID,
+		T_collection:           collectionUUID,
+		T_reporting:            reportingUUID,
+		T_delivery:             deliveryUUID,
+		T_project:              projectUUID,
+		T_category:             categoryValue,
+		T_device_type:          deviceTypeValue,
+		T_verify_type:          verifyTypeValue,
+	}
+
+	var taskID string
+	if taskExists {
+		taskRecord.Id = existingTask.Id
+		taskRecord.T_task_id = existingTask.T_task_id
+		taskRecord.T_State = existingTask.T_State
+		if ok := Task.Update_Task(taskRecord, "T_Distributor_id", "T_uuid", "T_name", "T_VerifyTemplate_id", "T_VerifyTemplate_class", "T_sn", "T_uid", "T_scheme", "T_collection", "T_reporting", "T_delivery", "T_project", "T_category", "T_device_type", "T_verify_type"); !ok {
+			return "", fmt.Errorf("更新任务失败")
+		}
+		taskID = existingTask.T_task_id
+	} else {
+		dc := Device.DeviceClass{
+			T_uuid:  user.T_uuid,
+			T_State: 1,
+		}
+		classID, ok := Device.Add_DeviceClass(dc)
+		if !ok {
+			return "", fmt.Errorf("创建分类失败")
+		}
+		taskRecord.T_class = int(classID)
+		taskRecord.T_State = 1
+		taskRecord.T_deadline = time.Now().AddDate(0, 2, 0).Format("2006-01-02")
+		if len(strings.TrimSpace(taskRecord.T_device_type)) > 0 {
+			number, err := Task.GenerateNextT_report_number(taskRecord.T_device_type)
+			if err != nil {
+				return "", fmt.Errorf("生成报告编号失败: %w", err)
+			}
+			taskRecord.T_report_number = number
+		}
+
+		if newID, ok := Task.Add_Task(taskRecord); !ok {
+			return "", fmt.Errorf("创建任务失败")
+		} else {
+			taskID = newID
+		}
+	}
+
+	mapList, _ := VerifyTemplate.Read_VerifyTemplateMap_List(templateID, 0, 0)
+	labelMap := make(map[string]VerifyTemplate.VerifyTemplateMap_R, len(mapList))
+	for _, m := range mapList {
+		labelMap[m.T_name] = m
+	}
+
+	dataList := make([]VerifyTemplate.VerifyTemplateMapData, 0, len(row))
+	for key, val := range row {
+		if vtm, exists := labelMap[key]; exists {
+			dataList = append(dataList, VerifyTemplate.VerifyTemplateMapData{
+				T_source:               vtm.T_source,
+				T_task_id:              taskID,
+				T_VerifyTemplate_id:    templateID,
+				T_VerifyTemplateMap_id: vtm.T_id,
+				T_value:                val,
+				T_Required:             vtm.T_Required,
+				T_Construction:         vtm.T_Construction,
+				T_flow_sort:            vtm.T_flow_sort,
+				T_max_time:             vtm.T_max_time,
+				T_min_time:             vtm.T_min_time,
+				T_start_time:           int64(vtm.T_start_time),
+			})
+		}
+	}
+	if len(dataList) > 0 {
+		if _, ok := VerifyTemplate.AddOrUpdate_VerifyTemplateMapData(dataList); !ok {
+			return "", fmt.Errorf("写入模版数据失败")
+		}
+	}
+
+	return taskID, nil
+}
+
 // 列表 -
 func (c *TaskController) List() {
 	// 验证登录 User_is, User_r
@@ -1135,6 +1473,18 @@ func (c *TaskController) Up() {
 		clos = append(clos, "T_VerifyTemplate_class")
 	}
 	if len(T_VerifyTemplate_id) > 0 {
+		// 保存旧的 T_VerifyTemplate_id
+		old_T_VerifyTemplate_id := r.T_VerifyTemplate_id
+		// 判断旧的模版id与新模版id是否一致,如果不一致则复制旧模板数据到新模板
+		if old_T_VerifyTemplate_id != T_VerifyTemplate_id && len(old_T_VerifyTemplate_id) > 0 {
+			// 创建新任务对象用于复制数据
+			new_task := r
+			new_task.T_VerifyTemplate_id = T_VerifyTemplate_id
+			_, err := CopyMapData(r, new_task, 0)
+			if err != nil {
+				logs.Error("复制旧模板数据到新模板失败", err)
+			}
+		}
 		r.T_VerifyTemplate_id = T_VerifyTemplate_id
 		clos = append(clos, "T_VerifyTemplate_id")
 	}
@@ -3098,7 +3448,7 @@ func (c *TaskController) Copy() {
 		T_name:                 T_name,
 		T_VerifyTemplate_class: r.T_VerifyTemplate_class,
 		T_VerifyTemplate_id:    r.T_VerifyTemplate_id,
-		T_deadline:             r.T_deadline,
+		T_deadline:             time.Now().AddDate(0, 2, 0).Format("2006-01-02"),
 		T_scheme:               r.T_scheme,
 		T_collection:           r.T_collection,
 		T_reporting:            r.T_reporting,
@@ -3130,6 +3480,24 @@ func (c *TaskController) Copy() {
 		c.ServeJSON()
 		return
 	}
+
+	// 复制 DeviceClassList
+	err := CopyDeviceClassList(r, var_)
+	if err != nil {
+		logs.Error("复制设备列表失败", err)
+	}
+	// 自动填写备注
+	err = AutoFillDeviceClassRemark(T_paste_task_id)
+	if err != nil {
+		logs.Error("自动填写设备备注失败", err)
+	}
+
+	// 自动填写布点
+	err = AutoFillDeploy(T_paste_task_id, 0)
+	if err != nil {
+		logs.Error("自动填写布点失败", err)
+	}
+
 	NatsServer.Create_Local_Table(T_paste_task_id)
 	Task.Redis_Task_T_report_number_DelK(var_.T_report_number) // 删除redis内的任务编号
 
@@ -3457,3 +3825,134 @@ func (c *TaskController) SyncPDFWatermark() {
 	c.ServeJSON()
 	return
 }
+
+// 复制设备列表
+func CopyDeviceClassList(copy_task, paste_task Task.Task) error {
+	// 读取源任务的设备列表
+	copyList, _ := Device.Read_DeviceClassList_OrderList(copy_task.T_class, "", "", "", 0, 9999)
+
+	// 遍历源任务的设备列表,为每个设备创建新的 DeviceClassList 并添加到目标任务
+	for _, v := range copyList {
+		// 读取证书信息
+		var pdf Certificate.CertificatePdf
+		pdfList, _ := Certificate.Read_CertificatePdf_T_layout_no(v.T_id, "")
+		if len(pdfList) > 0 {
+			pdf = pdfList[0]
+		}
+
+		var_ := Device.DeviceClassList{
+			T_class:          paste_task.T_class,
+			T_id:             v.T_id,
+			T_sn:             v.T_sn,
+			T_failure_time:   pdf.T_failure_time,
+			T_pdf:            pdf.T_pdf,
+			T_Certificate_sn: pdf.T_Certificate_sn,
+			T_remark:         v.T_remark,
+			T_terminal:       v.T_terminal,
+			T_State:          1,
+		}
+
+		_, is := Device.Add_DeviceClassList(var_)
+		if !is {
+			return fmt.Errorf("添加设备列表失败: %s", v.T_id)
+		}
+	}
+	return nil
+}
+
+// 自动填写设备备注
+func AutoFillDeviceClassRemark(T_task_id string) error {
+	task, is := Task.Read_Task(T_task_id)
+	if !is {
+		return fmt.Errorf("读取任务失败: %s", T_task_id)
+	}
+
+	verifyTemplate, is := VerifyTemplate.Read_VerifyTemplate(task.T_VerifyTemplate_id)
+	if !is {
+		return fmt.Errorf("读取验证模板失败: %s", task.T_VerifyTemplate_id)
+	}
+	verifyTemplate_R := VerifyTemplate.VerifyTemplateToVerifyTemplate_R(verifyTemplate)
+	T_deploy_list := verifyTemplate_R.T_deploy_list
+
+	// 循环查询布点
+	deviceClassRemarkMap := make(map[int][]string)
+	deviceClassList := Device.Read_DeviceClassList_List_id_By_Terminal(task.T_class, false)
+	for _, deploy := range T_deploy_list {
+		if len(deploy.T_scope) > 0 {
+			dcl := FilterByRange(deviceClassList, deploy.T_scope)
+			for _, dc := range dcl {
+				deviceClassRemarkMap[dc.Id] = append(deviceClassRemarkMap[dc.Id], deploy.T_name)
+			}
+		}
+	}
+
+	for _, deviceClass := range deviceClassList {
+		if remark, ok := deviceClassRemarkMap[deviceClass.Id]; ok {
+			deviceClass.T_remark = strings.Join(remark, "|")
+			if !Device.Update_DeviceClassList(deviceClass, "T_remark") {
+				return fmt.Errorf("修改备注失败: %d", deviceClass.Id)
+			}
+		}
+	}
+
+	return nil
+}
+
+// 自动填写布点
+func AutoFillDeploy(T_task_id string, T_source int) error {
+	task, is := Task.Read_Task(T_task_id)
+	if !is {
+		return fmt.Errorf("读取任务失败: %s", T_task_id)
+	}
+
+	verifyTemplate, is := VerifyTemplate.Read_VerifyTemplate(task.T_VerifyTemplate_id)
+	if !is {
+		return fmt.Errorf("读取验证模板失败: %s", task.T_VerifyTemplate_id)
+	}
+	verifyTemplate_R := VerifyTemplate.VerifyTemplateToVerifyTemplate_R(verifyTemplate)
+
+	Map_List := VerifyTemplate.Read_VerifyTemplateMap_List_For_Data(task.T_VerifyTemplate_id, T_source, 0)
+	MapData := VerifyTemplate.Read_VerifyTemplateMapData_List(T_source, T_task_id, task.T_VerifyTemplate_id, Map_List)
+	T_deploy_list := verifyTemplate_R.T_deploy_list
+
+	// 循环查询布点
+	deployMap := make(map[string]string)
+	for _, deploy := range T_deploy_list {
+		deviceClassList := Device.Read_DeviceClassList_List_ByT_remark(task.T_class, deploy.T_name)
+		var snList []string
+		for _, v := range deviceClassList {
+			snList = append(snList, v.T_sn)
+		}
+		deployMap[deploy.T_name] = strings.Join(snList, "|")
+	}
+	MapDataList := make([]VerifyTemplate.VerifyTemplateMapData, 0)
+
+	for _, v := range MapData {
+		if snList, ok := deployMap[v.T_name]; ok {
+			val := VerifyTemplate.VerifyTemplateMapData{
+				T_source:               v.T_source,
+				T_task_id:              task.T_task_id,
+				T_VerifyTemplate_id:    task.T_VerifyTemplate_id,
+				T_VerifyTemplateMap_id: v.T_VerifyTemplateMap_id,
+				T_Required:             v.T_Required,
+				T_Construction:         v.T_Construction,
+				T_flow_sort:            v.T_flow_sort,
+				T_max_time:             v.T_max_time,
+				T_min_time:             v.T_min_time,
+
+				T_value:      snList,
+				T_start_time: v.T_start_time,
+			}
+			MapDataList = append(MapDataList, val)
+		}
+	}
+
+	if len(MapDataList) > 0 {
+		_, is := VerifyTemplate.AddOrUpdate_VerifyTemplateMapData_ADD_History(MapDataList, T_source, "", 0, 0, 0)
+		if !is {
+			return fmt.Errorf("保存布点数据失败")
+		}
+	}
+
+	return nil
+}

+ 141 - 5
controllers/VerifyTemplate.go

@@ -1423,9 +1423,10 @@ func (c *VerifyTemplateController) Export_Labels() {
 
 	// 收集所有模板
 	type tpl struct {
-		Id        string
-		Name      string
-		ClassName string
+		Id         string
+		Name       string
+		TemplateId string
+		ClassName  string
 	}
 	var tplList []tpl
 	var collectTpl func(list []VerifyTemplate.VerifyTemplateClass)
@@ -1434,7 +1435,7 @@ func (c *VerifyTemplateController) Export_Labels() {
 			// 当前分类下的模板
 			vlist, _ := VerifyTemplate.Read_VerifyTemplate_List(cls.Id, "", 0, 9999)
 			for _, v := range vlist {
-				tplList = append(tplList, tpl{Id: v.T_VerifyTemplate_id, Name: v.T_name, ClassName: cls.T_name})
+				tplList = append(tplList, tpl{Id: v.T_VerifyTemplate_id, Name: v.T_name, TemplateId: v.T_VerifyTemplate_id, ClassName: cls.T_name})
 			}
 			// 递归子分类
 			if len(cls.Children) > 0 {
@@ -1470,7 +1471,7 @@ func (c *VerifyTemplateController) Export_Labels() {
 			line++
 			row := line
 			f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), t.ClassName)
-			f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), t.Name)
+			f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), t.Name+fmt.Sprintf("[%s]", t.TemplateId))
 			continue
 		}
 
@@ -1528,3 +1529,138 @@ func (c *VerifyTemplateController) Export_Labels() {
 	c.ServeJSON()
 	return
 }
+
+// 导出分类和模版名称(带T_VerifyTemplate_id)
+func (c *VerifyTemplateController) Export_Class_Template() {
+	// 验证登录
+	_, User_is := Account.Verification_Admin(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey"))
+	if !User_is {
+		c.Data["json"] = lib.JSONS{Code: 201, Msg: "请重新登录!"}
+		c.ServeJSON()
+		return
+	}
+
+	// 读取所有模板分类
+	classList := VerifyTemplate.Read_VerifyTemplateClass_List()
+
+	// 按分类收集模板,使用map存储:分类名称 -> 模板列表
+	type tpl struct {
+		Name       string
+		TemplateId string
+	}
+	classTemplates := make(map[string][]tpl)
+
+	var collectTpl func(list []VerifyTemplate.VerifyTemplateClass)
+	collectTpl = func(list []VerifyTemplate.VerifyTemplateClass) {
+		for _, cls := range list {
+			// 当前分类下的模板
+			vlist, _ := VerifyTemplate.Read_VerifyTemplate_List(cls.Id, "", 0, 9999)
+			if len(vlist) > 0 {
+				var templates []tpl
+				for _, v := range vlist {
+					templates = append(templates, tpl{
+						Name:       v.T_name,
+						TemplateId: v.T_VerifyTemplate_id,
+					})
+				}
+				classTemplates[cls.T_name] = templates
+			}
+			// 递归子分类
+			if len(cls.Children) > 0 {
+				collectTpl(cls.Children)
+			}
+		}
+	}
+	collectTpl(classList)
+
+	// 创建excel
+	f := excelize.NewFile()
+	sheetName := "Sheet1"
+	idx, _ := f.GetSheetIndex(sheetName)
+
+	f.SetActiveSheet(idx)
+
+	// 找到所有分类中模板数量最多的,作为最大行数
+	maxRows := 1 // 至少有一行表头
+	classNames := make([]string, 0, len(classTemplates))
+	for className, templates := range classTemplates {
+		classNames = append(classNames, className)
+		if len(templates)+1 > maxRows {
+			maxRows = len(templates) + 1
+		}
+	}
+
+	// Excel列名转换函数:1->A, 2->B, ..., 26->Z, 27->AA, 28->AB, ...
+	toExcelColumn := func(n int) string {
+		result := ""
+		for n > 0 {
+			n--
+			result = string(rune('A'+n%26)) + result
+			n /= 26
+		}
+		return result
+	}
+
+	// 第一行写入分类名称
+	for colIndex, className := range classNames {
+		colName := toExcelColumn(colIndex + 1)
+		cell := fmt.Sprintf("%s1", colName)
+		f.SetCellValue(sheetName, cell, className)
+		// 设置列宽
+		f.SetColWidth(sheetName, colName, colName, 40)
+	}
+
+	// 从第二行开始,按列写入每个分类下的模版
+	for row := 2; row <= maxRows; row++ {
+		for colIndex, className := range classNames {
+			templates := classTemplates[className]
+			colName := toExcelColumn(colIndex + 1)
+			cell := fmt.Sprintf("%s%d", colName, row)
+			// 如果该分类还有模版,则写入
+			templateIndex := row - 2
+			if templateIndex < len(templates) {
+				t := templates[templateIndex]
+				f.SetCellValue(sheetName, cell, t.Name+fmt.Sprintf("[%s]", t.TemplateId))
+			}
+		}
+	}
+
+	// 设置居中样式
+	styleCenter, styleErr := f.NewStyle(&excelize.Style{
+		Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center"},
+	})
+	if styleErr != nil {
+		logs.Error("NewStyle Err:", styleErr)
+	} else {
+		lastCol := toExcelColumn(len(classNames))
+		f.SetCellStyle(sheetName, "A1", fmt.Sprintf("%s%d", lastCol, maxRows), styleCenter)
+	}
+
+	// 生成文件
+	lib.Create_Dir("./ofile")
+	timeStr := time.Now().Format("20060102150405")
+	filePath := "ofile/" + timeStr + "_class_template.xlsx"
+	if err := f.SaveAs(filePath); err != nil {
+		logs.Error(lib.FuncName(), err)
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "文件保存失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	// 上传到七牛
+	if !lib.Pload_qiniu(filePath, filePath) {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "oss!"}
+		c.ServeJSON()
+		return
+	}
+
+	// 删除本地文件
+	err := os.Remove(filePath)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: "https://bzdcoldverifyoss.baozhida.cn/" + filePath}
+	c.ServeJSON()
+	return
+}

+ 114 - 1
models/Account/Admin.go

@@ -3,9 +3,15 @@ package Account
 import (
 	"ColdVerify_server/lib"
 	"ColdVerify_server/logs"
+	"fmt"
+	"strings"
+	"sync"
+	"time"
+
 	"github.com/beego/beego/v2/adapter/orm"
 	orm2 "github.com/beego/beego/v2/client/orm"
-	"time"
+	_ "github.com/go-sql-driver/mysql"
+	uuid "github.com/satori/go.uuid"
 )
 
 type Admin struct {
@@ -44,6 +50,103 @@ func init() {
 	orm.RegisterModel(new(Admin))
 
 }
+
+var adminCache = struct {
+	sync.RWMutex
+	data map[string]Admin
+}{}
+
+const (
+	DefaultAdminPassword = "12345678"
+)
+
+func GetAdminFromCache(name string) (Admin, bool, error) {
+	name = strings.TrimSpace(name)
+	if len(name) == 0 {
+		return Admin{}, false, nil
+	}
+
+	adminCache.RLock()
+	cached := adminCache.data
+	adminCache.RUnlock()
+
+	if cached == nil {
+		if err := RefreshAdminCache(); err != nil {
+			return Admin{}, false, err
+		}
+		adminCache.RLock()
+		cached = adminCache.data
+		adminCache.RUnlock()
+	}
+
+	admin, ok := cached[name]
+	return admin, ok, nil
+}
+
+func RefreshAdminCache() error {
+	admins, err := Read_AllActiveAdmins()
+	if err != nil {
+		return err
+	}
+	cache := make(map[string]Admin, len(admins))
+	for _, a := range admins {
+		cache[a.T_name] = a
+	}
+	adminCache.Lock()
+	adminCache.data = cache
+	adminCache.Unlock()
+	return nil
+}
+
+func UpsertAdminCache(admin Admin) {
+	if len(admin.T_name) == 0 {
+		return
+	}
+	adminCache.Lock()
+	if adminCache.data == nil {
+		adminCache.data = make(map[string]Admin)
+	}
+	adminCache.data[admin.T_name] = admin
+	adminCache.Unlock()
+}
+
+func EnsureAdminByName(name, distributorID string) (Admin, error) {
+	name = strings.TrimSpace(name)
+	if len(name) == 0 {
+		return Admin{}, fmt.Errorf("管理员名称为空")
+	}
+
+	if admin, ok, err := GetAdminFromCache(name); err != nil {
+		return Admin{}, err
+	} else if ok {
+		return admin, nil
+	}
+
+	newUUID := uuid.NewV4().String()
+	username := fmt.Sprintf("auto_%s", strings.ReplaceAll(newUUID, "-", "")[:8])
+
+	newAdmin := Admin{
+		T_uuid:           newUUID,
+		T_power:          6,
+		T_name:           name,
+		T_user:           username,
+		T_pass:           DefaultAdminPassword,
+		T_wxname:         name,
+		T_State:          1,
+		T_Distributor_id: distributorID,
+	}
+
+	if _, err := Add_Admin(newAdmin); err != nil {
+		return Admin{}, err
+	}
+
+	if err, persisted := Read_Admin_ByT_uuid(newUUID); err != nil {
+		return Admin{}, err
+	} else {
+		UpsertAdminCache(persisted)
+		return persisted, nil
+	}
+}
 func AdminToAdmin_R(T Admin, powerMap map[int]string) (T_r Admin_R) {
 	T_r.T_uuid = T.T_uuid
 	T_r.T_power = T.T_power
@@ -173,6 +276,16 @@ func Read_Admin_List_ALL() (maps []Admin) {
 	return maps
 }
 
+func Read_AllActiveAdmins() ([]Admin, error) {
+	o := orm.NewOrm()
+	var admins []Admin
+	_, err := o.QueryTable(new(Admin)).Filter("T_State", 1).All(&admins)
+	if err != nil {
+		return nil, err
+	}
+	return admins, nil
+}
+
 // 获取全部列表-包括软删除数据
 func Read_Admin_List_ALL_1() (maps []Admin) {
 

+ 71 - 0
models/Account/User.go

@@ -7,6 +7,7 @@ import (
 	"log"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/beego/beego/v2/adapter/orm"
@@ -55,6 +56,65 @@ func init() {
 
 }
 
+var companyCache = struct {
+	sync.RWMutex
+	data map[string]User
+}{}
+
+const (
+	DefaultCompanyPasswordHash  = "25d55ad283aa400af464c76d713c07ad"
+	DefaultCompanyPasswordPlain = "12345678"
+)
+
+func GetCompanyFromCache(name string) (User, bool, error) {
+	if len(strings.TrimSpace(name)) == 0 {
+		return User{}, false, nil
+	}
+
+	companyCache.RLock()
+	cached := companyCache.data
+	companyCache.RUnlock()
+
+	if cached == nil {
+		if err := RefreshCompanyCache(); err != nil {
+			return User{}, false, err
+		}
+		companyCache.RLock()
+		cached = companyCache.data
+		companyCache.RUnlock()
+	}
+
+	user, ok := cached[name]
+	return user, ok, nil
+}
+
+func RefreshCompanyCache() error {
+	users, err := Read_AllActiveUsers()
+	if err != nil {
+		return err
+	}
+	cache := make(map[string]User, len(users))
+	for _, u := range users {
+		cache[u.T_name] = u
+	}
+	companyCache.Lock()
+	companyCache.data = cache
+	companyCache.Unlock()
+	return nil
+}
+
+func UpsertCompanyCache(user User) {
+	if len(user.T_name) == 0 {
+		return
+	}
+	companyCache.Lock()
+	if companyCache.data == nil {
+		companyCache.data = make(map[string]User)
+	}
+	companyCache.data[user.T_name] = user
+	companyCache.Unlock()
+}
+
 // -------------------------------------------------------------
 func UserToUser_R(T User, distributorMap map[string]string) (T_r User_R) {
 	T_r.Id = T.Id
@@ -276,6 +336,17 @@ func Read_User_List_ALL() (maps []User) {
 	return maps
 }
 
+// 获取全部启用公司列表
+func Read_AllActiveUsers() ([]User, error) {
+	o := orm.NewOrm()
+	var users []User
+	_, err := o.QueryTable(new(User)).Filter("T_State", 1).All(&users)
+	if err != nil {
+		return nil, err
+	}
+	return users, nil
+}
+
 // 获取全部列表-包括软删除数据
 func Read_User_List_ALL_1() (maps []User) {
 

+ 109 - 0
models/FileManagement/FileManagement.go

@@ -0,0 +1,109 @@
+package FileManagement
+
+import (
+	"ColdVerify_server/lib"
+	"ColdVerify_server/logs"
+	"time"
+
+	"github.com/beego/beego/v2/adapter/orm"
+)
+
+type FileManagement struct {
+	Id         int       `orm:"column(ID);size(11);auto;pk"`
+	FileName   string    `orm:"column(file_name);size(256);null"`
+	FilePath   string    `orm:"column(file_path);size(512);null"`
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"`
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`
+}
+
+type FileManagementR struct {
+	Id         int
+	FileName   string
+	FilePath   string
+	CreateTime string
+	UpdateTime string
+}
+
+func (t *FileManagement) TableName() string {
+	return "file_management"
+}
+
+func init() {
+	orm.RegisterModel(new(FileManagement))
+}
+
+func ToFileManagementR(f FileManagement) FileManagementR {
+	return FileManagementR{
+		Id:         f.Id,
+		FileName:   f.FileName,
+		FilePath:   f.FilePath,
+		CreateTime: f.CreateTime.Format("2006-01-02 15:04:05"),
+		UpdateTime: f.UpdateTime.Format("2006-01-02 15:04:05"),
+	}
+}
+
+func AddFileManagement(f FileManagement) (int64, bool) {
+	o := orm.NewOrm()
+	id, err := o.Insert(&f)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return 0, false
+	}
+	return id, true
+}
+
+func UpdateFileManagement(f FileManagement, cols ...string) bool {
+	o := orm.NewOrm()
+	if num, err := o.Update(&f, cols...); err == nil {
+		logs.Println("Number of records updated in database:", num)
+		return true
+	}
+	return false
+}
+
+func DeleteFileManagement(f FileManagement) bool {
+	o := orm.NewOrm()
+	if num, err := o.Delete(&f); err == nil {
+		logs.Println("Number of records deleted in database:", num)
+		return true
+	}
+	return false
+}
+
+func ReadFileManagementById(id int) (FileManagement, bool) {
+	o := orm.NewOrm()
+	f := FileManagement{Id: id}
+	if err := o.Read(&f); err != nil {
+		logs.Error(lib.FuncName(), err)
+		return FileManagement{}, false
+	}
+	return f, true
+}
+
+func ReadFileManagementList(fileName string, page, pageSize int) ([]FileManagementR, int64) {
+	o := orm.NewOrm()
+	var list []FileManagement
+	qs := o.QueryTable(new(FileManagement))
+
+	if page <= 0 {
+		page = 1
+	}
+	if pageSize <= 0 {
+		pageSize = 10
+	}
+	offset := (page - 1) * pageSize
+
+	if len(fileName) > 0 {
+		qs = qs.Filter("file_name__icontains", fileName)
+	}
+
+	qs.Limit(pageSize, offset).OrderBy("-Id").All(&list)
+	cnt, _ := qs.Count()
+
+	result := make([]FileManagementR, 0, len(list))
+	for _, f := range list {
+		result = append(result, ToFileManagementR(f))
+	}
+
+	return result, cnt
+}

+ 35 - 0
models/Task/Task.go

@@ -149,6 +149,18 @@ var (
 	}
 
 	TaskCollectionTimeLimit float64 = 7 * 24 * 60
+
+	DeviceTypeMap = map[string]string{
+		"箱":  "X",
+		"柜":  "G",
+		"车":  "C",
+		"库":  "K",
+		"系统": "XT",
+		"位置": "WZ",
+		"培训": "PX",
+		"巡检": "XJ",
+		"其他": "QT",
+	}
 )
 
 type AuditRecord struct {
@@ -165,6 +177,7 @@ type AuditRecord struct {
 // 模版
 type Task struct {
 	Id                  int    `orm:"column(ID);size(11);auto;pk"`
+	T_uid               string `orm:"size(256);null"`       // 文件导入任务的唯一标识
 	T_Distributor_id    string `orm:"size(256);null"`       // 分销商id
 	T_class             int    `orm:"size(200);default(0)"` // 分类id
 	T_InfoCollection_id string `orm:"size(256);null"`       // 信息采集ID
@@ -856,6 +869,28 @@ func Read_Task(T_task_id string) (r Task, is bool) {
 	return r, true
 }
 
+// 获取 By T_uid
+func Read_Task_ByUid(T_uid string) (r Task, is bool) {
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(Task))
+	err := qs.Filter("T_uid", T_uid).Filter("T_State", 1).One(&r)
+	if err != nil {
+		return r, false
+	}
+	return r, true
+}
+
+// 获取 By T_class
+func Read_Task_ByT_class(T_class int) (r Task, is bool) {
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(Task))
+	err := qs.Filter("T_class", T_class).Filter("T_State", 1).One(&r)
+	if err != nil {
+		return r, false
+	}
+	return r, true
+}
+
 // 添加
 func Add_Task(r Task) (string, bool) {
 	o := orm.NewOrm()

+ 1 - 0
routers/Device.go

@@ -19,6 +19,7 @@ func init() {
 	beego.Router("/DeviceClassList/List", &controllers.DeviceClassController{}, "*:List_List")                                        // 设备分类列表
 	beego.Router("/DeviceClassList/Maximum", &controllers.DeviceClassController{}, "*:List_Maximum")                                  // 设备分类列表
 	beego.Router("/DeviceClassList/Add", &controllers.DeviceClassController{}, "*:List_Add")                                          // 添加设备分类列表
+	beego.Router("/DeviceClassList/Add_batch", &controllers.DeviceClassController{}, "*:List_Add_batch")                              // 添加设备分类列表
 	beego.Router("/DeviceClassList/Up", &controllers.DeviceClassController{}, "*:List_Up")                                            // 编辑设备分类列表
 	beego.Router("/DeviceClassList/Up_terminal", &controllers.DeviceClassController{}, "*:List_Up_terminal")                          // 编辑设备分类列表
 	beego.Router("/DeviceClassList/Del", &controllers.DeviceClassController{}, "*:List_Del")                                          // 删除设备分类列表

+ 15 - 0
routers/FileManagement.go

@@ -0,0 +1,15 @@
+package routers
+
+import (
+	"ColdVerify_server/controllers"
+
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+func init() {
+	beego.Router("/FileManagement/List", &controllers.FileManagementController{}, "*:List")
+	beego.Router("/FileManagement/Get", &controllers.FileManagementController{}, "*:Get")
+	beego.Router("/FileManagement/Add", &controllers.FileManagementController{}, "*:Add")
+	beego.Router("/FileManagement/Up", &controllers.FileManagementController{}, "*:Up")
+	beego.Router("/FileManagement/Del", &controllers.FileManagementController{}, "*:Del")
+}

+ 2 - 0
routers/Task.go

@@ -2,6 +2,7 @@ package routers
 
 import (
 	"ColdVerify_server/controllers"
+
 	beego "github.com/beego/beego/v2/server/web"
 )
 
@@ -34,6 +35,7 @@ func init() {
 	beego.Router("/Task/GenT_report_number", &controllers.TaskController{}, "*:GenT_report_number")                 // 生成报告编号
 	beego.Router("/Task/SyncPDFWatermark", &controllers.TaskController{}, "*:SyncPDFWatermark")                     // 生成报告编号
 	beego.Router("/Task/SaveElectronicSignaturePDF", &controllers.TaskController{}, "*:SaveElectronicSignaturePDF") // 生成报告编号
+	beego.Router("/Task/Import_Tasks", &controllers.TaskController{}, "*:Import_Tasks")                             // 导入任务
 
 	// 日志
 	beego.Router("/TaskLogs/List", &controllers.TaskController{}, "*:Logs_List")

+ 6 - 5
routers/VerifyTemplate.go

@@ -29,11 +29,12 @@ func init() {
 	beego.Router("/VerifyTemplate/Get", &controllers.VerifyTemplateController{}, "*:Get")   // 模版修改
 	beego.Router("/VerifyTemplate/Copy", &controllers.VerifyTemplateController{}, "*:Copy") // 模版复制
 
-	beego.Router("/VerifyTemplate/Map_List", &controllers.VerifyTemplateController{}, "*:Map_List")           // 标签列表
-	beego.Router("/VerifyTemplate/Map_Add", &controllers.VerifyTemplateController{}, "*:Map_Add")             // 标签添加
-	beego.Router("/VerifyTemplate/Map_Up", &controllers.VerifyTemplateController{}, "*:Map_Up")               // 标签修改
-	beego.Router("/VerifyTemplate/Map_Del", &controllers.VerifyTemplateController{}, "*:Map_Del")             // 标签删除
-	beego.Router("/VerifyTemplate/Export_Labels", &controllers.VerifyTemplateController{}, "*:Export_Labels") // 导出标签名称
+	beego.Router("/VerifyTemplate/Map_List", &controllers.VerifyTemplateController{}, "*:Map_List")                           // 标签列表
+	beego.Router("/VerifyTemplate/Map_Add", &controllers.VerifyTemplateController{}, "*:Map_Add")                             // 标签添加
+	beego.Router("/VerifyTemplate/Map_Up", &controllers.VerifyTemplateController{}, "*:Map_Up")                               // 标签修改
+	beego.Router("/VerifyTemplate/Map_Del", &controllers.VerifyTemplateController{}, "*:Map_Del")                             // 标签删除
+	beego.Router("/VerifyTemplate/Export_Labels", &controllers.VerifyTemplateController{}, "*:Export_Labels")                 // 导出标签名称
+	beego.Router("/VerifyTemplate/Export_Class_Template", &controllers.VerifyTemplateController{}, "*:Export_Class_Template") // 导出分类和模版名称
 
 	beego.Router("/VerifyTemplateMapData/List", &controllers.VerifyTemplateController{}, "*:Map_Data_List")                           // 标签数据列表
 	beego.Router("/VerifyTemplateMapData/Pu", &controllers.VerifyTemplateController{}, "*:Map_Data_Pu")                               // 添加标签数据