TreasuryFlow:为小企业提供隔夜银行流水与账簿核对工具
来源标题:Show HN: TreasuryFlow, an overnight bank-vs-books check for small businesses
TreasuryFlow 在 Show HN 上发布,帮助小企业将银行流水与内部账簿进行隔夜自动核对。其匿名演示在首次解析到生成示例 JSON 的整个启动过程中设有一个 10 秒的截止时间,超时则展示错误提示。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
示例看板未能加载 请重试。 重试 返回 TreasuryFlow
} catch (_) {}
// 匿名演示启动只有一个截止时间,从首次解析到示例 JSON 加载完成。
// 这段代码位于 portal 模块之前,不需要远程辅助脚本。
var _demoBootAttempt = null;
var _demoBootGeneration = 0;
function _newDemoBootAttempt() {
var previous = _demoBootAttempt;
if (previous) {
previous.failed = true;
clearTimeout(previous.timer);
previous.controller.abort();
if (previous.reject) previous.reject(new Error('Demo boot superseded'));
}
var attempt = {
generation: ++_demoBootGeneration, started: false, failed: false,
controller: new AbortController(), reject: null, timer: null
};
_demoBootAttempt = attempt;
attempt.timer = setTimeout(function () {
if (_demoBootAttempt !== attempt) return;
attempt.failed = true;
attempt.controller.abort();
if (attempt.reject) attempt.reject(new Error('Demo boot deadline exceeded'));
_renderDemoBootError();
}, 10000);
return attempt;
}
function _beginDemoBootAttempt() {
var attempt = _demoBootAttempt;
// 初始解析的看门狗负责初始截止时间;bootstrap 不能重置它。
if (attempt && !attempt.started) {
if (attempt.failed) return null;
} else {
attempt = _newDemoBootAttempt();
attempt.started = true;
}
return attempt;
}
function _finishDemoBootAttempt(attempt) {
if (_demoBootAttempt !== attempt || attempt.failed) return false;
clearTimeout(attempt.timer);
attempt.reject = null;
return true;
}
(function demoBootSeam() {
try {
if (new URLSearchParams(window.location.search).get('demo') === 'true') {
document.documentElement.classList.add('tf-demo-boot');
_newDemoBootAttempt();
}
} catch (e) { /* 尽力而为——最坏情况下认证浮层照旧显示 / }
})();
// ── R-1074:演示模式绝不沿用已存储的会话 ──────────────────────
// ?demo=true 过去会保留已登录浏览器中存储的 token,于是页面变成了
// 该账号的“尚无银行”预览:所有
// _demoMode && !token 判断都会走实时读取,示例的 Jobs 页面
// 也会调用该账号自己的端点。对于本次页面浏览,已存储的
// 会话被视为不存在(即访客窗口看到的页面),并且无法
// 被移除;它也绝不会被更改。从演示中进行的登录会存储一个新的
// token,从而结束这层保护。
(function demoSessionShield() {
try {
if (new URLSearchParams(window.location.search).get('demo') !== 'true') return;
var KEY = 'tf_portal_token', P = Storage.prototype, get = P.getItem, set = P.setItem, del = P.removeItem;
if (!get.call(localStorage, KEY)) return;
var on = true, mine = function (s, k) { return on && s === localStorage && String(k) === KEY; };
P.getItem = function (k) { return mine(this, k) ? null : get.apply(this, arguments); };
P.removeItem = function (k) { if (!mine(this, k)) return del.apply(this, arguments); };
P.setItem = function (k) { if (mine(this, k)) on = false; return set.apply(this, arguments); };
window._tfDemoShieldsSession = function () { return on; };
} catch (e) { / 存储不可用:没有任何已存储内容需要保护 / }
})();
// ── 登录入口预绘制接缝(收尾波次,2026-06-09)──────────────
// 这是 ?signin=1 对应演示接缝的镜像:回访客户绝不能
// 在 _applySigninDoor 重写卡片之前看到试用获客文案闪现。
// 在首次绘制前打上 .tf-signin-boot 标记;下面的 CSS
// 会隐藏注册 H1/副标题/CTA,直到 _applySigninDoor(完整
// 应用逻辑)替换文案并清除该类。
// 已有会话在 /me 返回之前先等待,然后再选择已认证
// 界面或登录界面。已存储的 token 本身从不授予访问权限。
(function sessionBootSeam() {
try {
if ((localStorage.getItem('tf_portal_token') || localStorage.getItem('tf_device_hint')) &&
new URLSearchParams(window.location.search).get('demo') !== 'true') {
document.documentElement.classList.add('tf-session-boot');
}
} catch (e) { / 存储不可用:普通登录界面照常显示 / }
})();
(function signinBootSeam() {
try {
if (new URLSearchParams(window.location.search).get('signin') === '1') {
document.documentElement.classList.add('tf-signin-boot');
}
} catch (e) { / 尽力而为——最坏情况下试用卡片照旧闪现 / }
})();
// 邀请接受入口预绘制接缝(Brief 8):队友通过
// ?invite=TOKEN 进入时,绝不能在看到预览请求重写卡片之前
// 看到试用注册文案闪现。隐藏方式与登录启动相同。
(function inviteBootSeam() {
try {
if (new URLSearchParams(window.location.search).get('invite')) {
document.documentElement.classList.add('tf-signin-boot');
}
} catch (e) { / 尽力而为——最坏情况下试用卡片照旧闪现 / }
})();
// 客户邀请入口(?bk_invite=TOKEN):事务所的客户是在
// 事务所品牌下进入的,因此卡片上的首页标题在那里绝不显示,
// 哪怕一帧也不行(创始人 2026-11-05 允许在账户
// 尚不存在时显示,但这个入口属于事务所)。_applyClientInviteDoor() 会写入
// 该入口自己的文案并清除该类。
(function clientInviteBootSeam() {
try {
if (new URLSearchParams(window.location.search).get('bk_invite')) {
document.documentElement.classList.add('tf-client-invite-boot');
}
} catch (e) { / 不依赖存储;最坏情况下标题闪现 */ }
})();
// 提前点击接缝(R-0888):注册按钮早在定义其处理程序的
// 主脚本解析完成之前就已绘制出来(Fast 3G 下 1.0 秒出现,
// 14.7 秒才可用,2026-11-07 走查),此时点击会抛出“未
// 定义”并且毫无反应。在真正的函数替换桩函数并且页面
// 脚本已运行(tfTrack、tfFirstUtm 在主代码块之后)之前,
// 首次点击会将其按钮置灰;之后若按钮仍显示,则重放一次。
// 60 秒后它放弃并重新启用。由
// test_portal_first_paint_browser.py 固定验证。
(function earlyTapSeam() {
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。