情况是这样的,今天测试反馈一个bug,说是用户上课签到完成后为嘛奖励的积分没有好好反馈过来,用户的积分还是保持着原样。
其实,对用户积分的计算我是放在一个定时任务中的,任务中遍历课程和用户,只要课程都有效签到的用户才奖励对应的积分,都未有效签到则扣除。
然后我调用任务日志,发现了这句:
2018-12-05 16:01:28-执行【签到积分计算】报名惩罚发生异常:更新用户积分失败【…….】
很显然,就是这个用户在写数据时出了问题
然后一个人的问题影响了所有人….
这个肯定是要不得的
罢了,先看下我原来的代码
//.....
# 开启事务
$transaction = Yii::$app->db->beginTransaction();
try {
# 更新该培训所有上课用户的未到场次数
if (!empty($endTrains)) {
/** @var Train $items */
foreach ($endTrains as $items) {
# 更新培训用户未到场次数
$sql = "UPDATE tb_users_train SET not_num = should_num - already_num WHERE train_id = {$items->train_id}";
Yii::$app->db->createCommand($sql)->execute();
# 只有有积分规则的才能更新用户积分情况
if ($items->point_rule > 0) {
//$trainIds[] = $item->train_id;
# 更新用户积分情况:全到场加分,一次都未到场减分(有效签到一次为一次到场)
## 必参到场奖励
$must_sign_honor = isset($pointsRules[$items->point_rule]['must_sign_honor']) ? round($pointsRules[$items->point_rule]['must_sign_honor']) : 0;
if ($must_sign_honor > 0) {
//这有一大坨代码...
#更改用户积分并写入用户-积分记录表
$change_res = MCommonFunc::ChangeUserPoints($item->user_id, 1, $must_sign_honor, $item->org_id, 1, $items->train_id, 0, $items->train_name, $items->train_type, "校内课程{$items->train_name}签到奖励");
if ($change_res === false) {
$transaction->rollBack();
$errMessage = "更新用户积分失败【train_id:{$items->train_id},train_name:{$items->train_name},user_id:{$item->user_id}";
LogUtils::writeTask('执行【积分任务】发生异常:'.$errMessage);
return false;
}
#积分变动:通知消息入库
//这有一大坨代码...
## 报名到场奖励
//这有一大坨代码...
if (!$item->update()) {
$transaction->rollBack();
$errMessage = "更新用户培训积分状态失败【train_id:{$items->train_id},train_name:{$items->train_name},user_id:{$item->user_id}";
LogUtils::writeTask('执行【积分任务】发生异常:'.$errMessage);
return false;
}
//这有一大坨代码...
}
}catch (\Exception $exception){
$transaction ->rollBack();
echo "异常:".$exception->getMessage();
return false;
}
$transaction ->commit();
//.....
由此可见,循环中某个用户出错,就会直接return掉,后面的用户就没机会执行了
那么要解决这种问题,肯定不能用用return,应该直接用continue;
于是我所有的return false 改为 continue。
结果还是报错了:
2018-12-05 16:01:25-执行【计算积分任务】发生异常:Failed to commit transaction: transaction was inactive.
查阅资料知道是事务的原因。在同事的帮助下说我每个事务都应该重新弄,意思是每个循环都得独立一个事务。然后在他的建议下,终于解决问题。
# 更新该培训所有上课用户的未到场次数
if (!empty($endTrains)) {
/** @var Train $items */
foreach ($endTrains as $items) {
# 开启事务
$transaction = Yii::$app->db->beginTransaction();
try{
//计算积分的处理
$res = self::dealPoints($items,$pointsRules);
if(!$res){
$transaction->rollBack();
continue;
}
else{
$transaction ->commit();
}
}catch (\Exception $exception){
$transaction ->rollBack();
echo "异常:".$exception->getMessage();
return false;
}
}
}
//...
这样一来也简单清晰得多,也不用那么多rollback。