diff --git a/admin/dashboard/blueprints/message_push.py b/admin/dashboard/blueprints/message_push.py
index aca9574..59cae3e 100644
--- a/admin/dashboard/blueprints/message_push.py
+++ b/admin/dashboard/blueprints/message_push.py
@@ -8,6 +8,8 @@ from datetime import datetime
from flask import Blueprint, render_template, jsonify, request, current_app, session
from pathlib import Path
from werkzeug.utils import secure_filename
+
+from wechat_ipad.models.appmsg_xml import LINK_XML_NORMAL
from .auth import login_required
from loguru import logger
@@ -268,32 +270,49 @@ def api_preview_task(task_id):
preview_user = session.get('username')
if not preview_user:
return jsonify({"success": False, "error": "未登录或会话已过期"}), 401
-
+
# 获取预览接收者并解析JSON
preview_recipients_str = task.get("preview_recipients", "[]")
try:
preview_recipients = json.loads(preview_recipients_str)
except json.JSONDecodeError:
return jsonify({"success": False, "error": "预览接收者格式错误"}), 400
-
+
if not preview_recipients:
return jsonify({"success": False, "error": "未设置预览接收者"}), 400
-
+
# 为每个接收者发送预览消息
for recipient in preview_recipients:
try:
# 发送文本消息
if task.get('content_text'):
send_message_in_thread(server.client.send_text_message, recipient, task['content_text'])
-
+
# 发送图片消息
if task.get('content_image'):
send_message_in_thread(server.client.send_image_message, recipient, Path(task['content_image']))
-
+
# 发送链接消息
if task.get('content_link'):
- send_message_in_thread(server.client.send_link_message, recipient, task['content_link'])
-
+ try:
+ link_data = json.loads(task['content_link'])
+
+ # content_link json 读取内容
+ xml_content = f"{LINK_XML_NORMAL}".format(title=link_data.get('title', ''),
+ des=link_data.get('des', ''),
+ url=link_data.get('url', ''),
+ thumburl=link_data.get('thumburl', '')
+ )
+
+ send_message_in_thread(
+ server.client.send_link_xml_message,
+ xml_content,
+ recipient
+ )
+ except json.JSONDecodeError:
+ logger.error(f"解析链接内容失败: {task['content_link']}")
+ continue
+
# # 发送小程序消息
# if task.get('content_miniprogram'):
# miniprogram = task['content_miniprogram']
@@ -347,17 +366,17 @@ def api_statistics():
try:
# 获取任务数据库实例
db = current_app.dashboard_server.task_db
-
+
# 获取各种状态的任务数量
total = db.get_tasks_count()
scheduled = db.get_tasks_count_by_status('scheduled')
paused = db.get_tasks_count_by_status('paused')
completed = db.get_tasks_count_by_status('completed')
failed = db.get_tasks_count_by_status('failed')
-
+
# 获取今日任务数量
today = db.get_tasks_count_by_date(datetime.now().strftime('%Y-%m-%d'))
-
+
return jsonify({
"success": True,
"data": {
@@ -382,28 +401,28 @@ def upload_file():
'success': False,
'message': '没有文件'
})
-
+
file = request.files['file']
if file.filename == '':
return jsonify({
'success': False,
'message': '没有选择文件'
})
-
+
if file and allowed_file(file.filename):
# 生成安全的文件名
filename = secure_filename(file.filename)
# 生成唯一文件名
unique_filename = f"{uuid.uuid4().hex}_{filename}"
-
+
# 确保上传目录存在
upload_folder = os.path.join(current_app.root_path, 'static', 'uploads')
os.makedirs(upload_folder, exist_ok=True)
-
+
# 保存文件
file_path = os.path.join(upload_folder, unique_filename)
file.save(file_path)
-
+
# 返回文件的绝对路径
return jsonify({
'success': True,
@@ -411,7 +430,7 @@ def upload_file():
'url': file_path # 返回绝对路径
}
})
-
+
return jsonify({
'success': False,
'message': '不支持的文件类型'
@@ -431,17 +450,17 @@ def audit_task(task_id):
'success': False,
'message': '任务不存在'
})
-
+
# 检查任务状态
if task['status'] != 'draft':
return jsonify({
'success': False,
'message': '只能审核草稿状态的任务'
})
-
+
# 更新任务状态为已排期
db.update_task(task_id, {'status': 'scheduled'})
-
+
# 记录操作日志
db.log_task_action({
'log_id': f"log_{datetime.now().strftime('%Y%m%d%H%M%S')}",
@@ -450,7 +469,7 @@ def audit_task(task_id):
'user_id': session.get('user_id'),
'changes': {'status': 'scheduled', 'action': 'audit'}
})
-
+
return jsonify({
'success': True,
'message': '审核成功'
diff --git a/admin/dashboard/templates/message_push_management.html b/admin/dashboard/templates/message_push_management.html
index 9eddad1..c935ab5 100644
--- a/admin/dashboard/templates/message_push_management.html
+++ b/admin/dashboard/templates/message_push_management.html
@@ -294,8 +294,26 @@
+
+
+
+
+
+
-
+
+
+
+
+ 点击上传
+ 只能上传jpg/png文件,且不超过2MB
+
@@ -377,7 +395,12 @@ new Vue({
groups: [],
content_text: '',
content_image: '',
- content_link: '',
+ content_link: {
+ title: '',
+ des: '',
+ url: '',
+ thumburl: ''
+ },
content_miniprogram: {
title: '',
path: ''
@@ -412,7 +435,8 @@ new Vue({
},
imageList: [],
previewVisible: false,
- previewUrl: ''
+ previewUrl: '',
+ thumbnailList: []
}
},
mounted() {
@@ -513,7 +537,12 @@ new Vue({
groups: [],
content_text: '',
content_image: '',
- content_link: '',
+ content_link: {
+ title: '',
+ des: '',
+ url: '',
+ thumburl: ''
+ },
content_miniprogram: {
title: '',
path: ''
@@ -529,7 +558,13 @@ new Vue({
this.$refs.taskForm.validate(async (valid) => {
if (valid) {
try {
- const response = await axios.post('/message_push/api/tasks', this.taskForm);
+ // 确保链接内容是JSON字符串
+ const formData = { ...this.taskForm };
+ if (formData.content_link) {
+ formData.content_link = JSON.stringify(formData.content_link);
+ }
+
+ const response = await axios.post('/message_push/api/tasks', formData);
if (response.data.success) {
this.$message.success('保存任务成功');
this.taskDialogVisible = false;
@@ -559,6 +594,31 @@ new Vue({
editTask(task) {
this.dialogTitle = '编辑任务';
this.taskForm = { ...task };
+ // 处理链接内容
+ if (task.content_link) {
+ try {
+ this.taskForm.content_link = typeof task.content_link === 'string'
+ ? JSON.parse(task.content_link)
+ : task.content_link;
+
+ // 如果有缩略图,显示缩略图
+ if (this.taskForm.content_link.thumburl) {
+ const fileName = this.taskForm.content_link.thumburl.split('/').pop();
+ this.thumbnailList = [{
+ name: '已上传缩略图',
+ url: `/static/uploads/${fileName}`
+ }];
+ }
+ } catch (e) {
+ console.error('解析链接内容失败:', e);
+ this.taskForm.content_link = {
+ title: '',
+ des: '',
+ url: '',
+ thumburl: ''
+ };
+ }
+ }
if (task.content_image) {
// 编辑时显示图片
const fileName = task.content_image.split('/').pop();
@@ -728,6 +788,28 @@ new Vue({
this.previewVisible = true;
},
+ // 缩略图上传相关
+ handleThumbnailSuccess(response, file) {
+ if (response.success) {
+ this.taskForm.content_link.thumburl = response.data.url; // 存储绝对路径
+ // 显示时使用文件名
+ const fileName = file.name;
+ this.thumbnailList = [{
+ name: fileName,
+ url: `/static/uploads/${response.data.url.split('/').pop()}` // 显示时使用相对路径
+ }];
+ } else {
+ this.$message.error('上传失败');
+ }
+ },
+
+ handleThumbnailPreview(file) {
+ // 预览时使用相对路径
+ const fileName = file.url.split('/').pop();
+ this.previewUrl = `/static/uploads/${fileName}`;
+ this.previewVisible = true;
+ },
+
// 工具函数
getStatusType(status) {
const typeMap = {
diff --git a/db/task_db.py b/db/task_db.py
index 0fc8d31..5396e91 100644
--- a/db/task_db.py
+++ b/db/task_db.py
@@ -27,7 +27,7 @@ class TaskDBOperator(BaseDBOperator):
recurring_end DATETIME DEFAULT NULL,
content_text TEXT(500),
content_image VARCHAR(255),
- content_link VARCHAR(255),
+ content_link JSON,
content_miniprogram JSON,
groups JSON,
priority ENUM('high', 'medium', 'low') DEFAULT 'medium',
@@ -47,8 +47,7 @@ class TaskDBOperator(BaseDBOperator):
action ENUM('create', 'update', 'delete', 'pause', 'resume') NOT NULL,
user_id VARCHAR(50) NOT NULL,
changes JSON,
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (task_id) REFERENCES t_push_tasks(task_id)
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
@@ -61,8 +60,7 @@ class TaskDBOperator(BaseDBOperator):
recipients JSON NOT NULL,
validation JSON,
status ENUM('sent', 'confirmed', 'modified') DEFAULT 'sent',
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (task_id) REFERENCES t_push_tasks(task_id)
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
@@ -73,8 +71,7 @@ class TaskDBOperator(BaseDBOperator):
task_id VARCHAR(36) NOT NULL,
user_id VARCHAR(50) NOT NULL,
content TEXT NOT NULL,
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (task_id) REFERENCES t_push_tasks(task_id)
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
diff --git a/plugins/group_member_change/main.py b/plugins/group_member_change/main.py
index 954300d..2d573f3 100644
--- a/plugins/group_member_change/main.py
+++ b/plugins/group_member_change/main.py
@@ -1,16 +1,15 @@
+import xml.etree.ElementTree as ET
from datetime import datetime
from typing import Dict, Any, List, Optional, Tuple
-import xml.etree.ElementTree as ET
-
from base.plugin_common.message_plugin_interface import MessagePluginInterface
from base.plugin_common.plugin_interface import PluginStatus
from db.connection import DBConnectionManager
from db.contacts_db import ContactsDBOperator
-from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
+from utils.robot_cmd.robot_command import PermissionStatus, GroupBotManager
from utils.wechat.contact_manager import ContactManager
from wechat_ipad import WechatAPIClient
-from wechat_ipad.models.appmsg_xml import LINK_XML
+from wechat_ipad.models.appmsg_xml import LINK_XML_WELCOME
class GroupMemberChangePlugin(MessagePluginInterface):
@@ -141,7 +140,7 @@ class GroupMemberChangePlugin(MessagePluginInterface):
contact_db.save_chatroom_member_simple(roomid, member_details)
except Exception as e:
self.LOG.warning(f"新增群员信息失败: {e}")
- xml_content = f"{LINK_XML}".format(nickname=nickname, now=now, head_url=head_url)
+ xml_content = f"{LINK_XML_WELCOME}".format(nickname=nickname, now=now, head_url=head_url)
await bot.send_link_xml_message(xml_content, roomid)
return True, "已发送进群欢迎语"
diff --git a/plugins/message_push_task/main.py b/plugins/message_push_task/main.py
index 8337deb..e76a00e 100644
--- a/plugins/message_push_task/main.py
+++ b/plugins/message_push_task/main.py
@@ -13,6 +13,7 @@ from utils.decorator.async_job import async_job
from utils.decorator.plugin_decorators import plugin_stats_decorator
from utils.robot_cmd.robot_command import PermissionStatus, GroupBotManager
from wechat_ipad import WechatAPIClient
+from wechat_ipad.models.appmsg_xml import LINK_XML_NORMAL
class MessagePushTask(MessagePluginInterface):
@@ -174,7 +175,14 @@ class MessagePushTask(MessagePluginInterface):
# 发送链接消息
if content_link:
- await self.bot.send_link_message(group_id, content_link)
+ # content_link json 读取内容
+ link_data = json.loads(content_link)
+ xml_content = f"{LINK_XML_NORMAL}".format(title=link_data.get('title', ''),
+ des=link_data.get('des', ''),
+ url=link_data.get('url', ''),
+ thumburl=link_data.get('thumburl', '')
+ )
+ await self.bot.send_link_xml_message(xml_content, group_id)
# # 发送小程序消息
# if content_miniprogram:
diff --git a/wechat_ipad/models/appmsg_xml.py b/wechat_ipad/models/appmsg_xml.py
index 0976f0b..ca25e31 100644
--- a/wechat_ipad/models/appmsg_xml.py
+++ b/wechat_ipad/models/appmsg_xml.py
@@ -1,4 +1,4 @@
-LINK_XML = """
+LINK_XML_WELCOME = """
👏欢迎 {nickname} 加入群聊!🎉
⌚时间:{now}
@@ -93,3 +93,50 @@ MUSIC_XML = """
"""
+LINK_XML_NORMAL = """
+
+ {title}
+ {des}
+ view
+ 5
+ 0
+
+ {url}
+
+
+
+
+ {thumburl}
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ 0
+
+
+
+WXID
+0
+
+ 1
+
+
+
+"""