feat: Notes 新增 views 观看人数列,dnfs2 get_notes 打开节点时自增
- db2 补丁 patch_001:Notes 增加 views 列(默认 0),补齐 pid/owner 索引 - dnfs2 get_notes(pid) 打开节点时对 pid 的 views 读改写 +1(best-effort,失败不影响主流程)
This commit is contained in:
1 parent
049b894416
commit
986acab1c5
2 files changed
+64
No files matched your search
@@ -0,0 +1,16 @@
|
|||||||
|
-- 补丁 001:Notes 表新增观看人数列 + 补齐索引
|
||||||
|
-- 数据库类型: SQLite
|
||||||
|
-- 依赖: create_sqlite3.sql 已执行(Users / Notes / Logs 表已存在)
|
||||||
|
-- 幂等性: 索引部分可重复执行(IF NOT EXISTS);
|
||||||
|
-- ADD COLUMN 为一次性迁移,重复执行会报 "duplicate column name: views",
|
||||||
|
-- 请按迁移编号顺序执行一次。
|
||||||
|
|
||||||
|
-- ── Notes 新增观看人数列 ────────────────────────────────────────────
|
||||||
|
-- 观看人数(view count),非空、默认 0,保证旧数据与新插入兼容。
|
||||||
|
ALTER TABLE Notes ADD COLUMN views INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- ── 补齐此前缺失的索引 ──────────────────────────────────────────────
|
||||||
|
-- dnfs2 get_notes 按 pid 查询子节点,此前为全表扫描。
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_notes_pid ON Notes(pid);
|
||||||
|
-- dnfs2 / ca 按 owner 做所有权校验,此前为全表扫描。
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_notes_owner ON Notes(owner);
|
||||||
@@ -651,6 +651,48 @@ private:
|
|||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 观看人数自增(get_notes 打开节点 pid 时调用)。
|
||||||
|
// db2 的 update 仅支持字面量绑定(SET col = ?),无法表达 views = views + 1,
|
||||||
|
// 故采用「读当前值 → 写 +1」。读改写非原子,高并发下可能少计,作为浏览计数可接受。
|
||||||
|
// 任何失败仅记 WARNING 日志并吞掉,绝不影响 get_notes 主流程。
|
||||||
|
void _bump_views(int64_t id) {
|
||||||
|
try {
|
||||||
|
json::object sel;
|
||||||
|
sel["action"] = "select";
|
||||||
|
sel["table"] = "Notes";
|
||||||
|
sel["columns"] = json::array{"views"};
|
||||||
|
sel["where"] = json::object{{"id", id}};
|
||||||
|
sel["limit"] = 1;
|
||||||
|
|
||||||
|
DbResult r = _call_db(sel);
|
||||||
|
if (!r.success || !r.data.is_array() || r.data.as_array().empty())
|
||||||
|
return; // 节点不存在或查询失败:静默跳过
|
||||||
|
|
||||||
|
const auto& note = r.data.as_array()[0];
|
||||||
|
int64_t views = 0;
|
||||||
|
if (note.is_object() && note.as_object().contains("views")
|
||||||
|
&& note.as_object().at("views").is_int64()) {
|
||||||
|
views = note.as_object().at("views").as_int64();
|
||||||
|
}
|
||||||
|
|
||||||
|
json::object upd;
|
||||||
|
upd["action"] = "update";
|
||||||
|
upd["table"] = "Notes";
|
||||||
|
upd["values"] = json::object{{"views", views + 1}};
|
||||||
|
upd["where"] = json::object{{"id", id}};
|
||||||
|
|
||||||
|
DbResult ur = _call_db(upd);
|
||||||
|
if (!ur.success) {
|
||||||
|
_log("WARNING", "dnfs2", "views 自增失败 id=" + std::to_string(id)
|
||||||
|
+ " err=" + ur.error);
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
// _call_db 网络异常会抛 DnfsExp(502),一并捕获,绝不影响主流程
|
||||||
|
_log("WARNING", "dnfs2", std::string("views 自增异常 id=")
|
||||||
|
+ std::to_string(id) + ": " + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Token 验签(通过 gateway /whoami)---------------------------------
|
// ---- Token 验签(通过 gateway /whoami)---------------------------------
|
||||||
|
|
||||||
struct UserInfo {
|
struct UserInfo {
|
||||||
@@ -843,6 +885,12 @@ private:
|
|||||||
return make_json_obj(http::status::internal_server_error, err, req);
|
return make_json_obj(http::status::internal_server_error, err, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打开节点 pid 时观看人数 +1(pid=0 为虚拟根节点,无对应行,跳过)。
|
||||||
|
// best-effort:失败仅记日志,不影响返回结果。
|
||||||
|
if (pid != 0) {
|
||||||
|
_bump_views(pid);
|
||||||
|
}
|
||||||
|
|
||||||
// 解析当前用户(可选):为每个节点附带 can_operate 标记
|
// 解析当前用户(可选):为每个节点附带 can_operate 标记
|
||||||
std::optional<UserInfo> info = std::nullopt;
|
std::optional<UserInfo> info = std::nullopt;
|
||||||
std::string token = extract_token(req);
|
std::string token = extract_token(req);
|
||||||
|
|||||||
Reference in new issue
Block a user