情况是这样的,今天测试反馈一个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。
