一.原因
近期在做短信与邮件通知系统。使用到了这一块。例如,当订单完成以后进行邮件短信的通知。虽然可以采用直接调用接口的方式实现,但有几个原因让我希望使用条件触发的方式
1.由于系统中支持线上线下以及代充值等多种方式,所以在多个地方订单改变状态。这样就让触发通知的代码凌乱分布。
2.系统将来扩建,需要新增加接口。则需要新增加调用的代码。
总而言之,直接调用将会增加维护难度。因此准备在订单的状态首次被置为支付成功时候进行短信与邮件的通知。
二.模块需求
短信与邮件的通知不能影响内部系统的运行,但由于是远程调用,时间是未知的。为了满足需求,模块必须满足以下条件
1.短信,邮件的发送使用线程
2.触发必须在数据库事务完成的状态下进行。
三.sqlalchemy的监听类型
sqlalchemy是我们采取的数据库后台python接口。它的事务触发机制包括
1.orm中的字段改变触发
2.orm对象状态改变触发
3.session改变触发
4.连接池事件,连接事件
四.具体实现
1.方式一
一开始我采用了倾听字段改变的事件
@event.listens_for(Order.status,"set",retval=True) def pay_success_reminder(target, value, oldvalue, initiator): ''' 已经充值成功,给用户发送提醒 :return: ''' if value==oldvalue:return value if value=="pay_success":#支付成功 try:
...... pass except Exception as e: pass return value
这个事件在订单状态改变的时候就会触发,不幸的事,他触发在事务当中,当然,如果在其中另外线程去执行发送短信与邮件应该也无妨。但处于某些原因我希望在事务处理完成之后调用。
2.方式二
另外一种方式,监听对象的更新事件,并在更新事件处理中监听这个session commit事件。这样就能够完成commit之后对该对象的处理。
def pay_success_reminder2(order_no): def func(session): try: ...... pass except Exception as e: pass return True return func @event.listens_for(Order, 'after_insert',raw=True) @event.listens_for(Order, 'after_update',raw=True) def pay_success_reminder1(mapper, connection, target):# ''' 已经充值成功,给用户发送提醒 :return: ''' if target.dict["status"]!=target.committed_state["status"] and target.dict["status"]=="pay_success": event.listen(target.session,"after_commit",pay_success_reminder2(target.dict["order_no"]) )
如果需要将某些信息传递给session状态监听处理函数,则在需要使用函数包装的方式传递变量。