Files
lunar-mini/server/internal/service/baike_term_sync_test.go
T
gouki 241a04496b
Build and Publish Server / build (push) Successful in 2m6s
Publish Mini Program Dev Version / publish (push) Successful in 3m3s
fix(term): 清理百度百科摘要的截断结尾
百度百科 BaikeLemmaCardApi 摘要以 ... 硬截断,出现半截话。
新增 trimTruncatedTail:去掉结尾省略号后,若末句不完整则
回退到最后一个完整句标点(。!?),避免「唐宋时期,冬至...」
这类戛然而止的结尾。仅依赖百度百科,不引入维基,线上可用。
2026-08-18 00:09:12 +00:00

152 lines
5.5 KiB
Go

package service
import (
"encoding/json"
"strings"
"testing"
)
// 取自百度百科 BaikeLemmaCardApi 「立春」的真实返回片段
const sampleLemmaJSON = `{
"id":25702,"key":"立春","desc":"二十四节气之一","title":"立春",
"image":"https://bkimg.cdn.bcebos.com/pic/d62a6059252dd42a2834edd463674cb5c9ea15ce1205",
"abstract":"立春(Beginning of Spring),又名正月节、岁节、改岁、岁旦、打春等,为二十四节气之首。当太阳到达黄经315°时为立春,于每年公历2月3-5日交节。",
"card":[
{"key":"m25_nameC","name":"中文名","value":["立春"]},
{"key":"m25_nameE","name":"外文名","value":["Beginning of Spring<sup>3</sup><a name=\"ref_3\"></a>","Lichun"]},
{"key":"m25_alternateName","name":"别名","value":["岁首、岁节、改岁、立春节、正月节"]},
{"key":"m25_meaning","name":"涵义","value":["阳气上升,万物更生,新岁开启"]},
{"key":"m25_solarDate","name":"公历时间","value":["公历2月3日-2月5日交节"]},
{"key":"m25_ecliptic","name":"黄道位置","value":["太阳到达黄经315度"]},
{"key":"m25_phenology","name":"物候现象","value":["东风解冻、蛰虫始振、鱼陟负冰"]},
{"key":"m25_farming","name":"农事活动","value":["备耕春播"]},
{"key":"m25_customs","name":"传统习俗","value":["<a target=_blank href=\"/item/x\">演春</a>、迎春、打春"]},
{"key":"m25_health","name":"起居养生","value":["防躁动,避免过度劳累,保证睡眠情绪豁达。"]},
{"key":"m25_flower","name":"花信","value":["迎春、樱桃、望春<sup>35</sup><a name=\"ref_35\"></a>"]}
]
}`
func TestParseLemmaCard(t *testing.T) {
var lemma baikeLemmaCard
if err := json.Unmarshal([]byte(sampleLemmaJSON), &lemma); err != nil {
t.Fatalf("JSON 解析失败: %v", err)
}
if lemma.Key != "立春" {
t.Errorf("key = %q, want 立春", lemma.Key)
}
if lemma.Image == "" {
t.Error("image 不应为空")
}
if !strings.Contains(lemma.Abstract, "二十四节气之首") {
t.Errorf("abstract 应含「二十四节气之首」, got %q", lemma.Abstract)
}
if len(lemma.Card) == 0 {
t.Fatal("card 不应为空")
}
}
func TestLemmaCardMapping(t *testing.T) {
var lemma baikeLemmaCard
json.Unmarshal([]byte(sampleLemmaJSON), &lemma)
extra := map[string]string{}
for _, c := range lemma.Card {
text := cleanTermHTML(firstValue(c.Value))
if text == "" {
continue
}
switch c.Name {
case "别名":
extra["alias"] = text
case "外文名":
extra["english"] = text
case "涵义":
extra["meaning"] = text
case "物候现象":
extra["phenology"] = text
case "传统习俗":
extra["customs"] = text
case "起居养生":
extra["health"] = text
}
}
if extra["alias"] != "岁首、岁节、改岁、立春节、正月节" {
t.Errorf("alias = %q", extra["alias"])
}
if extra["meaning"] != "阳气上升,万物更生,新岁开启" {
t.Errorf("meaning = %q", extra["meaning"])
}
if extra["phenology"] != "东风解冻、蛰虫始振、鱼陟负冰" {
t.Errorf("phenology = %q", extra["phenology"])
}
// 传统习俗含 HTML 链接,应被清洗
if strings.Contains(extra["customs"], "<a") || strings.Contains(extra["customs"], "href") {
t.Errorf("customs 应清洗 HTML, got %q", extra["customs"])
}
if !strings.Contains(extra["customs"], "演春") {
t.Errorf("customs 应保留文字「演春」, got %q", extra["customs"])
}
// 外文名含 <sup> 应清洗
if strings.Contains(extra["english"], "<sup>") {
t.Errorf("english 应清洗 sup 标签, got %q", extra["english"])
}
}
func TestCleanTermHTML(t *testing.T) {
cases := map[string]string{
`<a target=_blank href="/item/x">演春</a>、迎春`: "演春、迎春",
`Beginning of Spring<sup>3</sup><a name="ref_3"></a>`: "Beginning of Spring",
` 多余&nbsp; 空白 `: "多余 空白",
}
for in, want := range cases {
if got := cleanTermHTML(in); got != want {
t.Errorf("cleanTermHTML(%q) = %q, want %q", in, got, want)
}
}
}
func TestFirstSentence(t *testing.T) {
s := "立春为二十四节气之首。当太阳到达黄经315°时为立春。"
if got := firstSentence(s); got != "立春为二十四节气之首。" {
t.Errorf("firstSentence = %q", got)
}
// 无句号时返回原文
if got := firstSentence("无句号文本"); got != "无句号文本" {
t.Errorf("firstSentence 无句号 = %q", got)
}
}
func TestTermSort(t *testing.T) {
if termSort("立春") != 1 {
t.Errorf("termSort(立春) = %d, want 1", termSort("立春"))
}
if termSort("大寒") != 24 {
t.Errorf("termSort(大寒) = %d, want 24", termSort("大寒"))
}
if termSort("不存在") != 0 {
t.Errorf("termSort(不存在) = %d, want 0", termSort("不存在"))
}
}
func TestTrimTruncatedTail(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{"完整句号结尾不动", "夏至后人们普遍食用清补凉汤来避暑。", "夏至后人们普遍食用清补凉汤来避暑。"},
{"英文省略号回退", "表达感谢的心情。唐宋时期,冬至...", "表达感谢的心情。"},
{"中文省略号回退", "表达感谢的心情。唐宋时期,冬至…", "表达感谢的心情。"},
{"无句号半截话保留", "可明显感觉到早春的气...", "可明显感觉到早春的气"},
{"多点省略号", "农事谚语....", "农事谚语"},
{"结尾空白加省略号", "冬至大如年。 ...", "冬至大如年。"},
{"空字符串", "", ""},
}
for _, c := range cases {
if got := trimTruncatedTail(c.in); got != c.want {
t.Errorf("%s: trimTruncatedTail(%q) = %q, want %q", c.name, c.in, got, c.want)
}
}
}