已完成全部功能
This commit is contained in:
112
dingtalk.py
112
dingtalk.py
@@ -5,6 +5,11 @@ from flask import Blueprint, render_template, jsonify, current_app, request
|
||||
|
||||
dingtalk_bp = Blueprint('dingtalk', __name__)
|
||||
|
||||
# 延迟导入避免循环引用
|
||||
def get_user_model():
|
||||
from models import User
|
||||
return User
|
||||
|
||||
def get_dingtalk_config():
|
||||
print('[后端] [1] ============== get_dingtalk_config 开始 ==============')
|
||||
app_key = current_app.config.get('DINGTALK_APP_KEY', '')
|
||||
@@ -166,6 +171,113 @@ def get_dept_name(dept_id):
|
||||
print(f'[后端] [5] 异常:', e)
|
||||
return None
|
||||
|
||||
def send_dingtalk_card_to_user(user_id, title, content, jump_url):
|
||||
"""发送钉钉消息卡片给指定用户"""
|
||||
print(f'[后端] [N] ============== send_dingtalk_card_to_user 开始, user_id={user_id} ==============')
|
||||
access_token = get_access_token()
|
||||
if not access_token:
|
||||
print('[后端] [N] 获取 access_token 失败')
|
||||
return False
|
||||
|
||||
url = f'https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token={access_token}'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
agent_id = current_app.config.get('DINGTALK_AGENT_ID', '')
|
||||
data = {
|
||||
'agent_id': int(agent_id) if agent_id else 0,
|
||||
'userid_list': user_id,
|
||||
'msg': {
|
||||
'msgtype': 'link',
|
||||
'link': {
|
||||
'title': title,
|
||||
'text': content,
|
||||
'picUrl': 'https://img.alicdn.com/tfs/TB1NwmBkeL2gK0jSZPhXXahviXa-72-72.png',
|
||||
'messageUrl': jump_url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print(f'[后端] [N] 发送消息卡片: {data}')
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=data, headers=headers, timeout=10)
|
||||
result = response.json()
|
||||
print(f'[后端] [N] 钉钉响应:', result)
|
||||
if result.get('errcode') == 0:
|
||||
print(f'[后端] [N] 消息发送成功')
|
||||
return True
|
||||
else:
|
||||
print(f'[后端] [N] 消息发送失败: {result.get("errmsg", "未知错误")}')
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f'[后端] [N] 异常:', e)
|
||||
return False
|
||||
|
||||
def notify_admins_new_demand(demand, base_url):
|
||||
"""通知所有管理员有新需求提交"""
|
||||
print(f'[后端] [N] ============== notify_admins_new_demand 开始 ==============')
|
||||
|
||||
# 获取所有管理员用户
|
||||
from models import User
|
||||
admins = User.query.filter_by(role='admin').all()
|
||||
print(f'[后端] [N] 找到 {len(admins)} 个管理员')
|
||||
|
||||
if not admins:
|
||||
print('[后端] [N] 没有管理员用户,跳过通知')
|
||||
return
|
||||
|
||||
# 构建消息内容
|
||||
title = f'📋 新需求提交:{demand.title}'
|
||||
content = f'''需求标题:{demand.title}
|
||||
|
||||
提交者:{demand.contact or "未知"}
|
||||
|
||||
分会:{demand.branch}
|
||||
|
||||
点击查看并回答'''.strip()
|
||||
|
||||
# 回答页面的跳转链接
|
||||
jump_url = f'{base_url}/demand/{demand.id}/answer'
|
||||
|
||||
success_count = 0
|
||||
for admin in admins:
|
||||
if admin.dingtalk_userid:
|
||||
if send_dingtalk_card_to_user(admin.dingtalk_userid, title, content, jump_url):
|
||||
success_count += 1
|
||||
|
||||
print(f'[后端] [N] 消息发送完成,成功: {success_count}/{len(admins)}')
|
||||
|
||||
def notify_asker_answer(demand, base_url):
|
||||
"""通知提问者问题已被回答"""
|
||||
print(f'[后端] [N] ============== notify_asker_answer 开始 ==============')
|
||||
|
||||
# 获取提问者
|
||||
User = get_user_model()
|
||||
asker = User.query.get(demand.user_id)
|
||||
if not asker:
|
||||
print('[后端] [N] 未找到提问者,跳过通知')
|
||||
return
|
||||
|
||||
if not asker.dingtalk_userid:
|
||||
print(f'[后端] [N] 提问者 {asker.username} 没有钉钉userid,跳过通知')
|
||||
return
|
||||
|
||||
# 构建消息内容
|
||||
title = f'✅ 您的需求已被回答:{demand.title}'
|
||||
content = f'''需求标题:{demand.title}
|
||||
|
||||
管理员已回复您的问题,点击查看详情'''.strip()
|
||||
|
||||
# 跳转链接 - 指向首页
|
||||
jump_url = f'{base_url}' + '/dingtalk'
|
||||
|
||||
print(f'[后端] [N] 准备通知提问者: {asker.username}, dingtalk_userid: {asker.dingtalk_userid}')
|
||||
|
||||
if send_dingtalk_card_to_user(asker.dingtalk_userid, title, content, jump_url):
|
||||
print(f'[后端] [N] 提问者通知发送成功')
|
||||
else:
|
||||
print(f'[后端] [N] 提问者通知发送失败')
|
||||
|
||||
@dingtalk_bp.route('/')
|
||||
def dingtalk_entry():
|
||||
print('[后端] [0] ============== dingtalk_entry 请求到达 ==============')
|
||||
|
||||
@@ -19,7 +19,6 @@ tar -czvf "$OUTPUT_DIR/${PROJECT_NAME}_$(date +%Y%m%d_%H%M%S).tar.gz" \
|
||||
--exclude='.git' \
|
||||
--exclude='.codebuddy' \
|
||||
--exclude='node_modules' \
|
||||
--exclude='*.db' \
|
||||
--exclude='venv' \
|
||||
--exclude='env' \
|
||||
--exclude='.env' \
|
||||
|
||||
11
routes.py
11
routes.py
@@ -4,6 +4,7 @@ from flask_login import login_user, logout_user, login_required, current_user
|
||||
from __init__ import app, db
|
||||
from models import User, Demand, now_shanghai
|
||||
from forms import DemandForm, AnswerForm, LoginForm, RegisterForm
|
||||
from dingtalk import notify_admins_new_demand, notify_asker_answer
|
||||
|
||||
BRANCH_NAMES = {
|
||||
'comprehensive': '综合分会',
|
||||
@@ -125,6 +126,11 @@ def new_demand():
|
||||
)
|
||||
db.session.add(demand)
|
||||
db.session.commit()
|
||||
|
||||
# 通知管理员有新需求提交
|
||||
base_url = request.host_url.rstrip('/').replace('http://', 'https://') + '/requirement-collection'
|
||||
notify_admins_new_demand(demand, base_url)
|
||||
|
||||
flash('需求提交成功', 'success')
|
||||
return redirect(url_for('index'))
|
||||
return render_template('demand_form.html', form=form, title='提交新需求')
|
||||
@@ -164,6 +170,11 @@ def answer_demand(id):
|
||||
demand.answer = form.answer.data
|
||||
demand.answered_at = now_shanghai()
|
||||
db.session.commit()
|
||||
|
||||
# 通知提问者问题已被回答
|
||||
base_url = request.host_url.rstrip('/').replace('http://', 'https://') + '/requirement-collection'
|
||||
notify_asker_answer(demand, base_url)
|
||||
|
||||
flash('回答已保存', 'success')
|
||||
return redirect(url_for('index'))
|
||||
return render_template('answer_form.html', form=form, demand=demand)
|
||||
|
||||
0
start_dingtalk.sh
Normal file → Executable file
0
start_dingtalk.sh
Normal file → Executable file
@@ -55,7 +55,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src="/static/logo.png" alt="logo" class="logo">
|
||||
<img src="{{ url_for('static', filename='logo.png') }}" alt="logo" class="logo">
|
||||
<div class="spinner"></div>
|
||||
<p class="loading">正在跳转...</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user