7 Commits
Author SHA1 Message Date
Cheney 308ace8559 ci(android): Rust 缓存 key 升到 v2, 重建含 cargo-ndk/settings.toml 的完整缓存
Android Build & Publish / build-android (push) Successful in 16m7s
2026-11-02 10:28:13 +08:00
Cheney 6d560583ae ci(android): 修复 Rust 缓存命中后 cargo 报 no default toolchain
Android Build & Publish / build-android (push) Successful in 16m27s
2026-11-02 10:11:11 +08:00
jif2.zhang f2c7dae4a8 Merge branch 'dev-0.2' into android-1.0
Android Build & Publish / build-android (push) Failing after 6m23s
2026-10-30 17:17:52 +08:00
jif2.zhang 4ded8e30ed ci(android): 重跑构建 —— runner 磁盘不足已清理 (No space left on device)
Android Build & Publish / build-android (push) Successful in 19m51s
2026-10-26 21:56:34 +08:00
jif2.zhang be70868d0b chore(deps): 同步 package-lock —— tauri CLI 2.12.4 平台包对齐 (修复 npm ci 校验失败)
Android Build & Publish / build-android (push) Failing after 10m36s
2026-10-26 21:39:13 +08:00
jif2.zhang 49583599b3 Merge branch 'dev-0.2' into android-1.0 2026-10-26 21:34:39 +08:00
jif2.zhang 8cfa3802d5 合并 master 到 android-1.0: 引入米家环境监控模块与二维码登录修复, 触发 APK 构建
Android Build & Publish / build-android (push) Successful in 33m2s
2026-10-23 22:24:30 +08:00
4 changed files with 28 additions and 45 deletions
+20 -10
View File
@@ -60,9 +60,13 @@ jobs:
with: with:
path: | path: |
~/.cargo ~/.cargo
~/.rustup/toolchains ~/.rustup
# 工具链升级时 bump 版本号使缓存失效重装 (含 cargo-ndk) # 工具链升级时 bump 版本号使缓存失效重装 (含 cargo-ndk)
key: rust-android-v1-${{ runner.os }} # v2: v1 缓存是从只缓存 toolchains 的旧配置生成的, 缺 cargo-ndk/settings.toml,
# 且缓存命中后不会回写, 所以 bump 一次重建完整缓存。
# 注意: 必须缓存整个 ~/.rustup。只缓存 ~/.rustup/toolchains 会丢掉
# settings.toml (默认 toolchain 记录), 缓存命中时 cargo 无法选择版本。
key: rust-android-v2-${{ runner.os }}
- name: Cache Tauri cargo target - name: Cache Tauri cargo target
uses: https://gitea.com/actions/cache@v4 uses: https://gitea.com/actions/cache@v4
@@ -72,16 +76,22 @@ jobs:
key: tauri-target-android-${{ hashFiles('src-tauri/Cargo.lock') }} key: tauri-target-android-${{ hashFiles('src-tauri/Cargo.lock') }}
restore-keys: tauri-target-android- restore-keys: tauri-target-android-
# ---------- 安装工具链 (仅缓存未命中时) ---------- # ---------- 安装/修复工具链 (缓存命中时也要执行) ----------
- name: Install Rust + Android targets # 缓存命中时不能只是跳过安装: 缓存里可能缺少 ~/.rustup/settings.toml,
if: steps.cache-rust.outputs.cache-hit != 'true' # rustup 代理会报 "no default is configured", 导致 cargo / tauri 全部失败。
# 因此这一步无条件运行, 但都是幂等操作 (已安装时不会重新下载)。
- name: Setup Rust toolchain
run: | run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable if [ ! -x "$HOME/.cargo/bin/rustup" ]; then
echo "$HOME/.cargo/bin" >> $GITHUB_PATH curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
fi
. "$HOME/.cargo/env" . "$HOME/.cargo/env"
# 幂等: stable 已在 ~/.rustup/toolchains 时只补写 settings.toml
rustup default stable
# 只构建 aarch64 (现代 Android 手机); 需要模拟器/x86 设备时再加回其他 target # 只构建 aarch64 (现代 Android 手机); 需要模拟器/x86 设备时再加回其他 target
rustup target add aarch64-linux-android rustup target add aarch64-linux-android
cargo install cargo-ndk --locked command -v cargo-ndk >/dev/null 2>&1 || cargo install cargo-ndk --locked
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Install Android SDK & NDK - name: Install Android SDK & NDK
if: steps.cache-android.outputs.cache-hit != 'true' if: steps.cache-android.outputs.cache-hit != 'true'
@@ -117,9 +127,9 @@ jobs:
- name: Init Android project - name: Init Android project
run: | run: |
# tauri android init 需要 cargo (运行 cargo metadata)。 # tauri android init 需要 cargo (运行 cargo metadata)。
# Rust 工具链缓存命中时 "Install Rust" 步骤被跳过, 不会把 cargo 加进全局 PATH, # Setup Rust toolchain 已把 ~/.cargo/bin 写入 GITHUB_PATH, 这里再 source 一次兜底。
# 因此这里显式加载 cargo 环境 (与 Build release APK 步骤保持一致)。
. "$HOME/.cargo/env" . "$HOME/.cargo/env"
cargo --version
cd src-tauri cd src-tauri
npx tauri android init --ci npx tauri android init --ci
+3 -14
View File
@@ -122,15 +122,11 @@ router.get('/habits', async (req, res, next) => {
const userId = req.user.sub const userId = req.user.sub
const today = toDateStr() const today = toDateStr()
const date = normDate(req.query.date) const date = normDate(req.query.date)
const habits = (await loadActiveHabits(userId)) const habits = await loadActiveHabits(userId)
// 回看历史日期时, 当天还不存在的打卡项不应出现 (与 /calendar 口径一致)
.filter((h) => toDateStr(h.createdAt) <= date)
const ranges = habits.map((h) => periodRange(h.period, date)) const ranges = habits.map((h) => periodRange(h.period, date))
const grouped = await loadRecords(userId, habits, ranges.length ? ranges : [{ start: date, end: date }]) const grouped = await loadRecords(userId, habits, ranges.length ? ranges : [{ start: date, end: date }])
const items = habits.map((h) => buildItem(h, grouped.get(h.id) || new Map(), date, today)) const items = habits.map((h) => buildItem(h, grouped.get(h.id) || new Map(), date, today))
// summary 是"当天进度": 按当日份额 (countOnDate >= expectedPerDay) 统计, const doneCount = items.filter((i) => i.completed).length
// 不能用 completed (周期完成度), 否则历史日期会把同周期后续日期的打卡算进来。
const doneCount = items.filter((i) => i.dayDone).length
res.json({ today, date, items, summary: { total: items.length, done: doneCount } }) res.json({ today, date, items, summary: { total: items.length, done: doneCount } })
} catch (e) { next(e) } } catch (e) { next(e) }
}) })
@@ -295,14 +291,7 @@ router.get('/calendar', async (req, res, next) => {
try { try {
const userId = req.user.sub const userId = req.user.sub
const today = toDateStr() const today = toDateStr()
// 兼容旧版客户端: addMonths 曾把 "YYYY-MM" 算成 "YYYY-MM-01", 请求月份带了日号, const month = MONTH_RE.test(String(req.query.month || '')) ? String(req.query.month) : today.slice(0, 7)
// 旧逻辑判为非法后静默回退到本月, 导致切到上月时看不到任何记录。这里截回月份。
const rawMonth = String(req.query.month || '')
const month = MONTH_RE.test(rawMonth)
? rawMonth
: DATE_RE.test(rawMonth)
? rawMonth.slice(0, 7)
: today.slice(0, 7)
const start = month + '-01' const start = month + '-01'
const end = addDays(monthStartNext(month), -1) const end = addDays(monthStartNext(month), -1)
const habits = await prisma.habit.findMany({ where: { userId }, orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] }) const habits = await prisma.habit.findMany({ where: { userId }, orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] })
+2 -9
View File
@@ -130,14 +130,7 @@ export default function MergedView({ date, onDateChange }) {
} }
} }
// 当日进度 = 当天已打卡项 / 当天打卡项. 用 dayDone 而非 completed: const summary = detail?.summary || { total: 0, done: 0 }
// completed 是"周期目标已达成" (每周/每月项可能在本周期其他天完成), 放到
// 历史日期的标题上会把当天之后、同周期内的打卡也算进来。按 items 现算还能
// 让乐观更新 (打卡/撤销) 立即反映到标题, 不必等 /habits 重新拉取。
const summary = useMemo(
() => ({ total: items.length, done: items.filter((i) => i.dayDone).length }),
[items],
)
const fireBurst = (id) => setBursts((m) => ({ ...m, [id]: (m[id] || 0) + 1 })) const fireBurst = (id) => setBursts((m) => ({ ...m, [id]: (m[id] || 0) + 1 }))
// ---------- 打卡 / 撤销 (乐观更新) ---------- // ---------- 打卡 / 撤销 (乐观更新) ----------
@@ -263,7 +256,7 @@ export default function MergedView({ date, onDateChange }) {
<span className="arrow" onClick={() => setMonth((m) => addMonths(m, -1))}>‹</span> <span className="arrow" onClick={() => setMonth((m) => addMonths(m, -1))}>‹</span>
<b>{fmtMonth(month)}</b> <b>{fmtMonth(month)}</b>
<span className="arrow" onClick={() => setMonth((m) => addMonths(m, 1))}>›</span> <span className="arrow" onClick={() => setMonth((m) => addMonths(m, 1))}>›</span>
<span className="today" onClick={() => { setMonth(today.slice(0, 7)); onDateChange(today) }}>回到今天</span> <span className="today" onClick={() => onDateChange(today)}>回到今天</span>
</div> </div>
<div className="mv-grid mv-wd"> <div className="mv-grid mv-wd">
+3 -12
View File
@@ -25,22 +25,13 @@ export function addDays(s, n) {
return fromUTC(d) return fromUTC(d)
} }
/**
* 月份加减. 同时接受月粒度 "YYYY-MM" 与日粒度 "YYYY-MM-DD":
* - 月粒度必须原样返回 "YYYY-MM", 不能变成 "YYYY-MM-01" —— 否则
* `/checkin/calendar?month=2026-10-01` 会被服务端 MONTH_RE 判为非法并回退到当月,
* 日历切到上个月时就会"看不到任何记录"。
* - 日粒度保留原日号, 超出目标月天数时收敛到月末。
*/
export function addMonths(s, n) { export function addMonths(s, n) {
const str = String(s) const [y, m] = String(s).split('-').map(Number)
const [y, m, d] = str.split('-').map(Number)
const day = d || 1
const first = new Date(Date.UTC(y, (m || 1) - 1 + n, 1)) const first = new Date(Date.UTC(y, (m || 1) - 1 + n, 1))
const day = Number(String(s).slice(8, 10)) || 1
const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate() const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate()
first.setUTCDate(Math.min(day, lastDay)) first.setUTCDate(Math.min(day, lastDay))
const out = fromUTC(first) return fromUTC(first)
return str.length === 7 ? out.slice(0, 7) : out
} }
/** 周一为一周第一天 */ /** 周一为一周第一天 */