具体的教程请看官方文档:https://mongoosejs.com/docs/transactions.html
-
我这里简单总结下我项目是咋用的,我这里有个编辑设备的接口,要更新两个doc,设备doc,以及另外一个一对一的doc
-
忽略我代码中的数学运算,首先我的equip文档是没有使用到事务的,这为我后面埋了一个坑,我首先判断这个设备文档,以及和设备文档形成一对一的allZone文档的关联id是否存在于我的设备doc中,如果有问题就报错了,没有问题就开始事务了
-
然后咔咔咔一顿计算,我要开始save数据了,我就记着官方有句话是save是默认带着session去save的,然后我就偷懒没加save({session}),代码最后我故意抛出异常,结果我的equip doc还是保存成功了,把我郁闷的,后面才留意一段话,就是首先你的文档是通过session find的,那你才能在save的时候直接引用到session,所以在equip.save时我们要手动加上{session},所以最终的结果是别偷懒了,都加上就不会错
写法1
/**
* POST
* 编辑设备
* api/equip/editEquip
* id 设备id
*/
export async function editEquip(req: any, res: any) {
if (!req.body.id) {
return mongooseTool.sendRes(false, '请传入设备id', res)
}
const equip = await EquipmentModel.findById(req.body.id)
// 如果未查询到设备,或者设备表中找不到关联填写区的ID(可能是人为误删之类)
// 都要报错
if(!equip){
return mongooseTool.sendRes(false, '找不到该设备', res)
}else if(!equip.allZoneID){
return mongooseTool.sendRes(false, '数据出错', res)
}
const session = await mongoose.connection.startSession()
// 开始事务处理
await session.withTransaction(async () => {
equip.unitCostTarget = req.body.unitCostTarget ?? 0
const allZone = await AllZoneModel.findById(equip.allZoneID).session(session)
allZone.AutoWHInnerleft = req.body.AutoWHInnerleft ?? 0
allZone.BSInner = req.body.BSInner ?? 0
allZone.ManualStations = req.body.ManualStations ?? 0
// 设备总数=填写区域数量累加
equip.num = allZone.AutoWHInnerleft + allZone.BSInner + allZone.ManualStations
// Unit Cost Target>0时,GPA差距=(Unit Cost Target-单位成本)/单位成本;
// Unit Cost Target=0时,CPA差距为空
// 合计成本目标:假如Unit Cost Target > 0,等于Unit Cost Target*物料总数,假如Unit Cost Target <=0,等于单位成本 *总数。
if(equip.unitCostTarget > 0 ){
equip.gap = equip.unitCost > 0 ? compute.Div(compute.Sub(equip.unitCostTarget, equip.unitCost), equip.unitCost, 2) : 0
equip.costTarget = compute.Mul(equip.unitCostTarget, equip.num)
}else {
equip.gap = 0
equip.costTarget = compute.Mul(equip.unitCost, equip.num)
}
equip.cost = equip.costTarget
// If you get a Mongoose document from findOne() or find() using a session,
// the document will keep a reference to the session and use that session for save().
// 由于equip不是通过session查询到的,所以如果不加{session},那么equip.save不会被认为是事务的一部分,我们需要手动加上
await equip.save({ session })
// allZone是通过session查询到的,所以可以不加{session},文档会自动使用该session来执行save操作,被认为是事务的一部分
await allZone.save()
// 故意报个错,会进入到catch,不会执行成功的语句,最终执行endSession
// throw new Error('Oops')
mongooseTool.sendRes(true, equip, res)
}).catch(err => {
console.log(`editEquip接口报错了 ${err.message}`)
mongooseTool.sendRes(false, '编辑设备失败', res)
})
session.endSession();
}
}
写法2
/**
* POST
* 获取填写区域
* api/equip/getInputs
* id 设备id
*/
export async function getInputs(req: any, res: any) {
if (!req.body.id) {
return mongooseTool.sendRes(false, '请传入必要参数', res)
}
const session = await mongoose.connection.startSession()
session.startTransaction()
try {
const equip = await EquipmentModel.findById(req.body.id).session(session)
// 如果未查询到设备则报错
if (!equip) {
return mongooseTool.sendRes(false, '找不到该设备', res)
}
// 如果没有需要新增一个
if (!equip.allZoneID) {
const allZone = new AllZoneModel({
_id: new mongoose.Types.ObjectId(),
AutoWHInnerleft: 0,
BSInner: 0,
ManualStations: 0
})
await allZone.save({ session })
equip.allZoneID = allZone._id
await equip.save({ session })
// 故意报个错
// throw new Error('Oops')
await session.commitTransaction()
mongooseTool.sendRes(true, allZone, res)
} else {
// 如果找到了直接返回
const allZone = await AllZoneModel.findById(equip.allZoneID).exec()
mongooseTool.sendRes(true, allZone, res)
}
} catch (err) {
mongooseTool.sendRes(false, '获取设备失败', res)
} finally {
session.endSession()
}
}
网友评论