人!
我正在开发一个类似于https://airtasker.com的应用,用户将任务外包出去。 任务者将竞标任务,并等待用户批准他们的出价。
这些是涉及的集合:
检查给定taskId是否存在事务。 如果用户开始批准出价,则会添加交易。我允许多个任务者完成任务。
如果交易不存在,则应该 添加新的状态,如果达到所需的人力(等待处理),则标记状态正在进行,并更新出价集合以标记接受的出价。
如果存在交易,则应该 检查交易集合中当前批准的列表是否等于人力
如果尚未达到配额人力,请推送新任务并访问出价集合以标记已接受的出价。
如果在最后一个条件之后,已批准的列表已达到配额人力,请将任务标记为关闭,并将交易状态更改为正在进行
但我一直收到此错误:
未捕获(在承诺中)错误:还必须编写在事务中读取的每个文档。 在Transaction.commit(transaction.js:128) 在eval(sync_engine.js:244)
这是我的代码:
const acceptOffer = async (taskerId, taskId, bidId, offer) => {
let bulk
try {
const taskRef = db.collection('tasks').doc(taskId)
const transRef = db.collection('transactions').doc(taskId)
const bidRef = db.collection('bids').doc(bidId)
const fees = solveFees(offer)
bulk = await db
.runTransaction(async t => {
const transdoc = await t.get(transRef)
const taskdoc = await t.get(taskRef)
const manpower = await taskdoc.get('manpower')
let status = 'pending'
if (manpower === 1) {
status = 'ongoing'
}
if (!transdoc.exists) {
t.set(transRef, {
taskId,
status, // pending, ongoing, completed
approved: [
{ taskerId, ...fees }
]
})
t.update(bidRef, {
accepted: true
})
} else {
const approved = await transdoc.get('approved')
if (manpower < approved.length) {
approved.push({ taskerId, ...fees })
t.update(transRef, { approved })
t.update(bidRef, { accepted: true })
if (manpower === approved.length) {
t.update(taskRef, { open: false })
t.update(transRef, { status: 'ongoing' })
}
}
}
})
} catch (e) {
bulk = e
console.log('nag error', e)
throw e
}
if (bulk.success) {
swal('Offer accepted!', '', 'success')
} else {
swal('Oh, no!',
'This task might already be approved',
'error'
)
}
}
我一直被困在这里,因为我不明白交易失败的地方。很感谢任何形式的帮助。
谢谢你!答案 0 :(得分:1)
对于那些遇到同样问题的人,这是我的(hackish)解决方案:
适用于所有条件,
添加一个文档写入(可能是set() update()
或delete()
),对应于我的代码中的每个文档读取:get()
s的使用。
并返回Promise
这是更新后的代码:
// a transaction is added if the user starts to approve offers
// this function allows multiple taskers
const acceptOffer = async (taskerId, taskId, bidId, offer) => {
let bulk
try {
const taskRef = db.collection('tasks').doc(taskId)
const transRef = db.collection('transactions').doc(taskId)
const bidRef = db.collection('bids').doc(bidId)
const fees = solveFees(offer)
bulk = await db
.runTransaction(async t => {
const transdoc = await t.get(transRef)
const taskdoc = await t.get(taskRef)
const manpower = await taskdoc.get('manpower')
// check if a transaction exists with the given taskId
// if it doesn't, then the task doesn't have
// any approved bidders yet
if (!transdoc.exists) {
// check if there is only one manpower required for the task
// mark the status of the transaction 'ongoing' if so
const status = manpower === 1
? 'ongoing' : 'pending'
// add a transaction with the approved tasker
t.set(transRef, {
taskId,
status, // pending, ongoing, completed
approved: [
{ taskerId, ...fees }
]
})
// mark the bid 'accepted'
t.update(bidRef, {
accepted: true
})
// hackish (to prevent firestore transaction errors)
t.update(taskRef, {})
return Promise.resolve(true)
} else { // if a transaction exists with the given taskId
const approved = await transdoc.get('approved')
// check if the current approved list from
// the transactions collection hasn't
// reached the manpower quota yet
if (approved.length < manpower) {
// push new approved bid of the tasker
approved.push({ taskerId, ...fees })
t.update(transRef, { approved })
t.update(bidRef, { accepted: true }) // mark the bid 'accepted'
t.update(taskRef, {}) // hackish
// if, after pushing a new transaction,
// the approved list reached the manpower quota
if (approved.length === manpower) {
t.update(taskRef, { open: false }) // mark the task 'close'
t.update(transRef, { status: 'ongoing' }) // mark the transaction 'ongoing'
t.update(bidRef, {}) // hackish
}
return Promise.resolve(true)
}
return Promise.reject(new Error('Task closed!'))
}
})
} catch (e) {
swal('Oh, no!',
'This task might already be closed',
'error'
)
throw e
}
if (bulk) {
swal('Offer accepted!', '', 'success')
}
}
答案 1 :(得分:0)
我遇到了同样的问题。只要Google不能发送验证错误,而错误信息的错误要大于不允许客户端写入数据的错误(安全规则)。我更喜欢在客户端站点上处理它。因此,例如,我使用事务来验证写入数据时引用的文档仍然可用。 (例如,我已经编写了一个引用order
的{{1}}文档,并希望确保该客户仍然存在。)因此,我必须阅读它,但实际上不需要编写它。 / p>
我想出了一些与nrions解决方案相似的方法,但是尝试使用一种更通用的方法,因此我为runTransaction写了一个包装器。当然,这不是最干净的方法,但也许对其他人有用。
customer