56 lines
2.2 KiB
Python
56 lines
2.2 KiB
Python
"""独立业务事件入队;调用方持有事务,唯一键阻止多实例重复事件。"""
|
|
import hashlib
|
|
import json
|
|
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
from app.core.config import settings
|
|
from app.core.utils import generate_uuid
|
|
from app.models.models import SysUser
|
|
from app.models.wechat_notice import WechatNotice
|
|
|
|
|
|
def event_key(*, tenant_no, appid, business_type, business_id, event_id, kind, recipient_id):
|
|
return hashlib.sha256(json.dumps([
|
|
tenant_no, appid, business_type, business_id, event_id, kind, recipient_id,
|
|
], ensure_ascii=False).encode()).hexdigest()
|
|
|
|
|
|
def enqueue_event(db, *, tenant_no, business_type, business_id, event_id, kind,
|
|
recipient_id, template_id, payload, page='', actor_id='system',
|
|
invalid_error=None):
|
|
if not settings.WECHAT_OA_ENABLED:
|
|
return None
|
|
db.flush()
|
|
user = db.query(SysUser).filter_by(
|
|
id=recipient_id, tenant_no=tenant_no, status='1', del_flag=0,
|
|
).first()
|
|
if not user:
|
|
return None
|
|
key = event_key(
|
|
tenant_no=tenant_no, appid=settings.WECHAT_OA_APPID,
|
|
business_type=business_type, business_id=business_id, event_id=event_id,
|
|
kind=kind, recipient_id=recipient_id,
|
|
)
|
|
if db.query(WechatNotice.id).filter_by(event_key=key).first():
|
|
return None
|
|
error = invalid_error or ('通知模板未配置' if not template_id else None)
|
|
row = WechatNotice(
|
|
id=generate_uuid(), tenant_no=tenant_no, appid=settings.WECHAT_OA_APPID,
|
|
business_type=business_type, business_id=business_id, cycle_id=event_id,
|
|
event_key=key, kind=kind, recipient_id=recipient_id, template_id=template_id,
|
|
payload=json.dumps(payload, ensure_ascii=False), page=page or '',
|
|
status='failed' if error else 'pending', last_error=error,
|
|
create_by=actor_id, create_dept_id=user.dept_id or '',
|
|
)
|
|
try:
|
|
with db.begin_nested():
|
|
db.add(row)
|
|
db.flush()
|
|
except IntegrityError:
|
|
# MySQL REPEATABLE READ 用当前读确认竞争者已提交的相同事件。
|
|
if db.query(WechatNotice.id).filter_by(event_key=key).with_for_update().first():
|
|
return None
|
|
raise
|
|
return row
|