diff --git a/dingtalk.py b/dingtalk.py index 4b45e82..ad3066c 100644 --- a/dingtalk.py +++ b/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 请求到达 ==============') diff --git a/package.sh b/package.sh index e4eeae5..bb6beae 100755 --- a/package.sh +++ b/package.sh @@ -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' \ diff --git a/routes.py b/routes.py index 6ec160e..f659f3c 100644 --- a/routes.py +++ b/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) diff --git a/start_dingtalk.sh b/start_dingtalk.sh old mode 100644 new mode 100755 diff --git a/templates/dingtalk_entry.html b/templates/dingtalk_entry.html index 7f5460c..20fca25 100644 --- a/templates/dingtalk_entry.html +++ b/templates/dingtalk_entry.html @@ -55,7 +55,7 @@
+
正在跳转...