模拟第三方信息存入数据库并进行消息推送
This commit is contained in:
@@ -161,15 +161,29 @@ async function start() {
|
||||
async function claimNotification(id) {
|
||||
try {
|
||||
const body = { status_code: 'processing', updated_at: new Date().toISOString() }
|
||||
// 仅当当前仍为 pending 或 retrying 且到期时,才抢占
|
||||
// 仅当当前仍为 pending 或 retrying 且到期时,才抢占。
|
||||
// 为了避免在 PATCH 中使用复杂 or= 逻辑树导致匹配失败,这里拆成两次尝试。
|
||||
const now = new Date().toISOString()
|
||||
const path = `express_notifications?id=eq.${encodeURIComponent(id)}&or=(status_code.is.null,and(status_code.eq.retrying,or(next_attempt_at.is.null,next_attempt_at.lte.${encodeURIComponent(now)})))`
|
||||
const resp = await supaFetch(path, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Prefer: 'return=representation' }, body: JSON.stringify(body) })
|
||||
if (!resp.ok) return null
|
||||
const j = await resp.json().catch(() => null)
|
||||
if (Array.isArray(j)) return j.length > 0 ? j[0] : null
|
||||
if (!j || !j.id) return null
|
||||
return j
|
||||
const attempts = [
|
||||
// pending: status_code IS NULL
|
||||
`express_notifications?id=eq.${encodeURIComponent(id)}&status_code=is.null`,
|
||||
// retrying: status_code='retrying' and next_attempt_at is null or <= now
|
||||
`express_notifications?id=eq.${encodeURIComponent(id)}&status_code=eq.retrying&or=(next_attempt_at.is.null,next_attempt_at.lte.${encodeURIComponent(now)})`
|
||||
]
|
||||
|
||||
for (const path of attempts) {
|
||||
const resp = await supaFetch(path, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Prefer: 'return=representation' }, body: JSON.stringify(body) })
|
||||
if (!resp.ok) continue
|
||||
const j = await resp.json().catch(() => null)
|
||||
if (Array.isArray(j)) {
|
||||
if (j.length > 0) return j[0]
|
||||
// debug hint: matched 0 rows
|
||||
try { console.log('claimNotification: updated 0 rows for', path) } catch (e) {}
|
||||
continue
|
||||
}
|
||||
if (j && j.id) return j
|
||||
}
|
||||
return null
|
||||
} catch (e) {
|
||||
console.warn('claimNotification error', e)
|
||||
return null
|
||||
@@ -192,7 +206,17 @@ async function start() {
|
||||
const txt = await resp.text().catch(() => '')
|
||||
let json
|
||||
try { json = JSON.parse(txt) } catch (e) { json = { statusText: txt } }
|
||||
return { ok: resp.ok, status: resp.status, body: json }
|
||||
// Treat explicit business-level failures as errors even when HTTP is 2xx.
|
||||
// Many cloud functions return `{ ok: false, ... }` with HTTP 200.
|
||||
const businessOk = (() => {
|
||||
if (!json || typeof json !== 'object') return true
|
||||
if (json.ok === false) return false
|
||||
// uniCloud/uni-push demo often returns { errCode: 0|..., errMsg: '...' }
|
||||
if (typeof json.errCode === 'number') return json.errCode === 0
|
||||
if (typeof json.errCode === 'string' && json.errCode.trim() !== '') return json.errCode === '0'
|
||||
return true
|
||||
})()
|
||||
return { ok: resp.ok && businessOk, httpOk: resp.ok, status: resp.status, body: json }
|
||||
} catch (e) {
|
||||
return { ok: false, error: String(e) }
|
||||
}
|
||||
@@ -239,7 +263,13 @@ async function start() {
|
||||
if (CLOUD_FUNC_URL) {
|
||||
const calls = await Promise.all(targets.map(cid => invokeCloudFuncForCid(CLOUD_FUNC_URL, PUSH_TOKEN, cid, notification && notification.title, notification && notification.body, payload)))
|
||||
for (const c of calls) {
|
||||
if (!c.ok) { allOk = false; lastNote = (c.error || JSON.stringify(c.body)).toString().substring(0, 1000); break }
|
||||
if (!c.ok) {
|
||||
allOk = false
|
||||
const err = c.error ? String(c.error) : ''
|
||||
const bodyStr = c.body ? JSON.stringify(c.body) : ''
|
||||
lastNote = `cloudfunc failed: status=${c.status || ''} httpOk=${c.httpOk === true} ${err} ${bodyStr}`.trim().substring(0, 1000)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allOk = false
|
||||
|
||||
Reference in New Issue
Block a user