mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-08 06:16:37 +08:00
feat(reader): legado 书源规则引擎 Go 移植 + 阅读 P0 后端
- internal/service/reader/rule/:逐方法移植 legado analyzeRule 包 (AnalyzeRule/AnalyzeByJSoup/AnalyzeByJSonPath/AnalyzeByXPath/ AnalyzeByRegex/AnalyzeUrl/RuleAnalyzer),JS 规则留 P2 接入点 - 书源导入(JSON数组/对象/Base64/URL)、多源聚合搜索(legado 四档 排序合并)、详情/目录/正文(nextContentUrl 翻页合并) - 数据模型 4 表注册迁移;/api/reader/* 路由组(书源/搜索/书架/进度/调试) - 单测 + httptest 全链路端到端测试 - docs/reader-ui-spec.md:legado UI 交互仿制规格(供 P1 前端使用)
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 本文件对应 AnalyzeByJSonPath.kt(Jayway JsonPath 语义,Go 侧用
|
||||
// PaesslerAG/jsonpath 实现)。
|
||||
|
||||
type jsonAnalyzer struct {
|
||||
root any
|
||||
}
|
||||
|
||||
func newJSONAnalyzer(doc any) *jsonAnalyzer {
|
||||
switch t := doc.(type) {
|
||||
case string:
|
||||
var v any
|
||||
if err := json.Unmarshal([]byte(t), &v); err != nil {
|
||||
return &jsonAnalyzer{root: nil}
|
||||
}
|
||||
return &jsonAnalyzer{root: v}
|
||||
default:
|
||||
return &jsonAnalyzer{root: doc}
|
||||
}
|
||||
}
|
||||
|
||||
// jsonRead 对应 ctx.read(rule):路径求值,失败返回 nil。
|
||||
func jsonRead(root any, path string) any {
|
||||
if root == nil || path == "" {
|
||||
return nil
|
||||
}
|
||||
v, err := jsonpathGet(path, root)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// jsonValueToString 对应 Kotlin 的 ob.toString() / joinToString("\n")。
|
||||
func jsonValueToString(ob any) string {
|
||||
switch t := ob.(type) {
|
||||
case nil:
|
||||
return ""
|
||||
case string:
|
||||
return t
|
||||
case []any:
|
||||
parts := make([]string, len(t))
|
||||
for i, e := range t {
|
||||
parts[i] = jsonValueToString(e)
|
||||
}
|
||||
return strings.Join(parts, "\n")
|
||||
default:
|
||||
return anyToString(t)
|
||||
}
|
||||
}
|
||||
|
||||
// getString 对应 AnalyzeByJSonPath.getString。
|
||||
func (a *jsonAnalyzer) getString(rule string) string {
|
||||
if rule == "" {
|
||||
return ""
|
||||
}
|
||||
ra := NewRuleAnalyzer(rule, true)
|
||||
rules := ra.SplitRule("&&", "||")
|
||||
if len(rules) == 1 {
|
||||
ra.ReSetPos()
|
||||
result := ra.InnerRule("{$.", 1, 1, func(inner string) string {
|
||||
return a.getString(inner)
|
||||
})
|
||||
if result == "" {
|
||||
result = jsonValueToString(jsonRead(a.root, rule))
|
||||
}
|
||||
return result
|
||||
}
|
||||
var textList []string
|
||||
for _, rl := range rules {
|
||||
temp := a.getString(rl)
|
||||
if temp != "" {
|
||||
textList = append(textList, temp)
|
||||
if ra.ElementsType() == "||" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(textList, "\n")
|
||||
}
|
||||
|
||||
// getStringList 对应 AnalyzeByJSonPath.getStringList。
|
||||
func (a *jsonAnalyzer) getStringList(rule string) []string {
|
||||
var result []string
|
||||
if rule == "" {
|
||||
return result
|
||||
}
|
||||
ra := NewRuleAnalyzer(rule, true)
|
||||
rules := ra.SplitRule("&&", "||", "%%")
|
||||
if len(rules) == 1 {
|
||||
ra.ReSetPos()
|
||||
st := ra.InnerRule("{$.", 1, 1, func(inner string) string {
|
||||
return a.getString(inner)
|
||||
})
|
||||
if st == "" {
|
||||
ob := jsonRead(a.root, rule)
|
||||
if ob == nil {
|
||||
return result
|
||||
}
|
||||
if lst, ok := ob.([]any); ok {
|
||||
for _, o := range lst {
|
||||
result = append(result, jsonValueToString(o))
|
||||
}
|
||||
} else {
|
||||
result = append(result, jsonValueToString(ob))
|
||||
}
|
||||
} else {
|
||||
result = append(result, st)
|
||||
}
|
||||
return result
|
||||
}
|
||||
var results [][]string
|
||||
for _, rl := range rules {
|
||||
temp := a.getStringList(rl)
|
||||
if len(temp) > 0 {
|
||||
results = append(results, temp)
|
||||
if ra.ElementsType() == "||" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(results) > 0 {
|
||||
if ra.ElementsType() == "%%" {
|
||||
for i := 0; i < len(results[0]); i++ {
|
||||
for _, temp := range results {
|
||||
if i < len(temp) {
|
||||
result = append(result, temp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, temp := range results {
|
||||
result = append(result, temp...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getObject 对应 getObject:直接返回求值结果。
|
||||
func (a *jsonAnalyzer) getObject(rule string) any {
|
||||
return jsonRead(a.root, rule)
|
||||
}
|
||||
|
||||
// getList 对应 getList:要求路径结果为数组(对应 jayway read<ArrayList>,
|
||||
// 非数组时 legado 侧捕获异常返回空列表)。
|
||||
func (a *jsonAnalyzer) getList(rule string) []any {
|
||||
var result []any
|
||||
if rule == "" {
|
||||
return result
|
||||
}
|
||||
ra := NewRuleAnalyzer(rule, true)
|
||||
rules := ra.SplitRule("&&", "||", "%%")
|
||||
if len(rules) == 1 {
|
||||
ob := jsonRead(a.root, rules[0])
|
||||
if lst, ok := ob.([]any); ok {
|
||||
return lst
|
||||
}
|
||||
return result
|
||||
}
|
||||
var results [][]any
|
||||
for _, rl := range rules {
|
||||
temp := a.getList(rl)
|
||||
if len(temp) > 0 {
|
||||
results = append(results, temp)
|
||||
if ra.ElementsType() == "||" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(results) > 0 {
|
||||
if ra.ElementsType() == "%%" {
|
||||
for i := 0; i < len(results[0]); i++ {
|
||||
for _, temp := range results {
|
||||
if i < len(temp) {
|
||||
result = append(result, temp[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, temp := range results {
|
||||
result = append(result, temp...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user