Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5016d5f3f1 | ||
|
|
2da005ecca | ||
|
|
4e40f486d5 | ||
|
|
039ad42025 | ||
|
|
3d59c9a975 |
@@ -149,12 +149,9 @@ def api_player(pid):
|
|||||||
def api_games():
|
def api_games():
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
status = request.args.get("status", "")
|
status = request.args.get("status", "")
|
||||||
limit = int(request.args.get("limit", 30))
|
page = max(1, int(request.args.get("page", 1)))
|
||||||
r = tools.search_games(q, limit=limit)
|
size = min(40, max(1, int(request.args.get("size", 12))))
|
||||||
results = r.get("results", [])
|
return jsonify(tools.list_games(q, status=status, page=page, size=size))
|
||||||
if status:
|
|
||||||
results = [g for g in results if g["status"] == status]
|
|
||||||
return jsonify(results)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/games/<int:gid>")
|
@app.route("/api/games/<int:gid>")
|
||||||
@@ -194,15 +191,10 @@ def api_playoffs():
|
|||||||
def api_news():
|
def api_news():
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
kind = request.args.get("kind", "")
|
kind = request.args.get("kind", "")
|
||||||
limit = int(request.args.get("limit", 20))
|
tag = request.args.get("tag", "")
|
||||||
if q:
|
page = max(1, int(request.args.get("page", 1)))
|
||||||
r = tools.search_news(q, limit=limit)
|
size = min(30, max(1, int(request.args.get("size", 10))))
|
||||||
return jsonify(r.get("results", []))
|
return jsonify(tools.list_news(q, kind=kind, tag=tag, page=page, size=size))
|
||||||
from db import query
|
|
||||||
rows = query("""SELECT id,title,author,source,publish_time,tags,kind,substr(content,1,160) AS summary
|
|
||||||
FROM news WHERE (?='' OR kind=?) ORDER BY publish_time DESC, id DESC LIMIT ?""",
|
|
||||||
(kind, kind, limit))
|
|
||||||
return jsonify(rows)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/news/<int:nid>")
|
@app.route("/api/news/<int:nid>")
|
||||||
@@ -219,12 +211,9 @@ def api_news_detail(nid):
|
|||||||
def api_persons():
|
def api_persons():
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
role = request.args.get("role", "")
|
role = request.args.get("role", "")
|
||||||
limit = int(request.args.get("limit", 50))
|
page = max(1, int(request.args.get("page", 1)))
|
||||||
r = tools.search_persons(q, limit=limit)
|
size = min(30, max(1, int(request.args.get("size", 12))))
|
||||||
results = r.get("results", [])
|
return jsonify(tools.list_persons(q, role=role, page=page, size=size))
|
||||||
if role:
|
|
||||||
results = [p for p in results if p["role"] == role]
|
|
||||||
return jsonify(results)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/persons/<int:pid>")
|
@app.route("/api/persons/<int:pid>")
|
||||||
|
|||||||
+56
-25
@@ -86,9 +86,24 @@ function loadView(v) {
|
|||||||
$$("#view-players .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.pos === ""));
|
$$("#view-players .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.pos === ""));
|
||||||
loadPlayers();
|
loadPlayers();
|
||||||
}
|
}
|
||||||
else if (v === "games") loadGames("");
|
else if (v === "games") {
|
||||||
else if (v === "news") loadNews("");
|
gamesPage = 1; gamesQ = ""; gamesStatus = "";
|
||||||
else if (v === "persons") loadPersons("");
|
$("#search-games").value = "";
|
||||||
|
$$("#view-games .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.status === ""));
|
||||||
|
loadGames();
|
||||||
|
}
|
||||||
|
else if (v === "news") {
|
||||||
|
newsPage = 1; newsQ = ""; newsKind = ""; newsTag = "";
|
||||||
|
$("#search-news").value = "";
|
||||||
|
$$("#view-news .btn.small").forEach((x) => x.classList.toggle("active", !x.dataset.kind && !x.dataset.tag));
|
||||||
|
loadNews();
|
||||||
|
}
|
||||||
|
else if (v === "persons") {
|
||||||
|
personsPage = 1; personsQ = ""; personsRole = "";
|
||||||
|
$("#search-persons").value = "";
|
||||||
|
$$("#view-persons .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.role === ""));
|
||||||
|
loadPersons();
|
||||||
|
}
|
||||||
else if (v === "standings") {
|
else if (v === "standings") {
|
||||||
if ($("#season-select").options.length) { loadStandings(); loadPlayoffs(); }
|
if ($("#season-select").options.length) { loadStandings(); loadPlayoffs(); }
|
||||||
else loadSeasons();
|
else loadSeasons();
|
||||||
@@ -405,7 +420,9 @@ function renderPager(d, key) {
|
|||||||
}
|
}
|
||||||
function goPage(key, p) {
|
function goPage(key, p) {
|
||||||
if (key === "players") { playersPage = p; loadPlayers(); }
|
if (key === "players") { playersPage = p; loadPlayers(); }
|
||||||
else if (key === "games") { gamesPage = p; loadGames($("#search-games").value.trim()); }
|
else if (key === "games") { gamesPage = p; loadGames(); }
|
||||||
|
else if (key === "news") { newsPage = p; loadNews(); }
|
||||||
|
else if (key === "persons") { personsPage = p; loadPersons(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#btn-players").addEventListener("click", () => { playersQ = $("#search-players").value.trim(); playersPage = 1; loadPlayers(); });
|
$("#btn-players").addEventListener("click", () => { playersQ = $("#search-players").value.trim(); playersPage = 1; loadPlayers(); });
|
||||||
@@ -439,12 +456,11 @@ async function openPlayer(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 比赛 ================= */
|
/* ================= 比赛 ================= */
|
||||||
let gameStatusFilter = "";
|
let gamesPage = 1, gamesQ = "", gamesStatus = "";
|
||||||
async function loadGames(q) {
|
async function loadGames() {
|
||||||
const url = `/api/games?q=${encodeURIComponent(q)}&limit=40`;
|
const d = await getJSON(`/api/games?q=${encodeURIComponent(gamesQ)}&status=${gamesStatus}&page=${gamesPage}&size=12`);
|
||||||
const games = await getJSON(url);
|
const games = d.results || [];
|
||||||
const list = games.filter((g) => !gameStatusFilter || g.status === gameStatusFilter);
|
$("#list-games").innerHTML = games.map((g) => {
|
||||||
$("#list-games").innerHTML = list.map((g) => {
|
|
||||||
const finished = g.status === "finished";
|
const finished = g.status === "finished";
|
||||||
const score = finished ? `<span class="score-big">${g.away_score}</span> : <span class="score-big">${g.home_score}</span>` : "VS";
|
const score = finished ? `<span class="score-big">${g.away_score}</span> : <span class="score-big">${g.home_score}</span>` : "VS";
|
||||||
return `<div class="list-item" onclick="openGame(${g.id})">
|
return `<div class="list-item" onclick="openGame(${g.id})">
|
||||||
@@ -460,14 +476,16 @@ async function loadGames(q) {
|
|||||||
<div class="meta">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div>
|
<div class="meta">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join("") || '<div class="meta">没有符合条件的比赛</div>';
|
}).join("") || '<div class="meta">没有符合条件的比赛</div>';
|
||||||
|
renderPager(d, "games");
|
||||||
}
|
}
|
||||||
$$("#view-games .btn.small").forEach((b) => b.addEventListener("click", () => {
|
$$("#view-games .btn.small").forEach((b) => b.addEventListener("click", () => {
|
||||||
$$("#view-games .btn.small").forEach((x) => x.classList.remove("active"));
|
$$("#view-games .btn.small").forEach((x) => x.classList.remove("active"));
|
||||||
b.classList.add("active");
|
b.classList.add("active");
|
||||||
gameStatusFilter = b.dataset.status;
|
gamesStatus = b.dataset.status;
|
||||||
loadGames($("#search-games").value.trim());
|
gamesPage = 1;
|
||||||
|
loadGames();
|
||||||
}));
|
}));
|
||||||
$("#btn-games").addEventListener("click", () => loadGames($("#search-games").value.trim()));
|
$("#btn-games").addEventListener("click", () => { gamesQ = $("#search-games").value.trim(); gamesPage = 1; loadGames(); });
|
||||||
$("#search-games").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-games").click(); });
|
$("#search-games").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-games").click(); });
|
||||||
async function openGame(id) {
|
async function openGame(id) {
|
||||||
const g = await getJSON(`/api/games/${id}`);
|
const g = await getJSON(`/api/games/${id}`);
|
||||||
@@ -491,8 +509,10 @@ async function openGame(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 新闻 ================= */
|
/* ================= 新闻 ================= */
|
||||||
async function loadNews(q) {
|
let newsPage = 1, newsQ = "", newsKind = "", newsTag = "";
|
||||||
const news = await getJSON(`/api/news?q=${encodeURIComponent(q)}&limit=30`);
|
async function loadNews() {
|
||||||
|
const d = await getJSON(`/api/news?q=${encodeURIComponent(newsQ)}&kind=${newsKind}&tag=${encodeURIComponent(newsTag)}&page=${newsPage}&size=10`);
|
||||||
|
const news = d.results || [];
|
||||||
$("#list-news").innerHTML = news.map((n) => `
|
$("#list-news").innerHTML = news.map((n) => `
|
||||||
<div class="list-item" onclick="openNews(${n.id})">
|
<div class="list-item" onclick="openNews(${n.id})">
|
||||||
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||||||
@@ -501,10 +521,19 @@ async function loadNews(q) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="meta">🕐 ${esc(n.publish_time)} · ${esc(n.source)}${n.author ? " · " + esc(n.author) : ""}</div>
|
<div class="meta">🕐 ${esc(n.publish_time)} · ${esc(n.source)}${n.author ? " · " + esc(n.author) : ""}</div>
|
||||||
<div class="meta" style="margin-top:4px">${esc(n.summary || n.content || "")}…</div>
|
<div class="meta" style="margin-top:4px">${esc(n.summary || n.content || "")}…</div>
|
||||||
</div>`).join("");
|
</div>`).join("") || '<div class="meta">没有符合条件的新闻</div>';
|
||||||
|
renderPager(d, "news");
|
||||||
}
|
}
|
||||||
$("#btn-news").addEventListener("click", () => loadNews($("#search-news").value.trim()));
|
$("#btn-news").addEventListener("click", () => { newsQ = $("#search-news").value.trim(); newsPage = 1; loadNews(); });
|
||||||
$("#search-news").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-news").click(); });
|
$("#search-news").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-news").click(); });
|
||||||
|
$$("#view-news .btn.small").forEach((b) => b.addEventListener("click", () => {
|
||||||
|
$$("#view-news .btn.small").forEach((x) => x.classList.remove("active"));
|
||||||
|
b.classList.add("active");
|
||||||
|
newsKind = b.dataset.kind;
|
||||||
|
newsTag = b.dataset.tag;
|
||||||
|
newsPage = 1;
|
||||||
|
loadNews();
|
||||||
|
}));
|
||||||
async function openNews(id) {
|
async function openNews(id) {
|
||||||
const n = await getJSON(`/api/news/${id}`);
|
const n = await getJSON(`/api/news/${id}`);
|
||||||
openModal(`
|
openModal(`
|
||||||
@@ -516,24 +545,26 @@ async function openNews(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 人物 ================= */
|
/* ================= 人物 ================= */
|
||||||
let personRoleFilter = "";
|
let personsPage = 1, personsQ = "", personsRole = "";
|
||||||
async function loadPersons(q) {
|
async function loadPersons() {
|
||||||
const persons = await getJSON(`/api/persons?q=${encodeURIComponent(q)}&limit=60`);
|
const d = await getJSON(`/api/persons?q=${encodeURIComponent(personsQ)}&role=${personsRole}&page=${personsPage}&size=12`);
|
||||||
const list = persons.filter((p) => !personRoleFilter || p.role === personRoleFilter);
|
const persons = d.results || [];
|
||||||
$("#grid-persons").innerHTML = list.map((p) => `
|
$("#grid-persons").innerHTML = persons.map((p) => `
|
||||||
<div class="card" onclick="openPerson(${p.id})">
|
<div class="card" onclick="openPerson(${p.id})">
|
||||||
<h3>${esc(p.name)} <span class="en">${esc(p.name_en || "")}</span></h3>
|
<h3>${esc(p.name)} <span class="en">${esc(p.name_en || "")}</span></h3>
|
||||||
<div class="meta"><span class="tag">${esc(p.role_cn)}</span>${esc(p.title || "")}${p.team ? " · " + esc(p.team) : ""}</div>
|
<div class="meta"><span class="tag">${esc(p.role_cn)}</span>${esc(p.title || "")}${p.team ? " · " + esc(p.team) : ""}</div>
|
||||||
<div class="meta">${esc((p.bio || "").slice(0, 60))}…</div>
|
<div class="meta">${esc((p.bio || "").slice(0, 60))}…</div>
|
||||||
</div>`).join("");
|
</div>`).join("");
|
||||||
|
renderPager(d, "persons");
|
||||||
}
|
}
|
||||||
$$("#view-persons .btn.small").forEach((b) => b.addEventListener("click", () => {
|
$$("#view-persons .btn.small").forEach((b) => b.addEventListener("click", () => {
|
||||||
$$("#view-persons .btn.small").forEach((x) => x.classList.remove("active"));
|
$$("#view-persons .btn.small").forEach((x) => x.classList.remove("active"));
|
||||||
b.classList.add("active");
|
b.classList.add("active");
|
||||||
personRoleFilter = b.dataset.role;
|
personsRole = b.dataset.role;
|
||||||
loadPersons($("#search-persons").value.trim());
|
personsPage = 1;
|
||||||
|
loadPersons();
|
||||||
}));
|
}));
|
||||||
$("#btn-persons").addEventListener("click", () => loadPersons($("#search-persons").value.trim()));
|
$("#btn-persons").addEventListener("click", () => { personsQ = $("#search-persons").value.trim(); personsPage = 1; loadPersons(); });
|
||||||
$("#search-persons").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-persons").click(); });
|
$("#search-persons").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-persons").click(); });
|
||||||
async function openPerson(id) {
|
async function openPerson(id) {
|
||||||
const p = await getJSON(`/api/persons/${id}`);
|
const p = await getJSON(`/api/persons/${id}`);
|
||||||
|
|||||||
+14
-2
@@ -78,11 +78,22 @@
|
|||||||
<input class="search" id="search-games" placeholder="搜索:如 湖人 最近">
|
<input class="search" id="search-games" placeholder="搜索:如 湖人 最近">
|
||||||
<button class="btn" id="btn-games">搜索</button>
|
<button class="btn" id="btn-games">搜索</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="list" id="list-games"></div>
|
<div class="grid games-grid" id="list-games"></div>
|
||||||
|
<div class="pager" id="pager-games"></div>
|
||||||
</section>
|
</section>
|
||||||
<section id="view-news" class="view">
|
<section id="view-news" class="view">
|
||||||
<div class="toolbar"><input class="search" id="search-news" placeholder="搜索新闻关键词"><button class="btn" id="btn-news">搜索</button></div>
|
<div class="toolbar">
|
||||||
|
<button class="btn small active" data-kind="" data-tag="">全部</button>
|
||||||
|
<button class="btn small" data-kind="news" data-tag="">新闻</button>
|
||||||
|
<button class="btn small" data-kind="wiki" data-tag="">百科</button>
|
||||||
|
<button class="btn small" data-kind="" data-tag="总决赛">总决赛</button>
|
||||||
|
<button class="btn small" data-kind="" data-tag="续约">续约</button>
|
||||||
|
<button class="btn small" data-kind="" data-tag="中国球员">中国球员</button>
|
||||||
|
<button class="btn small" data-kind="" data-tag="选秀">选秀</button>
|
||||||
|
<input class="search" id="search-news" placeholder="搜索新闻关键词"><button class="btn" id="btn-news">搜索</button>
|
||||||
|
</div>
|
||||||
<div class="list" id="list-news"></div>
|
<div class="list" id="list-news"></div>
|
||||||
|
<div class="pager" id="pager-news"></div>
|
||||||
</section>
|
</section>
|
||||||
<section id="view-persons" class="view">
|
<section id="view-persons" class="view">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
@@ -96,6 +107,7 @@
|
|||||||
<input class="search" id="search-persons" placeholder="搜索人物"><button class="btn" id="btn-persons">搜索</button>
|
<input class="search" id="search-persons" placeholder="搜索人物"><button class="btn" id="btn-persons">搜索</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid" id="grid-persons"></div>
|
<div class="grid" id="grid-persons"></div>
|
||||||
|
<div class="pager" id="pager-persons"></div>
|
||||||
</section>
|
</section>
|
||||||
<section id="view-standings" class="view">
|
<section id="view-standings" class="view">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
|
|||||||
@@ -132,6 +132,10 @@ main { flex: 1; width: 100%; max-width: 1200px; margin: 0 auto; padding: 20px 16
|
|||||||
.pager button.active { background: linear-gradient(135deg, #f97316, #ea580c); color: #fff; border-color: transparent; }
|
.pager button.active { background: linear-gradient(135deg, #f97316, #ea580c); color: #fff; border-color: transparent; }
|
||||||
.pager button:disabled { opacity: .35; cursor: default; }
|
.pager button:disabled { opacity: .35; cursor: default; }
|
||||||
.pager span { margin: 0 4px; }
|
.pager span { margin: 0 4px; }
|
||||||
|
|
||||||
|
/* ---------- 比赛网格卡片(固定宽度420px,居中排列) ---------- */
|
||||||
|
.games-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }
|
||||||
|
.games-grid .list-item { flex: 0 0 420px; width: 420px; display: flex; flex-direction: column; justify-content: space-between; gap: 8px; height: 100%; padding: 14px 16px; }
|
||||||
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; cursor: pointer; transition: .18s; }
|
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; cursor: pointer; transition: .18s; }
|
||||||
.card:hover { transform: translateY(-3px); border-color: var(--orange2); box-shadow: 0 6px 20px rgba(0,0,0,.35); }
|
.card:hover { transform: translateY(-3px); border-color: var(--orange2); box-shadow: 0 6px 20px rgba(0,0,0,.35); }
|
||||||
.card h3 { font-size: 15.5px; margin-bottom: 4px; }
|
.card h3 { font-size: 15.5px; margin-bottom: 4px; }
|
||||||
|
|||||||
@@ -279,6 +279,18 @@ def get_game_detail(game_id):
|
|||||||
return detail
|
return detail
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================== 比赛列表(前台分页版)
|
||||||
|
def list_games(q="", status="", page=1, size=12):
|
||||||
|
"""前台比赛列表:关键词 + 状态筛选 + 分页(数据量小,全量匹配后切片)"""
|
||||||
|
r = search_games(q, limit=500)
|
||||||
|
results = r.get("results", [])
|
||||||
|
if status:
|
||||||
|
results = [g for g in results if g["status"] == status]
|
||||||
|
total = len(results)
|
||||||
|
start = (page - 1) * size
|
||||||
|
return {"results": results[start:start + size], "total": total, "page": page, "size": size}
|
||||||
|
|
||||||
|
|
||||||
# ================================================================== 排名
|
# ================================================================== 排名
|
||||||
def search_standings(query_text, limit=20, season=None):
|
def search_standings(query_text, limit=20, season=None):
|
||||||
q = (query_text or "").strip()
|
q = (query_text or "").strip()
|
||||||
@@ -339,6 +351,28 @@ def player_game_logs(player_id, limit=10):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================== 新闻列表(前台分页版)
|
||||||
|
def list_news(q="", kind="", tag="", page=1, size=10):
|
||||||
|
"""前台新闻列表:关键词 + 类型(kind) + 话题标签(tag) + 分页"""
|
||||||
|
conds, args = [], []
|
||||||
|
if q:
|
||||||
|
like = f"%{fuzzy(q)}%"
|
||||||
|
conds.append("(title LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\')")
|
||||||
|
args += [like, like, like]
|
||||||
|
if kind:
|
||||||
|
conds.append("kind=?")
|
||||||
|
args.append(kind)
|
||||||
|
if tag:
|
||||||
|
conds.append("tags LIKE ? ESCAPE '\\'")
|
||||||
|
args.append(f"%{fuzzy(tag)}%")
|
||||||
|
where = ("WHERE " + " AND ".join(conds)) if conds else ""
|
||||||
|
total = query_one(f"SELECT COUNT(*) AS c FROM news {where}", args)["c"]
|
||||||
|
rows = query(f"""SELECT id,title,author,source,publish_time,tags,kind,substr(content,1,160) AS summary
|
||||||
|
FROM news {where} ORDER BY publish_time DESC, id DESC LIMIT ? OFFSET ?""",
|
||||||
|
args + [size, (page - 1) * size])
|
||||||
|
return {"results": rows, "total": total, "page": page, "size": size}
|
||||||
|
|
||||||
|
|
||||||
# ================================================================== 新闻(SQL 关键词 + 向量语义 + 可选 rerank 融合)
|
# ================================================================== 新闻(SQL 关键词 + 向量语义 + 可选 rerank 融合)
|
||||||
def search_news(query_text, limit=5):
|
def search_news(query_text, limit=5):
|
||||||
q = (query_text or "").strip()
|
q = (query_text or "").strip()
|
||||||
@@ -399,6 +433,28 @@ def search_persons(query_text, limit=MAX_SHOW):
|
|||||||
"bio": r["bio"], "achievements": r["achievements"]} for r in rows]}
|
"bio": r["bio"], "achievements": r["achievements"]} for r in rows]}
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================== 人物列表(前台分页版)
|
||||||
|
def list_persons(q="", role="", page=1, size=12):
|
||||||
|
"""前台人物列表:关键词 + 角色筛选 + 分页"""
|
||||||
|
conds, args = [], []
|
||||||
|
if q:
|
||||||
|
like = f"%{fuzzy(q)}%"
|
||||||
|
conds.append("(p.name LIKE ? ESCAPE '\\' OR p.name_en LIKE ? ESCAPE '\\' OR p.role_cn LIKE ? ESCAPE '\\' OR t.name LIKE ? ESCAPE '\\' OR p.bio LIKE ? ESCAPE '\\')")
|
||||||
|
args += [like] * 5
|
||||||
|
if role:
|
||||||
|
conds.append("p.role=?")
|
||||||
|
args.append(role)
|
||||||
|
where = ("WHERE " + " AND ".join(conds)) if conds else ""
|
||||||
|
total = query_one(f"SELECT COUNT(*) AS c FROM persons p LEFT JOIN teams t ON p.team_id=t.id {where}", args)["c"]
|
||||||
|
rows = query(f"""SELECT p.*, t.name AS team_name FROM persons p LEFT JOIN teams t ON p.team_id=t.id
|
||||||
|
{where} ORDER BY p.id LIMIT ? OFFSET ?""", args + [size, (page - 1) * size])
|
||||||
|
return {"results": [
|
||||||
|
{"id": r["id"], "name": r["name"], "name_en": r["name_en"], "role": r["role"],
|
||||||
|
"role_cn": r["role_cn"], "title": r["title"], "team": r["team_name"],
|
||||||
|
"bio": r["bio"], "achievements": r["achievements"]} for r in rows],
|
||||||
|
"total": total, "page": page, "size": size}
|
||||||
|
|
||||||
|
|
||||||
# ================================================================== 知识百科(纯向量检索)
|
# ================================================================== 知识百科(纯向量检索)
|
||||||
def search_knowledge(query_text, limit=3):
|
def search_knowledge(query_text, limit=3):
|
||||||
q = (query_text or "").strip()
|
q = (query_text or "").strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user