feat: 前端卡片观看数/子节点数接线后端
- dnfs2 get_notes 为每个节点附上 child_count(一次 IN 批量查询统计子节点数,best-effort) - dnfs.js 卡片眼睛=views 观看数、转发=child_count 子节点数(原 share_count/visit_count 后端无字段恒为 0)
This commit is contained in:
1 parent
986acab1c5
commit
c9777cbff8
2 files changed
+49
-3
No files matched your search
@@ -693,6 +693,45 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// 统计各节点的子节点数(get_notes 用于前端"分享数"展示)。
|
||||
// 用一次 IN 批量查询所有孙节点(仅取 pid 列,避免拉取 content),按 pid 分组计数,
|
||||
// 避免对每个节点单独查询。best-effort:任何失败仅记 WARNING 并返回已有结果,
|
||||
// 绝不影响 get_notes 主流程(前端缺省显示 0)。
|
||||
std::unordered_map<int64_t, int64_t> _child_counts(const json::array& items) {
|
||||
std::unordered_map<int64_t, int64_t> counts;
|
||||
try {
|
||||
json::array ids;
|
||||
for (const auto& item : items) {
|
||||
if (item.is_object() && item.as_object().contains("id")
|
||||
&& item.as_object().at("id").is_int64()) {
|
||||
ids.push_back(item.as_object().at("id").as_int64());
|
||||
}
|
||||
}
|
||||
if (ids.empty()) return counts;
|
||||
|
||||
json::object payload;
|
||||
payload["action"] = "select";
|
||||
payload["table"] = "Notes";
|
||||
payload["columns"] = json::array{"pid"};
|
||||
payload["where"] = json::object{{"pid", json::object{{"in", ids}}}};
|
||||
|
||||
DbResult r = _call_db(payload);
|
||||
if (!r.success || !r.data.is_array()) {
|
||||
_log("WARNING", "dnfs2", "child_count 统计失败: " + r.error);
|
||||
return counts;
|
||||
}
|
||||
for (const auto& row : r.data.as_array()) {
|
||||
if (row.is_object() && row.as_object().contains("pid")
|
||||
&& row.as_object().at("pid").is_int64()) {
|
||||
counts[row.as_object().at("pid").as_int64()]++;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
_log("WARNING", "dnfs2", std::string("child_count 统计异常: ") + e.what());
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
// ---- Token 验签(通过 gateway /whoami)---------------------------------
|
||||
|
||||
struct UserInfo {
|
||||
@@ -904,6 +943,8 @@ private:
|
||||
|
||||
if (r.data.is_array()) {
|
||||
json::array result = r.data.as_array();
|
||||
// 一次性统计各节点的子节点数(供前端"分享数"展示),避免逐节点查询
|
||||
auto child_counts = _child_counts(result);
|
||||
for (auto& item : result) {
|
||||
if (item.is_object()) {
|
||||
json::object& note = item.as_object();
|
||||
@@ -916,6 +957,10 @@ private:
|
||||
}
|
||||
}
|
||||
note["can_operate"] = can_operate;
|
||||
int64_t nid = (note.contains("id") && note.at("id").is_int64())
|
||||
? note.at("id").as_int64() : 0;
|
||||
auto cit = child_counts.find(nid);
|
||||
note["child_count"] = (cit != child_counts.end()) ? cit->second : 0;
|
||||
}
|
||||
}
|
||||
_log("INFO", "dnfs2", "get_notes 查询 pid=" + std::to_string(pid)
|
||||
|
||||
@@ -1011,12 +1011,13 @@ function renderCards(list) {
|
||||
owner_name.textContent = owner_text;
|
||||
wrt_div.appendChild(owner_avatar);
|
||||
wrt_div.appendChild(owner_name);
|
||||
// 互动统计:眼睛=观看、爱心=点赞、转发=分享(后端 Notes 表暂无计数字段,缺省显示 0)
|
||||
// 互动统计:眼睛=观看数(后端 views 列)、爱心=点赞、转发=子节点数(后端 child_count)。
|
||||
// views / child_count 由 dnfs2 get_notes 返回;like_count 后端暂无字段,缺省显示 0。
|
||||
let sts_div = document.createElement('div');
|
||||
sts_div.className = "card-stats";
|
||||
sts_div.appendChild(makeStatItem(ICON_EYE, item.visit_count));
|
||||
sts_div.appendChild(makeStatItem(ICON_EYE, item.views));
|
||||
sts_div.appendChild(makeStatItem(ICON_HEART, item.like_count));
|
||||
sts_div.appendChild(makeStatItem(ICON_SHARE, item.share_count));
|
||||
sts_div.appendChild(makeStatItem(ICON_SHARE, item.child_count));
|
||||
// 卡片操作按钮:编辑 / 移动 / 删除(stopPropagation 防止触发卡片自身的点入跳转)
|
||||
// 按钮组整体按 can_operate 显隐:操作权由后端 get_notes 按 owner / is_admin 判定,
|
||||
// 与 delete_note / update_note 鉴权口径一致(移动本质也是 update_note);
|
||||
|
||||
Reference in new issue
Block a user