feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
大文件上传脚本 - 通过 JumpServer plink PTY 使用 base64 通道上传
|
||||
使用 4000 字符的大块,比 jms_ops.py 的 500 字符快 8 倍
|
||||
"""
|
||||
import sys, os, base64, hashlib, time
|
||||
|
||||
# 导入 jms_ops 模块
|
||||
SKILL_DIR = r"C:\Users\simon\.workbuddy\skills\jumpserver-ops\scripts"
|
||||
sys.path.insert(0, SKILL_DIR)
|
||||
import jms_ops
|
||||
|
||||
def upload_large_file(local_path, remote_path, chunk_size=4000):
|
||||
"""通过 plink PTY 上传大文件,使用大块 base64 编码"""
|
||||
local_data = open(local_path, 'rb').read()
|
||||
local_md5 = hashlib.md5(local_data).hexdigest()
|
||||
b64_data = base64.b64encode(local_data).decode('ascii')
|
||||
|
||||
total_chunks = (len(b64_data) + chunk_size - 1) // chunk_size
|
||||
print(f"📤 上传: {local_path} → {remote_path}")
|
||||
print(f" 原始: {len(local_data)} bytes, base64: {len(b64_data)} chars")
|
||||
print(f" 分 {total_chunks} 块发送 (每块 {chunk_size} chars)")
|
||||
|
||||
# 获取 token + 启动会话
|
||||
tokens = jms_ops.get_connection_tokens(1)
|
||||
if not tokens:
|
||||
print("❌ 获取 token 失败")
|
||||
return False
|
||||
|
||||
token_id, token_secret = tokens[0]
|
||||
session = jms_ops.PlinkSession(f"JMS-{token_id}", token_secret)
|
||||
if not session.connect():
|
||||
print("❌ 会话启动失败")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 1. 清空目标文件
|
||||
session.run_command(f'> {remote_path}', timeout=5)
|
||||
|
||||
# 2. 逐块追加 (大块)
|
||||
start_time = time.time()
|
||||
for i in range(0, len(b64_data), chunk_size):
|
||||
chunk = b64_data[i:i+chunk_size]
|
||||
chunk_num = i // chunk_size + 1
|
||||
cmd = f"echo '{chunk}' | base64 -d >> {remote_path}"
|
||||
r = session.run_command(cmd, timeout=15)
|
||||
if not r["success"]:
|
||||
print(f" ❌ 块 {chunk_num}/{total_chunks} 发送失败")
|
||||
return False
|
||||
if chunk_num % 50 == 0 or chunk_num == total_chunks:
|
||||
elapsed = time.time() - start_time
|
||||
pct = chunk_num / total_chunks * 100
|
||||
print(f" 📦 已发送 {chunk_num}/{total_chunks} 块 ({pct:.0f}%) - {elapsed:.1f}s")
|
||||
|
||||
# 3. 验证大小
|
||||
r = session.run_command(f'wc -c < {remote_path}', timeout=5)
|
||||
if r["success"]:
|
||||
remote_size = int(r["output"].strip()) if r["output"].strip().isdigit() else -1
|
||||
if remote_size == len(local_data):
|
||||
elapsed = time.time() - start_time
|
||||
print(f" ✅ 上传成功! 大小匹配 ({remote_size} bytes), 耗时 {elapsed:.1f}s")
|
||||
# 4. MD5 验证
|
||||
r2 = session.run_command(f'md5sum {remote_path}', timeout=5)
|
||||
if r2["success"]:
|
||||
remote_md5 = r2["output"].split()[0]
|
||||
if remote_md5 == local_md5:
|
||||
print(f" ✅ MD5 匹配! 文件完整")
|
||||
else:
|
||||
print(f" ⚠️ MD5 不匹配 (本地 {local_md5[:12]}, 远程 {remote_md5[:12]})")
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ 大小不匹配 (本地 {len(local_data)}, 远程 {remote_size})")
|
||||
return False
|
||||
else:
|
||||
print(" ⚠️ 无法验证远程文件大小")
|
||||
return False
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
local = r"D:\资料\03-项目开发\wecom_it_smart_desk\tmp-agent-dist.tar.gz"
|
||||
remote = "/tmp/agent-dist.tar.gz"
|
||||
success = upload_large_file(local, remote, chunk_size=4000)
|
||||
if success:
|
||||
print("\n✅ 上传完成,可以在服务器上解压了")
|
||||
else:
|
||||
print("\n❌ 上传失败")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user