mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-20 06:43:42 +08:00
fix: deduplicate cumulative IMS SMS segments (#34)
This commit is contained in:
@@ -3,6 +3,7 @@ package store
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -94,9 +95,15 @@ func mergeConcatSegment(
|
||||
}
|
||||
}
|
||||
}
|
||||
prior, alreadyHad := parts[sequence]
|
||||
changed = !alreadyHad || prior != segmentBody
|
||||
// Some IMS stacks hand us a cumulative segment: sequence 2 contains the
|
||||
// already-decoded text of sequence 1 followed by its own payload. Keep a
|
||||
// snapshot so normalizing that representation remains idempotent on a later
|
||||
// redelivery of the same segment.
|
||||
previousParts := maps.Clone(parts)
|
||||
normalizeCumulativeConcatParts(previousParts)
|
||||
parts[sequence] = segmentBody
|
||||
normalizeCumulativeConcatParts(parts)
|
||||
changed = !maps.Equal(previousParts, parts)
|
||||
|
||||
sequences := make([]int, 0, len(parts))
|
||||
for n := range parts {
|
||||
@@ -130,3 +137,24 @@ func mergeConcatSegment(
|
||||
}
|
||||
return joined.String(), json.RawMessage(encoded), changed, nil
|
||||
}
|
||||
|
||||
// normalizeCumulativeConcatParts converts cumulative IMS segment bodies back
|
||||
// into ordinary per-segment bodies. It only removes an exact, non-empty prefix
|
||||
// assembled from every preceding sequence starting at 1, and only when the
|
||||
// current value also contains additional text. That deliberately leaves equal
|
||||
// repeated segments and incomplete/out-of-order prefixes untouched.
|
||||
func normalizeCumulativeConcatParts(parts map[int]string) {
|
||||
var prefix strings.Builder
|
||||
for sequence := 1; ; sequence++ {
|
||||
text, ok := parts[sequence]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
assembled := prefix.String()
|
||||
if assembled != "" && len(text) > len(assembled) && strings.HasPrefix(text, assembled) {
|
||||
text = strings.TrimPrefix(text, assembled)
|
||||
parts[sequence] = text
|
||||
}
|
||||
prefix.WriteString(text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,62 @@ func TestMergeConcatSegmentRedeliveryIsIdempotent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConcatSegmentNormalizesCumulativeIMSPart(t *testing.T) {
|
||||
first := strings.Repeat("安全提醒", 17)
|
||||
want := first + "请通过官方渠道核实。"
|
||||
_, extra, _, err := mergeConcatSegment(nil, first, concatExtra(t, 8, 2, 1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, normalized, changed, err := mergeConcatSegment(extra, want, concatExtra(t, 8, 2, 2))
|
||||
if err != nil || !changed {
|
||||
t.Fatalf("cumulative segment: body=%q changed=%v err=%v", body, changed, err)
|
||||
}
|
||||
if body != want {
|
||||
t.Fatalf("body = %q, want cumulative text once %q", body, want)
|
||||
}
|
||||
|
||||
// Redelivering the cumulative wire representation must compare equal to the
|
||||
// normalized stored representation and must not churn the durable row id.
|
||||
body, _, changed, err = mergeConcatSegment(normalized, want, concatExtra(t, 8, 2, 2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if changed || body != want {
|
||||
t.Fatalf("redelivery: body=%q changed=%v, want %q/false", body, changed, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConcatSegmentNormalizesCumulativeIMSPartOutOfOrder(t *testing.T) {
|
||||
first := strings.Repeat("甲", 67)
|
||||
want := first + "尾段"
|
||||
_, extra, _, err := mergeConcatSegment(nil, want, concatExtra(t, 12, 2, 2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, _, changed, err := mergeConcatSegment(extra, first, concatExtra(t, 12, 2, 1))
|
||||
if err != nil || !changed {
|
||||
t.Fatalf("out-of-order segment: body=%q changed=%v err=%v", body, changed, err)
|
||||
}
|
||||
if body != want {
|
||||
t.Fatalf("body = %q, want cumulative text once %q", body, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConcatSegmentKeepsEqualRepeatedPart(t *testing.T) {
|
||||
_, extra, _, err := mergeConcatSegment(nil, "重复", concatExtra(t, 13, 2, 1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, _, _, err := mergeConcatSegment(extra, "重复", concatExtra(t, 13, 2, 2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if body != "重复重复" {
|
||||
t.Fatalf("body = %q, want intentional equal segments preserved", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConcatSegmentWithoutHeaderPassesThrough(t *testing.T) {
|
||||
extra, err := json.Marshal(map[string]any{"encoding": "gsm7"})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user