feat(platform): query tdengine history data
This commit is contained in:
@@ -1,59 +1,78 @@
|
||||
package platform
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func buildRawFrameSQL(database string, query RawFrameQuery) SQLQuery {
|
||||
table := qualifyTDengine(database, "raw_frames")
|
||||
where := []string{"1 = 1"}
|
||||
table := rawFrameTable(database, query)
|
||||
where := rawFrameWhere(query)
|
||||
parsedFieldsSelect := "'' AS parsed_fields"
|
||||
if query.IncludeFields || len(query.Fields) > 0 {
|
||||
parsedFieldsSelect = "parsed_json AS parsed_fields"
|
||||
}
|
||||
args := []any{}
|
||||
if query.Protocol != "" {
|
||||
where = append(where, "protocol = ?")
|
||||
args = append(args, query.Protocol)
|
||||
}
|
||||
if query.VIN != "" {
|
||||
where = append(where, "vin = ?")
|
||||
args = append(args, query.VIN)
|
||||
}
|
||||
if query.DateFrom != "" {
|
||||
where = append(where, "ts >= ?")
|
||||
args = append(args, query.DateFrom)
|
||||
}
|
||||
if query.DateTo != "" {
|
||||
where = append(where, "ts <= ?")
|
||||
args = append(args, query.DateTo)
|
||||
}
|
||||
limit := query.Limit
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
args = append(args, limit, query.Offset)
|
||||
return SQLQuery{
|
||||
Text: `SELECT ts, frame_id, event_time, received_at, raw_size_bytes, parsed_fields, parse_status, ` +
|
||||
`parse_error, source_endpoint, protocol, vehicle_key, vin, phone FROM ` + table +
|
||||
` WHERE ` + strings.Join(where, " AND ") + ` ORDER BY ts DESC LIMIT ? OFFSET ?`,
|
||||
Args: args,
|
||||
offset := query.Offset
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
text := `SELECT ts, frame_id, event_time, received_at, raw_size_bytes, ` + parsedFieldsSelect +
|
||||
`, parse_status, parse_error, source_endpoint, protocol, vehicle_key, vin, phone FROM ` + table
|
||||
if len(where) > 0 {
|
||||
text += ` WHERE ` + strings.Join(where, " AND ")
|
||||
}
|
||||
text += ` ORDER BY ts DESC LIMIT ` + strconv.Itoa(limit) + ` OFFSET ` + strconv.Itoa(offset)
|
||||
return SQLQuery{Text: text, Args: args}
|
||||
}
|
||||
|
||||
func rawFrameWhere(query RawFrameQuery) []string {
|
||||
where := make([]string, 0, 4)
|
||||
if query.Protocol != "" {
|
||||
where = append(where, "protocol = '"+quoteTDengine(strings.ToUpper(strings.TrimSpace(query.Protocol)))+"'")
|
||||
}
|
||||
if query.VIN != "" {
|
||||
where = append(where, "vin = '"+quoteTDengine(strings.TrimSpace(query.VIN))+"'")
|
||||
}
|
||||
if query.DateFrom != "" {
|
||||
where = append(where, "ts >= '"+quoteTDengine(normalizeTDengineTime(query.DateFrom))+"'")
|
||||
}
|
||||
if query.DateTo != "" {
|
||||
where = append(where, "ts <= '"+quoteTDengine(normalizeTDengineTime(query.DateTo))+"'")
|
||||
}
|
||||
return where
|
||||
}
|
||||
|
||||
func buildHistoryLocationSQL(database string, query map[string]string) SQLQuery {
|
||||
table := qualifyTDengine(database, "vehicle_locations")
|
||||
where := []string{"1 = 1"}
|
||||
table := locationTable(database, query)
|
||||
where := make([]string, 0, 4)
|
||||
args := []any{}
|
||||
if protocol := strings.TrimSpace(query["protocol"]); protocol != "" {
|
||||
where = append(where, "protocol = ?")
|
||||
args = append(args, protocol)
|
||||
where = append(where, "protocol = '"+quoteTDengine(strings.ToUpper(protocol))+"'")
|
||||
}
|
||||
if vin := strings.TrimSpace(query["vin"]); vin != "" {
|
||||
where = append(where, "vin = ?")
|
||||
args = append(args, vin)
|
||||
where = append(where, "vin = '"+quoteTDengine(vin)+"'")
|
||||
}
|
||||
if dateFrom := strings.TrimSpace(query["dateFrom"]); dateFrom != "" {
|
||||
where = append(where, "ts >= '"+quoteTDengine(normalizeTDengineTime(dateFrom))+"'")
|
||||
}
|
||||
if dateTo := strings.TrimSpace(query["dateTo"]); dateTo != "" {
|
||||
where = append(where, "ts <= '"+quoteTDengine(normalizeTDengineTime(dateTo))+"'")
|
||||
}
|
||||
limit, offset := parseLimitOffset(query["limit"], query["offset"])
|
||||
args = append(args, limit, offset)
|
||||
return SQLQuery{
|
||||
Text: `SELECT ts, vin, protocol, longitude, latitude, speed_kmh, total_mileage_km, event_time, received_at FROM ` +
|
||||
table + ` WHERE ` + strings.Join(where, " AND ") + ` ORDER BY ts DESC LIMIT ? OFFSET ?`,
|
||||
Args: args,
|
||||
text := `SELECT ts, vin, protocol, longitude, latitude, speed_kmh, total_mileage_km, received_at FROM ` + table
|
||||
if len(where) > 0 {
|
||||
text += ` WHERE ` + strings.Join(where, " AND ")
|
||||
}
|
||||
text += ` ORDER BY ts DESC LIMIT ` + strconv.Itoa(limit) + ` OFFSET ` + strconv.Itoa(offset)
|
||||
return SQLQuery{Text: text, Args: args}
|
||||
}
|
||||
|
||||
func qualifyTDengine(database, table string) string {
|
||||
@@ -62,3 +81,50 @@ func qualifyTDengine(database, table string) string {
|
||||
}
|
||||
return database + "." + table
|
||||
}
|
||||
|
||||
func rawFrameTable(database string, query RawFrameQuery) string {
|
||||
protocol := strings.ToUpper(strings.TrimSpace(query.Protocol))
|
||||
vin := strings.TrimSpace(query.VIN)
|
||||
if protocol == "" || vin == "" {
|
||||
return qualifyTDengine(database, "raw_frames")
|
||||
}
|
||||
if protocol == "JT808" {
|
||||
return qualifyTDengine(database, "raw_frames")
|
||||
}
|
||||
return qualifyTDengine(database, "raw_"+strings.ToLower(protocol)+"_"+hash16(vin))
|
||||
}
|
||||
|
||||
func locationTable(database string, query map[string]string) string {
|
||||
protocol := strings.ToUpper(strings.TrimSpace(query["protocol"]))
|
||||
vin := strings.TrimSpace(query["vin"])
|
||||
if protocol == "" || vin == "" {
|
||||
return qualifyTDengine(database, "vehicle_locations")
|
||||
}
|
||||
return qualifyTDengine(database, "loc_"+strings.ToLower(protocol)+"_"+hash16(vin))
|
||||
}
|
||||
|
||||
func hash16(value string) string {
|
||||
sum := sha1.Sum([]byte(value))
|
||||
return hex.EncodeToString(sum[:8])
|
||||
}
|
||||
|
||||
func quoteTDengine(value string) string {
|
||||
return strings.ReplaceAll(value, "'", "''")
|
||||
}
|
||||
|
||||
func normalizeTDengineTime(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
shanghai := time.FixedZone("Asia/Shanghai", 8*3600)
|
||||
for _, layout := range []string{"2006-01-02T15:04:05", "2006-01-02 15:04:05", "2006-01-02"} {
|
||||
if parsed, err := time.ParseInLocation(layout, value, shanghai); err == nil {
|
||||
return parsed.UTC().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
if parsed, err := time.Parse(time.RFC3339, value); err == nil {
|
||||
return parsed.UTC().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user