diff --git a/data.go b/data.go index 843eab1..d11af77 100644 --- a/data.go +++ b/data.go @@ -32,6 +32,20 @@ type Tool_warning struct { Flag int `json:"flag"` } +type devToolata struct { + Series string `json:"series_num"` + Instrument string `json:"instrument_id"` + ProductName string `json:"product_name"` // 产品名称 + WellName string `json:"wellname"` // 井名 + InTime string `json:"in_time"` // 入井时间(可能为null) + OutTime string `json:"out_time"` // 出井时间(可能为null) + Engineer string `json:"engineer"` // 上井工程师(可能为null) + TotalWorkTime string `json:"total_work_time"` // 累计工作时间(可能为null) + CurrentWorkTime string `json:"current_work_time"` // 本次工作时间(可能为null) + ServiceType string `json:"service_type"` // 服务类型(可能为null) + Location string `json:"location"` // 所在地(COALESCE保证非null) +} + type Srr_event struct { Bt string `json:"bt"` Et string `json:"et"` @@ -133,3 +147,49 @@ type Drilling struct { Footage string `json:"Footage"` Next_step string `json:"Next_step"` } + +type WorkHoursRecord struct { + InTime string + OutTime string + Engineer string + ServiceType string +} + +type DevQueryWellResp struct { + WellNames []string `json:"wellNames"` // 井名称(去重) + Series []string `json:"series"` // 系列号(去重) + Instruments []string `json:"instruments"` // 仪器编号(去重) + Locations []string `json:"locations"` // 所在地(去重) + Engineers []string `json:"engineers"` // 仪器工程师(去重) +} + +type WellDetailReqData struct { + Opuser string `json:"opuser"` + OpuserUuid string `json:"opuser_uuid"` + Serial []string `json:"serial"` + Number []string `json:"number"` + Wellname []string `json:"wellname"` + Engineer []string `json:"engineer"` + InTimeStart string `json:"in_time_start"` + InTimeEnd string `json:"in_time_end"` + ServiceType string `json:"serviceType"` + Index int `json:"index"` + Count int `json:"count"` +} + +// 定义结构体(放在函数外面,和 WellDetailReqData 放在一起) +type SeirDetailReqData struct { + Opuser string `json:"opuser"` + OpuserUuid string `json:"opuser_uuid"` + Series []string `json:"series"` + Instrument []string `json:"instrument"` + ProductName []string `json:"productName"` + WellName []string `json:"wellName"` + Engineer []string `json:"engineer"` + Location []string `json:"location"` + ServiceType []string `json:"serviceType"` + StartTime string `json:"startTime"` + EndTime string `json:"endTime"` + Index int `json:"index"` + Count int `json:"count"` +} diff --git a/main.go b/main.go index 4f15943..1d08dc2 100644 --- a/main.go +++ b/main.go @@ -1553,6 +1553,10 @@ func main() { http.HandleFunc("/deescloud/download_ds_file", download_ds_file) http.HandleFunc("/deescloud/delete_ds_file", delete_ds_file) http.HandleFunc("/deescloud/saveLinkData", saveLinkData) + http.HandleFunc("/deescloud/get_dev_list", get_dev_list) + http.HandleFunc("/deescloud/export_Seir_detail", export_Seir_detail) + http.HandleFunc("/deescloud/export_Well_detail", export_Well_detail) + http.HandleFunc("/deescloud/getQueryWell", getQueryWell) //监听服务 fmt.Println("listen server ...") @@ -1788,10 +1792,59 @@ func get_well_ver_timeout(response http.ResponseWriter, request *http.Request) { }*/ + var n1, n2 int + d := strings.Split(v.Ver, ".") + if len(d) == 3 { + t1, _ := strconv.Atoi(d[0]) + t2, _ := strconv.Atoi(d[1]) + t3, _ := strconv.Atoi(d[2]) + if t1 < 100 && t2 < 100 && t3 < 100 { + n1 = t1*10000 + t2*100 + // n1 = t1*10000 + t2*100 + t3 + //ver = fmt.Sprintf("%02d.%02d.%02d", t1, t2, t3) + } + } else if len(d) == 2 { + t1, _ := strconv.Atoi(d[0]) + t2, _ := strconv.Atoi(d[1]) + if t1 < 100 && t2 < 100 { + n1 = t1*100 + t2 + //ver = fmt.Sprintf("00.%02d.%02d", t1, t2) + } + } + + d1 := strings.Split(v.LastVer, ".") + if len(d1) == 3 { + t1, _ := strconv.Atoi(d1[0]) + t2, _ := strconv.Atoi(d1[1]) + t3, _ := strconv.Atoi(d1[2]) + if t1 < 100 && t2 < 100 && t3 < 100 { + n2 = t1*10000 + t2*100 + // n1 = t1*10000 + t2*100 + t3 + //lastVer = fmt.Sprintf("%02d.%02d.%02d", t1, t2, t3) + } + + } else if len(d1) == 2 { + t1, _ := strconv.Atoi(d1[0]) + t2, _ := strconv.Atoi(d1[1]) + if t1 < 100 && t2 < 100 { + n2 = t1*100 + t2 + //lastVer = fmt.Sprintf("00.%02d.%02d", t1, t2) + } + + } + var diff_level int + if n2-n1 > 200 { + diff_level = 1 + } else if n2-n1 > 0 { + diff_level = 2 + } else if n2-n1 < 0 { + diff_level = 3 + } + if strings.Contains(v.Ver, "A") || strings.Contains(v.Ver, "a") || strings.Contains(v.LastVer, "B") || strings.Contains(v.LastVer, "b") { } else { - if v.Ver != "" && v.LastVer != "" && v.Ver < v.LastVer { + if v.Ver != "" && v.LastVer != "" && diff_level == 2 { if !req.If_en { v.Warn_type = fmt.Sprintf(`建议升级到%s版本`, v.LastVer) } else { @@ -5038,7 +5091,7 @@ func leftOneData(wellName string, IsMemory *bool) []VibrationStatData { sql = fmt.Sprintf("SELECT DISTINCT [4303] AS DEEP, [4304] AS TIME, [4305] AS CODE, [4306] AS VALUE, CASE WHEN [4305] in(104,105) THEN CASE WHEN [4306] < 0.5 THEN 0 WHEN [4306] >= 0.5 AND [4306] < 1 THEN 1 WHEN [4306] >= 1 AND [4306] < 2 THEN 2 WHEN [4306] >= 2 AND [4306] < 3 THEN 3 WHEN [4306] >= 3 AND [4306] < 5 THEN 4 WHEN [4306] >= 5 AND [4306] < 8 THEN 5 WHEN [4306] >= 8 AND [4306] < 15 THEN 6 WHEN [4306] >= 15 THEN 7 ELSE NULL END WHEN [4305] = 106 THEN CASE WHEN [4306] < 40 THEN 0 WHEN [4306] >= 40 AND [4306] < 60 THEN 1 WHEN [4306] >= 60 AND [4306] < 80 THEN 2 WHEN [4306] >= 80 AND [4306] < 100 THEN 3 WHEN [4306] >= 100 AND [4306] < 120 THEN 4 WHEN [4306] >= 120 AND [4306] < 150 THEN 5 WHEN [4306] >= 150 THEN 6 ELSE NULL END ELSE NULL END AS LEVEL FROM DECODETABLE WHERE [4305] IN (104, 105, 106) ORDER BY [4304] ASC;") } fmt.Println("执行的查询sql-----------------------------------", sql) - fmt.Sprintf("执行SQL查询: %s", sql) + fmt.Printf("执行SQL查询: %s\n", sql) rows, err := conn.Query(sql) if err != nil { logs.Info("Query Error", err.Error()) @@ -5055,7 +5108,7 @@ func leftOneData(wellName string, IsMemory *bool) []VibrationStatData { fmt.Println("Scan Error", err.Error()) continue } - fmt.Println("Code: %d, Value: %f, Level: %d", data.Code, data.Value, data.Level) + fmt.Printf("Code: %d, Value: %f, Level: %d\n", data.Code, data.Value, data.Level) // 统计各类CODE的数量 switch data.Code { case 104: @@ -6528,252 +6581,445 @@ func get_tool_warning_info(user string, wellname string) (resp []Tool_warning) { return } -// func get_tool_warning_info(user string, wellname string) (resp []Tool_warning) { -// logs.Info("get_tool_warning_infof方法--------------------------------", user) -// ps_info := get_ps_info(user) -// var db string -// wellSql := fmt.Sprintf(`SELECT WELLName from WellInformation where WELLNameSource = '%s'`, wellname) -// wellRow := sqlConn.QueryRow(wellSql) -// wellRow.Scan(&db) -// fmt.Println("db----------", db) +func get_tool_info(wellname string) (resp []devToolata) { + fmt.Println("get_tool_info--------------------------------") -// // 第一个查询:获取仪器组合 -// if strings.HasPrefix(db, "LH2020-") || -// strings.HasPrefix(db, "LH2021-") || -// strings.HasPrefix(db, "LH2022-") || -// strings.HasPrefix(db, "LH2023-") || -// strings.HasPrefix(db, "LH2025-") { -// conn, err := getOdbcConn("Master") -// if err != nil { -// logs.Info("链接master库失败", err.Error()) -// fmt.Println("链接master库失败", err.Error()) -// } -// defer conn.Close() -// cmdSql := fmt.Sprintf("ALTER DATABASE [%s] SET ONLINE", db) -// _, err = conn.Exec(cmdSql) -// } -// conn, err := getOdbcConn(db) + // 解析井名,支持 JSON 数组,兼容单个字符串 + var wellNames []string + if err := json.Unmarshal([]byte(wellname), &wellNames); err != nil { + wellNames = []string{wellname} + } -// if err != nil { -// logs.Info("Connecting Error", err.Error()) -// } -// defer conn.Close() -// sel_sql := fmt.Sprintf(`select DISTINCT [2327], [2328] from REPAIRANDMENT where [2327] IS NOT NULL`) -// rows, err := conn.Query(sel_sql) -// if err != nil { -// fmt.Println("仪器组合查询错误:", err.Error()) -// logs.Info("仪器组合查询错误:", err.Error()) -// return -// } -// defer rows.Close() + if len(wellNames) == 0 { + return []devToolata{} + } -// // 获取仪器组合列表 -// type ToolPair struct { -// Series string -// Instrument string -// } -// var toolPairs []ToolPair + // 构建井名 IN 条件(转义单引号防注入) + var safeNames []string + for _, name := range wellNames { + safe := strings.ReplaceAll(name, "'", "''") + safeNames = append(safeNames, "'"+safe+"'") + } + nameList := strings.Join(safeNames, ", ") -// for rows.Next() { -// var ( -// seriesPtr sql.NullString -// instrumentPtr sql.NullString -// ) + // 总库一次性查询(使用 base_data 作为主表) + // ORDER BY 井名升序,入井时间降序 + detailSql := fmt.Sprintf(` + SELECT + b.product_name, + r.[2327] AS series, + r.[2328] AS instrument, + r.[2321] AS 井名, + w.入井时间, + w.出井时间, + w.上井人员, + r.[2331] AS 累计工作时间, + r.[2340] AS 本次工作时间, + w.服务类型, + CONCAT(e.所在地, + CASE WHEN e.所在地 IS NOT NULL AND e.所在地 != '' + AND e.其他字段 IS NOT NULL AND e.其他字段 != '' + THEN ' ' ELSE '' END, + e.其他字段) AS 所在地 + FROM base_data b + LEFT JOIN ( + SELECT + [2321], [2327], [2328], [2331], [2340], + ROW_NUMBER() OVER (PARTITION BY [2327], [2328] ORDER BY [2323] DESC) AS rn + FROM REPAIRANDMENT + WHERE [2327] IS NOT NULL + AND [2331] IS NOT NULL AND [2331] != '' + AND [2340] IS NOT NULL AND [2340] != '' + AND [2321] IN (%s) + ) r ON b.series = r.[2327] AND b.instrument = r.[2328] AND r.rn = 1 + LEFT JOIN ( + SELECT + [6841] AS 井名, + [6844] AS 服务类型, + [6845] AS 上井人员, + [6848] AS 入井时间, + [6849] AS 出井时间, + ROW_NUMBER() OVER (PARTITION BY [6841] ORDER BY [6848] DESC) AS rn_w + FROM WORKHOURS + WHERE [6841] IN (%s) + ) w ON r.[2321] = w.井名 AND w.rn_w = 1 + LEFT JOIN ( + SELECT + [6800] AS 井名, + [6803] AS 系列号, + [6804] AS 编号, + [6812] AS 所在地, + [6813] AS 其他字段, + ROW_NUMBER() OVER (PARTITION BY [6800], [6803], [6804] + ORDER BY CASE WHEN [6812] IS NOT NULL AND [6812] != '' THEN 0 ELSE 1 END) AS rn_e + FROM EQUIPMENTASSETS + WHERE [6800] IN (%s) + ) e ON r.[2321] = e.井名 AND b.series = e.系列号 AND b.instrument = e.编号 AND e.rn_e = 1 + WHERE r.[2321] IS NOT NULL + ORDER BY 井名 ASC, w.入井时间 DESC + `, nameList, nameList, nameList) -// err = rows.Scan(&seriesPtr, &instrumentPtr) -// if err != nil { -// fmt.Println("仪器组合行扫描错误:", err.Error()) -// logs.Info("仪器组合行扫描错误:", err.Error()) -// continue -// } + fmt.Println("========= 总库查询SQL =========") + fmt.Println(detailSql) + fmt.Println("================================") -// toolPairs = append(toolPairs, ToolPair{ -// Series: seriesPtr.String, -// Instrument: instrumentPtr.String, -// }) -// } + rows, err := sqlConn.Query(detailSql) + if err != nil { + fmt.Println("总库查询失败:", err.Error()) + return []devToolata{} + } + defer rows.Close() -// if err = rows.Err(); err != nil { -// fmt.Println("仪器组合遍历错误:", err.Error()) -// logs.Info("仪器组合遍历错误:", err.Error()) -// } + toolSet := make(map[string]*devToolata) // 用于去重,保留最新入井时间 -// // 第二个查询:获取警告信息 -// warningSql := fmt.Sprintf(`SELECT [time], [err_level], [context] -// FROM [analysis_tool_warning] -// WHERE [wellname] = '%s' -// ORDER BY [time] DESC`, wellname) -// warningRows, err := sqlConn.Query(warningSql) -// if err != nil { -// fmt.Println("警告信息查询错误:", err.Error()) -// logs.Info("警告信息查询错误:", err.Error()) -// return -// } -// defer warningRows.Close() + for rows.Next() { + var ( + productName sql.NullString + series, instrument, wellname sql.NullString + inTime, outTime, engineer sql.NullString + totalWork, currentWork, serviceType sql.NullString + location sql.NullString + ) + err := rows.Scan( + &productName, &series, &instrument, &wellname, + &inTime, &outTime, &engineer, + &totalWork, ¤tWork, &serviceType, + &location, + ) + if err != nil { + logs.Info("行扫描失败:", err.Error()) + continue + } -// // 存储所有警告记录 -// var warnings []Tool_warning -// for warningRows.Next() { -// var w Tool_warning -// var ( -// timePtr sql.NullString -// errLevelPtr sql.NullString -// contextPtr sql.NullString -// ) + key := series.String + "|" + instrument.String + if existing, ok := toolSet[key]; ok { + // 已存在,保留入井时间更晚的记录(字符串比较,要求时间格式可字典序) + if inTime.Valid && (existing.InTime == "" || existing.InTime < inTime.String) { + existing.ProductName = productName.String + existing.WellName = wellname.String + existing.InTime = inTime.String + existing.OutTime = outTime.String + existing.Engineer = engineer.String + existing.TotalWorkTime = totalWork.String + existing.CurrentWorkTime = currentWork.String + existing.ServiceType = serviceType.String + existing.Location = location.String + } + } else { + tool := &devToolata{ + Series: series.String, + Instrument: instrument.String, + ProductName: productName.String, + WellName: wellname.String, + Location: location.String, + InTime: inTime.String, + OutTime: outTime.String, + Engineer: engineer.String, + TotalWorkTime: totalWork.String, + CurrentWorkTime: currentWork.String, + ServiceType: serviceType.String, + } + toolSet[key] = tool + } + } + if err = rows.Err(); err != nil { + fmt.Println("遍历结果集出错:", err.Error()) + } -// err = warningRows.Scan(&timePtr, &errLevelPtr, &contextPtr) -// if err != nil { -// fmt.Println("警告信息行扫描错误:", err.Error()) -// logs.Info("警告信息行扫描错误:", err.Error()) -// continue -// } + // 转为切片 + for _, v := range toolSet { + resp = append(resp, *v) + } -// w.Time = timePtr.String -// w.Err_level = errLevelPtr.String -// w.Context = contextPtr.String -// warnings = append(warnings, w) -// } + // 排序:井名升序,入井时间降序(string 直接比较) + sort.Slice(resp, func(i, j int) bool { + if resp[i].WellName != resp[j].WellName { + return resp[i].WellName < resp[j].WellName + } + // 井名相同,按入井时间降序(最新的在前) + // 空字符串视为最小值,排在最后 + if resp[i].InTime == resp[j].InTime { + return false + } + if resp[i].InTime == "" { + return false + } + if resp[j].InTime == "" { + return true + } + return resp[i].InTime > resp[j].InTime + }) -// if err = warningRows.Err(); err != nil { -// fmt.Println("警告信息遍历错误:", err.Error()) -// logs.Info("警告信息遍历错误:", err.Error()) -// } + if resp == nil { + resp = []devToolata{} + } -// // 处理结果:检查每个仪器组合是否出现在警告记录中 -// var data []Tool_warning -// for _, pair := range toolPairs { -// v := Tool_warning{ -// Series: pair.Series, -// Instrument: pair.Instrument, -// Flag: 0, // 默认flag=0 -// Context: "正常", -// } + // 打印最终返回信息 + jsonBytes, err := json.MarshalIndent(resp, "", " ") + if err != nil { + fmt.Printf("JSON序列化失败,直接打印: %+v\n", resp) + } else { + fmt.Println("最终返回信息:") + fmt.Println(string(jsonBytes)) + } -// // 构建要查找的字符串格式:系列号 + 空格 + 仪器号 -// searchStr := pair.Series + " " + pair.Instrument + return +} -// // 在警告记录中查找匹配的context -// for _, warn := range warnings { -// if strings.Contains(warn.Context, searchStr) { -// // 找到匹配项,使用警告记录的信息 -// v.Time = warn.Time -// v.Err_level = warn.Err_level -// v.Context = warn.Context -// v.Flag = 1 // 设置flag=1 -// // 处理时间格式 -// if len(v.Time) >= 19 { -// originalTime := v.Time -// v.Time = v.Time[:10] + " " + v.Time[11:19] -// fmt.Printf("格式化时间: %s -> %s\n", originalTime, v.Time) +func splitEngineers(engineerStr string) []string { + if engineerStr == "" { + return []string{} + } + // 统一替换为英文逗号 + replacer := strings.NewReplacer( + ",", ",", + "、", ",", + " ", ",", + ";", ",", + ";", ",", + "|", ",", + "/", ",", + " ", ",", // 多个空格 + ) + normalized := replacer.Replace(engineerStr) + parts := strings.Split(normalized, ",") + result := make([]string, 0, len(parts)) + for _, p := range parts { + trimmed := strings.TrimSpace(p) + if trimmed != "" { + result = append(result, trimmed) + } + } + return result +} -// convertedTime := do_time_by_ps(v.Time, ps_info) -// fmt.Printf("转换时间: %s -> %s\n", v.Time, convertedTime) -// v.Time = convertedTime -// } else { -// fmt.Printf("时间格式异常,长度不足19: %s\n", v.Time) -// } -// fmt.Println("ps_info.R1----------", ps_info.R1) -// // 压力单位转换 -// if ps_info.R1 == "2" { -// if strings.Contains(v.Context, "MPa") { -// fmt.Printf("压力单位转换(MPa->psi): %s\n", v.Context) +func get_tool_info_test(wellname []string) (resp []devToolata) { + // 1. 判断 wellname 是否为空 + if len(wellname) == 0 { + fmt.Println("警告:wellname 数组为空") + logs.Info("警告:wellname 数组为空") + return resp + } -// bid := strings.Index(v.Context, "=") -// eid := strings.Index(v.Context, "(") + // 2. 构建 SQL 查询 + var sql string -// if bid != -1 && eid != -1 && bid < eid { -// valueStr := v.Context[bid+1 : eid] -// f, err := strconv.ParseFloat(valueStr, 64) -// if err == nil { -// originalValue := f -// f = 145.038 * f -// f, _ = decimal.NewFromFloat(f).Round(2).Float64() + if len(wellname) == 1 { + sql = fmt.Sprintf(` + SELECT TOP 1 [2327], [2328], [2321], [2331], [2340] + FROM REPAIRANDMENT + WHERE [2321] = '%s' + AND [2327] IS NOT NULL + AND [2328] IS NOT NULL + AND [2331] IS NOT NULL + AND [2340] IS NOT NULL + ORDER BY [2323] DESC + `, wellname[0]) + } else { + // 多个井名 - 子查询 + UNION ALL + for i, name := range wellname { + if i > 0 { + sql += " UNION ALL " + } + sql += fmt.Sprintf(` + SELECT * FROM ( + SELECT TOP 1 [2327], [2328], [2321], [2331], [2340] + FROM REPAIRANDMENT + WHERE [2321] = '%s' + AND [2327] IS NOT NULL + AND [2328] IS NOT NULL + AND [2331] IS NOT NULL + AND [2340] IS NOT NULL + ORDER BY [2323] DESC + ) AS T%d`, name, i+1) + } + } + fmt.Println("执行的 SQL:", sql) -// newContext := v.Context[:bid+1] + fmt.Sprintf("%.02f", f) + v.Context[eid:] -// fmt.Printf("压力值转换: %.2f MPa -> %.2f psi\n", originalValue, f) + rows, err := sqlConn.Query(sql) + if err != nil { + fmt.Println("仪器组合查询错误:", err.Error()) + logs.Info("仪器组合查询错误:", err.Error()) + return + } + defer rows.Close() -// v.Context = strings.ReplaceAll(newContext, "MPa", "psi") -// } else { -// fmt.Printf("解析压力值失败: %s\n", valueStr) -// } -// } else { -// fmt.Println("在内容中未找到有效的压力值位置") -// } -// } -// } + // 3. 处理每条仪器组合记录 + for rows.Next() { + var ( + series string + instrument string + wellName string + totalWorkTime string + currentWorkTime string + ) + err = rows.Scan(&series, &instrument, &wellName, &totalWorkTime, ¤tWorkTime) + if err != nil { + fmt.Println("仪器组合行扫描错误:", err.Error()) + logs.Info("仪器组合行扫描错误:", err.Error()) + continue + } + if series == "" || instrument == "" || wellName == "" { + fmt.Println("跳过包含空值的记录") + continue + } -// // 温度单位转换 -// if ps_info.R2 == "2" { -// if strings.Contains(v.Context, "℃") { -// logs.Info(fmt.Sprintf("温度单位转换(℃->℉): %s", v.Context)) + // 获取仪器名称 + productName := getProductNameBySeries(series) + // 获取所在地 + location := getLocationByWellName(wellName) -// bid := strings.Index(v.Context, "=") -// eid := strings.Index(v.Context, "(") + // 获取该井的所有工时记录(可能多条) + workHoursList := getWorkHoursInfoList(wellName) + if len(workHoursList) == 0 { + // 没有工时记录,生成一条占位记录(所有字段为空) + workHoursList = []WorkHoursRecord{{InTime: "", OutTime: "", Engineer: "", ServiceType: ""}} + } -// if bid != -1 && eid != -1 && bid < eid { -// valueStr := v.Context[bid+1 : eid] -// f, err := strconv.ParseFloat(valueStr, 64) -// if err == nil { -// originalValue := f -// f = 1.8*f + 32 -// f, _ = decimal.NewFromFloat(f).Round(2).Float64() + // 对每条工时记录,分别生成输出(并拆分工程师) + for _, wh := range workHoursList { + // 拆分工程师字段(支持中英文分隔符) + engineers := splitEngineers(wh.Engineer) -// newContext := v.Context[:bid+1] + fmt.Sprintf("%.02f", f) + v.Context[eid:] -// fmt.Printf("温度值转换: %.2f ℃ -> %.2f ℉\n", originalValue, f) + // 基础数据(不含工程师字段,方便复用) + baseTool := devToolata{ + WellName: wellName, + Instrument: instrument, + ProductName: productName, + Series: series, + InTime: formatDateTime(wh.InTime), + OutTime: formatDateTime(wh.OutTime), + CurrentWorkTime: currentWorkTime, + TotalWorkTime: totalWorkTime, + Location: location, + ServiceType: wh.ServiceType, + Engineer: "", // 临时为空 + } -// v.Context = strings.ReplaceAll(newContext, "℃", "℉") -// } else { -// fmt.Printf("解析温度值失败: %s\n", valueStr) -// } -// } else { -// fmt.Println("在内容中未找到有效的温度值位置") -// } -// } -// } + if len(engineers) == 0 { + // 没有工程师,直接添加一条空工程师记录 + baseTool.Engineer = "" + resp = append(resp, baseTool) + } else { + // 每个工程师生成一条独立记录 + for _, eng := range engineers { + newTool := baseTool + newTool.Engineer = eng + resp = append(resp, newTool) + } + } + } + } -// // 找到匹配后跳出循环 -// break -// } -// } + if err = rows.Err(); err != nil { + fmt.Println("仪器组合遍历错误:", err.Error()) + logs.Info("仪器组合遍历错误:", err.Error()) + } -// data = append(data, v) -// } + fmt.Printf("共查询到仪器组合记录(拆分后共 %d 条)\n", len(resp)) + return resp +} -// fmt.Println("连接master库------") +// 根据 Series 查询 product_name +func getProductNameBySeries(series string) string { + if series == "" { + return "" + } -// if strings.HasPrefix(db, "LH2020-") || -// strings.HasPrefix(db, "LH2021-") || -// strings.HasPrefix(db, "LH2022-") || -// strings.HasPrefix(db, "LH2023-") || -// strings.HasPrefix(db, "LH2025-") { -// masterConn, err := getOdbcConn("master") -// if err != nil { -// logs.Info("连接master库失败", err.Error()) -// fmt.Println("连接master库失败", err.Error()) -// } else { -// fmt.Println("连接master库----成功-准备执行关闭命令-") -// defer masterConn.Close() -// cmdSql := "ALTER DATABASE [LH2025-0165] SET OFFLINE WITH ROLLBACK IMMEDIATE" -// _, err = masterConn.Exec(cmdSql) -// if err != nil { -// fmt.Println("SET OFFLINE command execution error:", err.Error()) -// } -// } -// } + query := fmt.Sprintf(`SELECT product_name FROM base_data WHERE series = '%s'`, series) -// fmt.Printf("总共处理 %d 条仪器组合\n", len(data)) -// logs.Info("总共处理 %d 条仪器组合\n", len(data)) -// if len(data) > 0 { -// resp = data -// fmt.Printf("成功返回 %d 条警告信息\n", len(data)) -// logs.Info("成功返回 %d 条警告信息\n", len(data)) -// } else { -// logs.Info("未查询到任何仪器组合") -// resp = []Tool_warning{} -// } + var productName string + err := sqlConn.QueryRow(query).Scan(&productName) + if err != nil { + if err == sql.ErrNoRows { + fmt.Printf("未找到 series=%s 对应的产品名称\n", series) + } else { + fmt.Printf("查询产品名称失败, series=%s, error=%s\n", series, err.Error()) + logs.Info(fmt.Sprintf("查询产品名称失败, series=%s, error=%s", series, err.Error())) + } + return "" + } -// return -// } + return productName +} + +// 从 WORKHOURS 表查询信息 +func getWorkHoursInfoList(wellName string) []WorkHoursRecord { + + if wellName == "" { + return nil + } + // 使用参数化查询防止 SQL 注入 + query := ` + SELECT [6848], [6849], [6845], [6844] + FROM ( + SELECT DISTINCT + [6841], + [6848], [6849], [6845], [6844] + FROM WORKHOURS + WHERE [6841] = @p1 + AND [6848] IS NOT NULL + AND [6849] IS NOT NULL + AND [6845] IS NOT NULL + AND [6844] IS NOT NULL + ) AS t + ORDER BY [6841] DESC, [6848] DESC, [6845], [6849], [6844] DESC + ` + + rows, err := sqlConn.Query(query, wellName) // 参数化查询 + if err != nil { + fmt.Printf("查询 WORKHOURS 表错误, wellName=%s, error=%s\n", wellName, err.Error()) + logs.Info(fmt.Sprintf("查询 WORKHOURS 表错误, wellName=%s, error=%s", wellName, err.Error())) + return nil + } + defer rows.Close() + + var records []WorkHoursRecord + for rows.Next() { + var inTime, outTime, engineer, serviceType string + if err := rows.Scan(&inTime, &outTime, &engineer, &serviceType); err != nil { + fmt.Println("扫描 WORKHOURS 记录错误:", err.Error()) + continue + } + records = append(records, WorkHoursRecord{ + InTime: inTime, + OutTime: outTime, + Engineer: engineer, + ServiceType: serviceType, + }) + } + return records +} + +// 从 EQUIPMENTASSETS 表查询 Location +func getLocationByWellName(wellName string) string { + if wellName == "" { + return "" + } + + query := fmt.Sprintf(` + SELECT TOP 1 [6811] + FROM EQUIPMENTASSETS + WHERE [6800] = '%s' + AND [6811] IS NOT NULL + `, wellName) + + fmt.Printf("查询 EQUIPMENTASSETS 表 SQL: %s\n", query) + + var location string + err := sqlConn.QueryRow(query).Scan(&location) + if err != nil { + if err == sql.ErrNoRows { + fmt.Printf("未找到井名 %s 的 EQUIPMENTASSETS 记录\n", wellName) + } else { + fmt.Printf("查询 EQUIPMENTASSETS 表错误, wellName=%s, error=%s\n", wellName, err.Error()) + logs.Info(fmt.Sprintf("查询 EQUIPMENTASSETS 表错误, wellName=%s, error=%s", wellName, err.Error())) + } + return "" + } + + return location +} func get_tool_warning_info_en(user string, wellname string) (resp []Tool_warning) { ps_info := get_ps_info(user) @@ -14151,7 +14397,6 @@ func export_pdf(opuser string, cmr_info Cmr, data1 []Cmr_input_record_ex, data2 pdf.SetLineWidth(0.1) pdf.Line(margin_left, base, gopdf.PageSizeA4.W-margin_left, base) - //每日录入信息 base += span if base > page_context_H { pdf.SetXY(page_F_x, page_F_y) @@ -33939,6 +34184,799 @@ func saveLinkData(response http.ResponseWriter, request *http.Request) { "cost:", (time.Now().UnixNano()-beginTime)/1e6, "ms") } +// 设备资产管理,设备页签的列表接口。 +func get_dev_list(response http.ResponseWriter, request *http.Request) { + beginTime := time.Now().UnixNano() + fmt.Println("get_dev_list recv req begin", time.Now().Format("2006-01-02 15:04:05")) + + response.Header().Set("Content-Type", "application/json") + + // 读取请求体(支持 JSON 格式参数) + reqdata, err := ioutil.ReadAll(request.Body) + if err != nil { + fmt.Println("读取请求体失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code": 1, "msg": "无效的请求数据"}`)) + return + } + + var req devData + if err := json.Unmarshal(reqdata, &req); err != nil { + fmt.Println("解析JSON失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code": 1, "msg": "无效的JSON数据"}`)) + return + } + + // 分页参数(默认第1页,每页50条) + page := 1 + pageSize := 50 + if req.Index > 0 { + page = req.Index + } + if req.Count > 0 { + pageSize = req.Count + } + + // 转义函数 + escape := func(s string) string { + return strings.ReplaceAll(s, "'", "''") + } + + // 辅助函数:构建 IN 子句 + buildIn := func(values []string, col string) string { + if len(values) == 0 { + return "" + } + quoted := make([]string, len(values)) + for i, v := range values { + quoted[i] = "'" + escape(v) + "'" + } + return fmt.Sprintf("%s IN (%s)", col, strings.Join(quoted, ",")) + } + + // 构建 WHERE 条件 + var whereParts []string + + // 使用 IN 子句 + if parts := buildIn(req.Serial, "series"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(req.Number, "instrument"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(req.WellName, "well_name"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(req.Engineer, "engineer"); parts != "" { + whereParts = append(whereParts, parts) + } + if req.InTimeStart != "" { + whereParts = append(whereParts, fmt.Sprintf("in_time >= '%s'", escape(req.InTimeStart))) + } + if req.InTimeEnd != "" { + whereParts = append(whereParts, fmt.Sprintf("in_time <= '%s'", escape(req.InTimeEnd))) + } + + // 构建 WHERE 子句 + whereSQL := "" + if len(whereParts) > 0 { + whereSQL = " WHERE " + strings.Join(whereParts, " AND ") + } + + // 查询总数 + countSQL := "SELECT COUNT(*) FROM base_worktime_data" + whereSQL + fmt.Println("get_dev_list-sql----", countSQL) + var totalCount int64 + err = sqlConn.QueryRow(countSQL).Scan(&totalCount) + if err != nil { + fmt.Println("get_dev_list查询总数失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code": 1, "msg": "数据库查询失败"}`)) + return + } + + // 查询数据(分页) + offset := (page - 1) * pageSize + dataSQL := fmt.Sprintf(` + SELECT + product_name, + series, + instrument, + well_name, + in_time, + out_time, + engineer, + total_work_time, + current_work_time, + service_type, + location + FROM base_worktime_data%s + ORDER BY in_time DESC + OFFSET %d ROWS FETCH NEXT %d ROWS ONLY + `, whereSQL, offset, pageSize) + + fmt.Println("get_dev_list-sql----", dataSQL) + + rows, err := sqlConn.Query(dataSQL) + if err != nil { + fmt.Println("查询设备列表失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code": 1, "msg": "数据库查询失败"}`)) + return + } + defer rows.Close() + + // 初始化为空切片,而不是 nil + resp := make([]devRespData, 0) + + for rows.Next() { + var d devRespData + var ( + productName, serial, number sql.NullString + wellName, inTimeRaw, outTimeRaw sql.NullString + engineer, totalWorkTime, currentWorkTime sql.NullString + serviceType, location sql.NullString + ) + err := rows.Scan( + &productName, &serial, &number, + &wellName, &inTimeRaw, &outTimeRaw, &engineer, + &totalWorkTime, ¤tWorkTime, &serviceType, + &location, + ) + if err != nil { + fmt.Println("扫描数据失败:", err) + continue + } + + d.ProductName = productName.String + d.Serial = serial.String + d.Number = number.String + d.WellName = wellName.String + d.InTime = formatDateTime(inTimeRaw.String) + d.OutTime = formatDateTime(outTimeRaw.String) + d.Engineer = engineer.String + d.TotalWorkTime = totalWorkTime.String + d.CurrentWorkTime = currentWorkTime.String + d.ServiceType = serviceType.String + d.Location = location.String + + resp = append(resp, d) + } + if err = rows.Err(); err != nil { + fmt.Println("遍历数据失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code": 1, "msg": "数据库查询失败"}`)) + return + } + + // 返回数据(包含总数,便于前端分页) + responseData := map[string]interface{}{ + "code": 0, + "msg": "操作成功", + "data": resp, + "total": totalCount, + } + + jsonResponse, err := json.Marshal(responseData) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code": 1, "msg": "生成响应失败"}`)) + return + } + + response.Write(jsonResponse) + fmt.Println("get_dev_list recv req end", time.Now().Format("2006-01-02 15:04:05"), "cost:", (time.Now().UnixNano()-beginTime)/1e6, "ms") +} + +func export_Seir_detail(response http.ResponseWriter, request *http.Request) { + beginTime := time.Now().UnixNano() + + // 1. 解析 JSON Body 参数 + body, err := ioutil.ReadAll(request.Body) + if err != nil { + fmt.Println("读取请求体失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code":1,"msg":"读取请求参数失败"}`)) + return + } + defer request.Body.Close() + + var reqData SeirDetailReqData + err = json.Unmarshal(body, &reqData) + if err != nil { + fmt.Println("解析JSON失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code":1,"msg":"请求参数格式错误"}`)) + return + } + + // 打印日志验证 + fmt.Println("seriesList--------", reqData.Series) + fmt.Println("instrumentList:", reqData.Instrument) + fmt.Println("productNameList:", reqData.ProductName) + fmt.Println("wellNameList:", reqData.WellName) + fmt.Println("engineerList:", reqData.Engineer) + fmt.Println("locationList:", reqData.Location) + fmt.Println("serviceTypeList:", reqData.ServiceType) + fmt.Println("startTime:", reqData.StartTime) + fmt.Println("endTime:", reqData.EndTime) + + // 数组条件 + seriesList := reqData.Series + instrumentList := reqData.Instrument + productNameList := reqData.ProductName + wellNameList := reqData.WellName + engineerList := reqData.Engineer + locationList := reqData.Location + serviceTypeList := reqData.ServiceType + + // 时间范围 + startTime := reqData.StartTime + endTime := reqData.EndTime + + // 分页 + page := reqData.Index + if page <= 0 { + page = 1 + } + pageSize := reqData.Count + if pageSize <= 0 { + pageSize = 10000 + } + if pageSize > 50000 { + pageSize = 50000 + } + + // 辅助函数:转义单引号(防止SQL注入) + escape := func(s string) string { + return strings.ReplaceAll(s, "'", "''") + } + + // 辅助函数:构建 IN 子句 + buildIn := func(values []string, col string) string { + if len(values) == 0 { + return "" + } + quoted := make([]string, len(values)) + for i, v := range values { + quoted[i] = "'" + escape(v) + "'" + } + return fmt.Sprintf("%s IN (%s)", col, strings.Join(quoted, ",")) + } + + // 动态构建 WHERE 条件 + var whereParts []string + + if parts := buildIn(seriesList, "series"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(instrumentList, "instrument"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(productNameList, "product_name"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(wellNameList, "well_name"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(engineerList, "engineer"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(locationList, "location"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(serviceTypeList, "service_type"); parts != "" { + whereParts = append(whereParts, parts) + } + if startTime != "" { + whereParts = append(whereParts, "in_time >= '"+escape(startTime)+"'") + } + if endTime != "" { + whereParts = append(whereParts, "out_time <= '"+escape(endTime)+"'") + } + + whereClause := "" + if len(whereParts) > 0 { + whereClause = " WHERE " + strings.Join(whereParts, " AND ") + } + + // 2. 查询总记录数 + countSQL := "SELECT COUNT(*) FROM base_worktime_data" + whereClause + fmt.Println("countSQL:", countSQL) + var totalCount int64 + err = sqlConn.QueryRow(countSQL).Scan(&totalCount) + if err != nil { + fmt.Println("查询总数失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询数据失败"}`)) + return + } + + // 3. 查询数据(带分页) + offset := (page - 1) * pageSize + dataSQL := "SELECT series, instrument, product_name, in_time, out_time, service_type, location, total_work_time, current_work_time, well_name, engineer" + + " FROM base_worktime_data" + whereClause + + fmt.Sprintf(" ORDER BY id OFFSET %d ROWS FETCH NEXT %d ROWS ONLY", offset, pageSize) + + fmt.Println("dataSQL:", dataSQL) + rows, err := sqlConn.Query(dataSQL) + if err != nil { + fmt.Println("查询数据失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询数据失败"}`)) + return + } + defer rows.Close() + + var allTools []devToolata + for rows.Next() { + var tool devToolata + err := rows.Scan( + &tool.Series, + &tool.Instrument, + &tool.ProductName, + &tool.InTime, + &tool.OutTime, + &tool.ServiceType, + &tool.Location, + &tool.TotalWorkTime, + &tool.CurrentWorkTime, + &tool.WellName, + &tool.Engineer, + ) + if err != nil { + fmt.Println("扫描数据失败:", err) + continue + } + allTools = append(allTools, tool) + } + + // 4. 生成 Excel 文件 + file := xlsx.NewFile() + sheet, err := file.AddSheet("工具明细") + if err != nil { + fmt.Println("创建sheet失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"生成文件失败"}`)) + return + } + + // 样式 + style := xlsx.NewStyle() + font := *xlsx.NewFont(11, "宋体") + style.Font = font + alignment := xlsx.Alignment{Horizontal: "center", Vertical: "center"} + style.Alignment = alignment + style.ApplyAlignment = true + + // 表头 + headers := []string{ + "序列号", "仪器名称", "编码", "井名称", "仪器入井时间", + "仪器出井时间", "仪器工程师", "本次使用时长", "累计使用时长", "所在地", + } + headerRow := sheet.AddRow() + for _, h := range headers { + cell := headerRow.AddCell() + cell.SetStyle(style) + cell.Value = h + } + + // 数据行 + for _, tool := range allTools { + values := []string{ + tool.Series, + tool.ProductName, + tool.Instrument, + tool.WellName, + formatDateTime(tool.InTime), + formatDateTime(tool.OutTime), + tool.Engineer, + tool.CurrentWorkTime, + tool.TotalWorkTime, + tool.Location, + } + row := sheet.AddRow() + for _, v := range values { + cell := row.AddCell() + cell.SetStyle(style) + cell.Value = v + } + } + + // 5. 保存文件 + err = os.MkdirAll("./authfile", 0755) + if err != nil { + fmt.Println("创建导出目录失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"创建目录失败"}`)) + return + } + + filename := fmt.Sprintf("井上仪器明细清单(仪器)_%s.xlsx", time.Now().Format("20060102150405")) + filePath := fmt.Sprintf("./authfile/%s", filename) + err = file.Save(filePath) + if err != nil { + fmt.Println("保存Excel文件失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"保存文件失败"}`)) + return + } + + // 6. 返回响应 + type exportResp struct { + FileId string `json:"fileId"` + TotalCount int64 `json:"totalCount"` + ExportRows int `json:"exportRows"` + } + resp := exportResp{ + FileId: filename, + TotalCount: totalCount, + ExportRows: len(allTools), + } + jresp, _ := json.Marshal(resp) + response.Header().Set("Content-Type", "application/json") + response.Write(jresp) + + fmt.Println("export_Seir_detail recv req end", time.Now().Format("2006-01-02 15:04:05"), "cost:", (time.Now().UnixNano()-beginTime)/1e6, "ms") +} + +// 格式化 ISO 8601 时间为 "2006-01-02 15:04:05" +func formatDateTime(iso string) string { + if iso == "" { + return "" + } + t, err := time.Parse(time.RFC3339, iso) + if err != nil { + return iso // 解析失败返回原值 + } + return t.Format("2006-01-02 15:04:05") +} + +func export_Well_detail(response http.ResponseWriter, request *http.Request) { + beginTime := time.Now().UnixNano() + + // 1. 解析 JSON Body 参数 + body, err := ioutil.ReadAll(request.Body) + if err != nil { + fmt.Println("读取请求体失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code":1,"msg":"读取请求参数失败"}`)) + return + } + defer request.Body.Close() + + var reqData WellDetailReqData + err = json.Unmarshal(body, &reqData) + if err != nil { + fmt.Println("解析JSON失败:", err) + response.WriteHeader(http.StatusBadRequest) + response.Write([]byte(`{"code":1,"msg":"请求参数格式错误"}`)) + return + } + + // 打印日志验证 + fmt.Println("wellNameList--------", reqData.Wellname) + fmt.Println("serial:", reqData.Serial) + fmt.Println("number:", reqData.Number) + fmt.Println("engineer:", reqData.Engineer) + fmt.Println("in_time_start:", reqData.InTimeStart) + fmt.Println("in_time_end:", reqData.InTimeEnd) + + // 数组条件(从 reqData 中获取) + seriesList := reqData.Serial + instrumentList := reqData.Number + wellNameList := reqData.Wellname + engineerList := reqData.Engineer + serviceType := reqData.ServiceType + + // 时间范围 + startTime := reqData.InTimeStart + endTime := reqData.InTimeEnd + + // 分页(从 reqData 中获取) + page := reqData.Index + if page <= 0 { + page = 1 + } + pageSize := reqData.Count + if pageSize <= 0 { + pageSize = 10000 + } + if pageSize > 50000 { + pageSize = 50000 + } + + // 辅助函数:转义单引号(防止SQL注入) + escape := func(s string) string { + return strings.ReplaceAll(s, "'", "''") + } + + // 辅助函数:构建 IN 子句 + buildIn := func(values []string, col string) string { + if len(values) == 0 { + return "" + } + quoted := make([]string, len(values)) + for i, v := range values { + quoted[i] = "'" + escape(v) + "'" + } + return fmt.Sprintf("%s IN (%s)", col, strings.Join(quoted, ",")) + } + + // 动态构建 WHERE 条件 + var whereParts []string + + if parts := buildIn(seriesList, "series"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(instrumentList, "instrument"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(wellNameList, "well_name"); parts != "" { + whereParts = append(whereParts, parts) + } + if parts := buildIn(engineerList, "engineer"); parts != "" { + whereParts = append(whereParts, parts) + } + if serviceType != "" { + whereParts = append(whereParts, fmt.Sprintf("service_type = '%s'", escape(serviceType))) + } + if startTime != "" { + whereParts = append(whereParts, "in_time >= '"+escape(startTime)+"'") + } + if endTime != "" { + whereParts = append(whereParts, "out_time <= '"+escape(endTime)+"'") + } + + whereClause := "" + if len(whereParts) > 0 { + whereClause = " WHERE " + strings.Join(whereParts, " AND ") + } + + // 2. 查询总记录数 + countSQL := "SELECT COUNT(*) FROM base_worktime_data" + whereClause + fmt.Println("countSQL:", countSQL) + var totalCount int64 + err = sqlConn.QueryRow(countSQL).Scan(&totalCount) + if err != nil { + fmt.Println("查询总数失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询数据失败"}`)) + return + } + + // 3. 查询数据(带分页) + offset := (page - 1) * pageSize + // 注意:ORDER BY 必须存在,这里假设表有 id 列,请根据实际调整 + dataSQL := "SELECT series, instrument, product_name, in_time, out_time, service_type, location, total_work_time, current_work_time, well_name, engineer" + + " FROM base_worktime_data" + whereClause + + fmt.Sprintf(" ORDER BY id OFFSET %d ROWS FETCH NEXT %d ROWS ONLY", offset, pageSize) + + rows, err := sqlConn.Query(dataSQL) + if err != nil { + fmt.Println("查询数据失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询数据失败"}`)) + return + } + defer rows.Close() + + var allTools []devToolata + for rows.Next() { + var tool devToolata + err := rows.Scan( + &tool.Series, + &tool.Instrument, + &tool.ProductName, + &tool.InTime, + &tool.OutTime, + &tool.ServiceType, + &tool.Location, + &tool.TotalWorkTime, + &tool.CurrentWorkTime, + &tool.WellName, + &tool.Engineer, + ) + if err != nil { + fmt.Println("扫描数据失败:", err) + continue + } + allTools = append(allTools, tool) + } + + // 4. 生成 Excel 文件 + file := xlsx.NewFile() + sheet, err := file.AddSheet("工具明细") + if err != nil { + fmt.Println("创建sheet失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"生成文件失败"}`)) + return + } + + // 样式 + style := xlsx.NewStyle() + font := *xlsx.NewFont(11, "宋体") + style.Font = font + alignment := xlsx.Alignment{Horizontal: "center", Vertical: "center"} + style.Alignment = alignment + style.ApplyAlignment = true + + // 表头 + headers := []string{ + "井名称", "序列号", "仪器名称", "编码", "仪器入井时间", + "仪器出井时间", "仪器工程师", "本次使用时长", "累计使用时长", "所在地", + } + headerRow := sheet.AddRow() + for _, h := range headers { + cell := headerRow.AddCell() + cell.SetStyle(style) + cell.Value = h + } + + // 数据行 + for _, tool := range allTools { + values := []string{ + tool.WellName, + tool.Series, + tool.ProductName, + tool.Instrument, + formatDateTime(tool.InTime), + formatDateTime(tool.OutTime), + tool.Engineer, + tool.CurrentWorkTime, + tool.TotalWorkTime, + tool.Location, + } + row := sheet.AddRow() + for _, v := range values { + cell := row.AddCell() + cell.SetStyle(style) + cell.Value = v + } + } + + // 5. 保存文件 + err = os.MkdirAll("./authfile", 0755) + if err != nil { + fmt.Println("创建导出目录失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"创建目录失败"}`)) + return + } + + filename := fmt.Sprintf("井上仪器明细清单(井位)_%s.xlsx", time.Now().Format("20060102150405")) + filePath := fmt.Sprintf("./authfile/%s", filename) + err = file.Save(filePath) + if err != nil { + fmt.Println("保存Excel文件失败:", err) + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"保存文件失败"}`)) + return + } + + // 6. 返回响应 + type exportResp struct { + FileId string `json:"fileId"` + TotalCount int64 `json:"totalCount"` + ExportRows int `json:"exportRows"` + } + resp := exportResp{ + FileId: filename, + TotalCount: totalCount, + ExportRows: len(allTools), + } + jresp, _ := json.Marshal(resp) + response.Header().Set("Content-Type", "application/json") + response.Write(jresp) + + fmt.Println("export_Well_detail recv req end", time.Now().Format("2006-01-02 15:04:05"), "cost:", (time.Now().UnixNano()-beginTime)/1e6, "ms") +} + +func getQueryWell(response http.ResponseWriter, request *http.Request) { + beginTime := time.Now().UnixNano() + response.Header().Set("Content-Type", "application/json") + + // 定义结果结构 + respData := DevQueryWellResp{} + + // 1. 查询井名称 + rows, err := sqlConn.Query(` + SELECT DISTINCT well_name FROM base_worktime_data + WHERE well_name IS NOT NULL AND well_name != '' ORDER BY well_name + `) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询井名称失败"}`)) + return + } + for rows.Next() { + var name string + rows.Scan(&name) + respData.WellNames = append(respData.WellNames, name) + } + rows.Close() + + // 2. 查询系列号 + rows, err = sqlConn.Query(` + SELECT DISTINCT series FROM base_worktime_data + WHERE series IS NOT NULL AND series != '' ORDER BY series + `) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询系列号失败"}`)) + return + } + for rows.Next() { + var s string + rows.Scan(&s) + respData.Series = append(respData.Series, s) + } + rows.Close() + + // 3. 查询仪器编号 + rows, err = sqlConn.Query(` + SELECT DISTINCT instrument FROM base_worktime_data + WHERE instrument IS NOT NULL AND instrument != '' ORDER BY instrument + `) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询编号失败"}`)) + return + } + for rows.Next() { + var inst string + rows.Scan(&inst) + respData.Instruments = append(respData.Instruments, inst) + } + rows.Close() + + // 4. 查询所在地 + rows, err = sqlConn.Query(` + SELECT DISTINCT location FROM base_worktime_data + WHERE location IS NOT NULL AND location != '' ORDER BY location + `) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询所在地失败"}`)) + return + } + for rows.Next() { + var loc string + rows.Scan(&loc) + respData.Locations = append(respData.Locations, loc) + } + rows.Close() + + // 5. 查询工程师 + rows, err = sqlConn.Query(` + SELECT DISTINCT engineer FROM base_worktime_data + WHERE engineer IS NOT NULL AND engineer != '' ORDER BY engineer + `) + if err != nil { + response.WriteHeader(http.StatusInternalServerError) + response.Write([]byte(`{"code":1,"msg":"查询工程师失败"}`)) + return + } + for rows.Next() { + var eng string + rows.Scan(&eng) + respData.Engineers = append(respData.Engineers, eng) + } + rows.Close() + + // 返回 JSON + jsonResp, _ := json.Marshal(map[string]interface{}{ + "code": 0, + "msg": "操作成功", + "data": respData, + }) + response.Write(jsonResp) + + fmt.Println("getQueryWell end, cost:", (time.Now().UnixNano()-beginTime)/1e6, "ms") +} + func addQualityData(response http.ResponseWriter, request *http.Request) { beginTime := time.Now().UnixNano() fmt.Println("addQualityData recv req begin", time.Now().Format("2006-01-02 15:04:05")) @@ -33964,7 +35002,6 @@ func addQualityData(response http.ResponseWriter, request *http.Request) { } // 构建查询语句,检查记录是否存在 - // 安全警告:当前代码存在SQL注入风险,建议使用参数化查询 querySQL := fmt.Sprintf(`SELECT id FROM t_quality_inspection WHERE well_name = '%s' AND series = '%s' AND instrument = '%s'`, diff --git a/req.go b/req.go index 829f423..c7f7f61 100644 --- a/req.go +++ b/req.go @@ -679,6 +679,19 @@ type QualityData struct { Value string `json:"value"` } +type devData struct { + OpUser string `json:"opuser"` // 操作用户 + Opuser_uuid string `json:"opuser_uuid"` // 用户UUID + Serial []string `json:"serial"` // 系列号筛选条件 + Number []string `json:"number"` // 编号筛选条件 + WellName []string `json:"wellname"` // 井名称筛选条件 + Engineer []string `json:"engineer"` // 仪器工程师筛选条件 + InTimeStart string `json:"in_time_start"` // 入井开始时间 + InTimeEnd string `json:"in_time_end"` // 入井结束时间 + Index int `json:"index"` // 当前页码 + Count int `json:"count"` // 每页数量 +} + type LinkDataReq struct { ID string `json:"id"` Instrument string `json:"instrument"` diff --git a/res.go b/res.go index 9d342eb..d37e125 100644 --- a/res.go +++ b/res.go @@ -792,3 +792,23 @@ type uploadRes struct { Types string `json:"type"` RealFileName string `json:"realFileName"` } + +type devRespData struct { + ProductName string `json:"product_name"` // 产品名称 + Serial string `json:"serial"` // 系列号 + Number string `json:"number"` // 编号 + WellName string `json:"wellname"` // 井名 + InTime string `json:"in_time"` // 入井时间(可能为null) + OutTime string `json:"out_time"` // 出井时间(可能为null) + Engineer string `json:"engineer"` // 上井工程师(可能为null) + TotalWorkTime string `json:"total_work_time"` // 累计工作时间(可能为null) + CurrentWorkTime string `json:"current_work_time"` // 本次工作时间(可能为null) + ServiceType string `json:"service_type"` // 服务类型(可能为null) + Location string `json:"location"` // 所在地(COALESCE保证非null) +} + +type toolResult struct { + WellName string `json:"wellname"` + Series string `json:"series_num"` + Instrument string `json:"instrument_id"` +}