feat(platform): query tdengine history data

This commit is contained in:
lingniu
2026-07-03 21:22:21 +08:00
parent 2de569f104
commit f9b8182949
11 changed files with 306 additions and 55 deletions

View File

@@ -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
}