当迁移wordpress把数据导入后后台无法登录的解决办法

这是个很奇怪的问题,常出的错第一天大家都知道。可第二条如果通过phpmyadmin去机械的更换哈希值,那依然无法登录,原因在于新老版本的数据哈希算法有了变化,出来的值是不一样的。
1,修复数据库中的地址:

sql
-- 确保地址正确(使用你的实际地址)
UPDATE wp_options SET option_value = 'http://你的域名或IP:端口' WHERE option_name = 'home';
UPDATE wp_options SET option_value = 'http://你的域名或IP:端口' WHERE option_name = 'siteurl';

2,数据库用户密码重置(新老版本的wordpress哈希算法不一样了)

sql
-- 生成新密码(将 your_new_password 替换为你要设置的密码)
UPDATE wp_users SET user_pass = MD5('your_new_password') WHERE user_login = 'admin';

-- 或使用 WordPress 的密码哈希
UPDATE wp_users SET user_pass = '$P$Bj8vM5rU2u5K5p5E5j5K5p5E5j5K5p5' WHERE user_login = 'admin';

Posted in Uncategorized | Leave a comment

WordPress HTTPS配置优化建议

// 在数据库配置之后,安全密钥之前添加:
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';

if ($host === ‘www.yuebin.uk’ || $host === ‘yuebin.uk’) {
define(‘WP_HOME’, ‘https://www.yuebin.uk’);
define(‘WP_SITEURL’, ‘https://www.yuebin.uk’);
define(‘FORCE_SSL_ADMIN’, true);
} else {
define(‘WP_HOME’, ‘http://’ . $host);
define(‘WP_SITEURL’, ‘http://’ . $host);
define(‘FORCE_SSL_ADMIN’, false);
}

Posted in Uncategorized | Leave a comment

如果防火墙不支持自动IPv6到IPv4

如果防火墙不支持自动IPv6到IPv4
如果上面的方案不行,使用这个替代方案:

opkg update
opkg install socat

方案2:极简脚本文件 + 启动项

bash
# 创建极简脚本
cat > /root/nas-start << 'EOF'
#!/bin/sh
# 极简启动,无日志

# 清理旧的
killall socat 2>/dev/null
sleep 1

# 获取NAS IPv6
NAS_MAC="bc:24:11:39:bc:d9"
NAS_IPV6=$(ip -6 neigh show | grep -i "$NAS_MAC" | grep '240e:' | awk '{print $1}' | head -1)

if [ -n "$NAS_IPV6" ]; then
    # 启动端口转发
    for port in 80 443 5666 3031; do
        socat TCP6-LISTEN:${port},fork,reuseaddr,ipv6only=1 TCP6:[${NAS_IPV6}]:${port} &
    done
fi
EOF

chmod +x /root/nas-start

然后在本地启动脚本中添加:

bash
# NAS IPv6转发
(sleep 50 && /root/nas-start) &

方案3:带自动恢复的无日志监控

bash
cat > /root/nas-monitor << 'EOF'
#!/bin/sh
# 无日志监控脚本

NAS_MAC="bc:24:11:39:bc:d9"

while true; do
    sleep 300  # 5分钟检查一次
    
    # 检查socat是否在运行
    if ! ps | grep -q "socat TCP6-LISTEN"; then
        # 重新启动
        killall socat 2>/dev/null
        sleep 1
        
        NAS_IPV6=$(ip -6 neigh show | grep -i "$NAS_MAC" | grep '240e:' | awk '{print $1}' | head -1)
        if [ -n "$NAS_IPV6" ]; then
            for port in 80 443 5666 3031; do
                socat TCP6-LISTEN:${port},fork,reuseaddr,ipv6only=1 TCP6:[${NAS_IPV6}]:${port} &
            done
        fi
    fi
done
EOF

chmod +x /root/nas-monitor

本地启动脚本:

bash
# NAS IPv6转发 + 监控
(sleep 50 && /root/nas-start && sleep 10 && /root/nas-monitor) &

方案4:一行命令解决方案(最简)

直接在本地启动脚本里写:

bash
# NAS IPv6端口转发
(sleep 50 && killall socat 2>/dev/null; sleep 2; NAS_IPV6=$(ip -6 neigh show | grep -i "bc:24:11:39:bc:d9" | grep '240e:' | awk '{print $1}' | head -1); [ -n "$NAS_IPV6" ] && for port in 80 443 5666 3031; do socat TCP6-LISTEN:${port},fork,reuseaddr,ipv6only=1 TCP6:[${NAS_IPV6}]:${port} & done) &

方案5:带IP变化检测的简洁版

bash
cat > /root/nas-auto << 'EOF'
#!/bin/sh
# 简洁自动转发

NAS_MAC="bc:24:11:39:bc:d9"
LAST_IP_FILE="/tmp/nas_last_ip"

# 获取当前IP
get_current_ip() {
    ip -6 neigh show | grep -i "$NAS_MAC" | grep '240e:' | awk '{print $1}' | head -1
}

# 主循环
while true; do
    CURRENT_IP=$(get_current_ip)
    LAST_IP=$(cat "$LAST_IP_FILE" 2>/dev/null)
    
    if [ -n "$CURRENT_IP" ] && [ "$CURRENT_IP" != "$LAST_IP" ]; then
        # IP变化,重启转发
        killall socat 2>/dev/null
        sleep 1
        
        for port in 80 443 5666 3031; do
            socat TCP6-LISTEN:${port},fork,reuseaddr,ipv6only=1 TCP6:[${CURRENT_IP}]:${port} &
        done
        
        echo "$CURRENT_IP" > "$LAST_IP_FILE"
    elif [ -z "$CURRENT_IP" ] && [ -f "$LAST_IP_FILE" ]; then
        # NAS可能离线
        rm -f "$LAST_IP_FILE"
    fi
    
    sleep 180  # 3分钟检查一次
done
EOF

chmod +x /root/nas-auto

本地启动脚本:

bash
# NAS自动转发
(sleep 55 && /root/nas-auto) &

推荐方案:简洁可靠

步骤1:创建简洁脚本

bash
cat > /root/nas-ipv6 << 'EOF'
#!/bin/sh
# 简洁NAS IPv6转发
killall socat 2>/dev/null
sleep 1
IP=$(ip -6 neigh show | grep -i "bc:24:11:39:bc:d9" | grep '240e:' | awk '{print $1}' | head -1)
[ -n "$IP" ] && for p in 80 443 5666 3031; do
    socat TCP6-LISTEN:${p},fork,reuseaddr,ipv6only=1 TCP6:[${IP}]:${p} &
done
EOF
chmod +x /root/nas-ipv6

步骤2:在Web界面本地启动脚本中添加

bash
# NAS IPv6转发
(sleep 50 && /root/nas-ipv6) &

步骤3:创建定时检查(可选)

bash
# 添加到crontab,每天凌晨3点重启一次转发
echo "0 3 * * * /root/nas-ipv6" >> /etc/crontabs/root
/etc/init.d/cron restart

验证是否生效

bash
# 重启后检查
sleep 60
echo "检查端口转发:"
netstat -tln | grep -E ':(80|443|5666|3031)'

echo ""
echo "检查进程:"
ps | grep socat | grep -v grep

# 手动测试
curl -6 -I http://[你的路由器IPv6]:80 2>/dev/null | head -1

这样就没有日志文件占空间了,而且简单可靠。核心就是等待网络就绪后启动转发,不需要复杂的监控和日志。

Posted in 网络相关 | Leave a comment

永久激活office

您可以使用开源激活脚本(如MAS)通过简单的命令永久激活Microsoft Office,确保安全可靠。

使用MAS激活Office

  1. 打开PowerShell:以管理员身份运行Windows PowerShell。可以通过按下Windows键+X,然后选择“Windows PowerShell (管理员)”来打开它。
  2. 输入激活命令:在PowerShell窗口中,输入以下命令并回车(确保您的计算机已连接到互联网):
irm get.activated.win | iex

注意事项

  • 安全性:使用开源激活脚本时,请确保从可信的来源获取脚本,以避免潜在的安全风险。
  • 激活状态检查:激活后,您可以在Office应用程序中检查激活状态,确保软件正常运行。
    通过以上步骤,您可以轻松实现Microsoft Office的永久激活,享受完整的功能而无需担心激活问题。
Posted in 网络相关 | Leave a comment

看片神器!完全免费开源,支持Vercel、Cloudflare Pages、Docker等一键部署!

https://github.com/yuebinliu/MoonTV
Cloudflare 部署

Cloudflare Pages 的环境变量尽量设置为密钥而非文本

普通部署(localstorage)

  1. Fork 本仓库到你的 GitHub 账户。
  2. 登陆 Cloudflare,点击 计算(Workers)-> Workers 和 Pages,点击创建
  3. 选择 Pages,导入现有的 Git 存储库,选择 Fork 后的仓库
  4. 构建命令填写 pnpm install –frozen-lockfile && pnpm run pages:build,预设框架为无,构建输出目录为 .vercel/output/static
  5. 保持默认设置完成首次部署。进入设置,将兼容性标志设置为 nodejs_compat,无需选择,直接粘贴
  6. 首次部署完成后进入设置,新增 PASSWORD 密钥(变量和机密下),而后重试部署。
  7. 如需自定义 config.json,请直接修改 Fork 后仓库中该文件。
  8. 每次 Push 到 main 分支将自动触发重新构建。

D1 支持

  1. 完成普通部署并成功访问
  2. 点击 存储和数据库 -> D1 SQL 数据库,创建一个新的数据库,名称随意
  3. 进入刚创建的数据库,点击左上角的 Explore Data,将D1 初始化 中的内容粘贴到 Query 窗口后点击 Run All,等待运行完成
  4. 返回你的 pages 项目,进入 设置 -> 绑定,添加绑定 D1 数据库,选择你刚创建的数据库,变量名称填 DB
  5. 设置环境变量 NEXT_PUBLIC_STORAGE_TYPE,值为 d1;设置 USERNAME 和 PASSWORD 作为站长账号
  6. 重试部署

Docker 部署

1. 直接运行(最简单,localstorage)

# 拉取预构建镜像
docker pull ghcr.io/senshinya/moontv:latest

# 运行容器
# -d: 后台运行  -p: 映射端口 3000 -> 3000
docker run -d --name moontv -p 3000:3000 --env PASSWORD=your_password ghcr.io/senshinya/moontv:latest

访问 http://服务器 IP:3000 即可。(需自行到服务器控制台放通 3000 端口)

Docker Compose 最佳实践

若你使用 docker compose 部署,以下是一些 compose 示例

local storage 版本

services:
  moontv:
    image: ghcr.io/senshinya/moontv:latest
    container_name: moontv
    restart: unless-stopped
    ports:
      - '3000:3000'
    environment:
      - PASSWORD=your_password
    # 如需自定义配置,可挂载文件
    # volumes:
    #   - ./config.json:/app/config.json:ro

Redis 版本(推荐,多账户数据隔离,跨设备同步)

services:
  moontv-core:
    image: ghcr.io/senshinya/moontv:latest
    container_name: moontv
    restart: unless-stopped
    ports:
      - '3000:3000'
    environment:
      - USERNAME=admin
      - PASSWORD=admin_password
      - NEXT_PUBLIC_STORAGE_TYPE=redis
      - REDIS_URL=redis://moontv-redis:6379
      - NEXT_PUBLIC_ENABLE_REGISTER=true
    networks:
      - moontv-network
    depends_on:
      - moontv-redis
    # 如需自定义配置,可挂载文件
    # volumes:
    #   - ./config.json:/app/config.json:ro
  moontv-redis:
    image: redis
    container_name: moontv-redis
    restart: unless-stopped
    networks:
      - moontv-network
    # 如需持久化
    # volumes:
    #   - ./data:/data
networks:
  moontv-network:
    driver: bridge

自动同步最近更改

建议在 fork 的仓库中启用本仓库自带的 GitHub Actions 自动同步功能(见 .github/workflows/sync.yml)。

如需手动同步主仓库更新,也可以使用 GitHub 官方的 Sync fork 功能。

Posted in Uncategorized | Leave a comment

通过cloudflare的zerotrust进行内网穿透保留IP直接访问能力

WordPress我通过cloudflare的zerotrust进行了内网穿透,绑定到了https://www.thanx.top,现在我想保留IP:端口的访问能力,怎么做?

修改 wp-config.php


// 在 wp-config.php 最顶部添加
$_SERVER['HTTPS'] = 'off'; // 全局强制禁用 HTTPS
define('FORCE_SSL_ADMIN', false);
define('FORCE_SSL_LOGIN', false);

// 然后添加你的域名/IP判断逻辑
$is_domain_request = (strpos($_SERVER['HTTP_HOST'], 'thanx.top') !== false);

if ($is_domain_request) {
    // 仅域名访问时启用 HTTPS
    $_SERVER['HTTPS'] = 'on';
    define('WP_HOME', 'https://www.thanx.top');
    define('WP_SITEURL', 'https://www.thanx.top');
    
    // 域名 HTTP 访问跳转到 HTTPS
    if (!isset($_SERVER['HTTPS'])) {
        header('Location: https://www.thanx.top' . $_SERVER['REQUEST_URI']);
        exit;
    }
} else {
    // IP访问强制保持 HTTP
    $_SERVER['HTTPS'] = 'off';
    define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST']);
    define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST']);
}
Posted in Uncategorized | Leave a comment

在最新WordPress blog中启用经典编辑器或者古登堡编辑器

1. 默认古登堡已启用

  • WordPress 5.0+版本:古登堡是默认编辑器,无需额外操作。直接创建或编辑文章/页面即可使用。

  • 如果看到的是经典编辑器(Classic Editor),可能是被插件或配置覆盖,需检查以下内容。


2. 检查是否被插件禁用

  • 进入 仪表盘 → 插件 → 已安装的插件

  • 如果安装了 Classic Editor 插件,它会强制禁用古登堡。你可以:

    • 停用并删除 Classic Editor插件(完全使用古登堡)。

    •  进入 设置 → 撰写,选择默认编辑器为“区块编辑器(古登堡)”。

Posted in Uncategorized | Leave a comment

月度工作量统计脚本

使用python3写了个脚本,用来统计月度工作量。本来只是在本地电脑运行的,想想还是把他移植到web端吧。说真的,要我一个个去搜索拉取,我真不敢说自己就能拉出来,而且一点差错都没有,且不说还要制表汇总,琐碎一地。好在,确实可以通过编写脚本来提高效率,不用一个个去拉取,啥也不说了,上代码。
web演示:https://www.thanx.top/re/shell/indexs.php
直连地址,生产环境用:
http://106.15.4.153:8085/re/shell/indexs.php
代码:



import pandas as pd
import argparse

def process_files(file1_path, file2_path, output_filename):
# 读取数据
df_jan = pd.read_excel(file1_path, sheet_name='导出数据')
df_feb = pd.read_excel(file2_path, sheet_name='导出数据')

# 定义项目名称映射规则
project_mapping = {
'无痛胃镜': '无胃',
'无痛肠镜': '无肠',
'EMR/APC': 'EMR',
'止血术': '止血',
'扩张术': '扩张',
'超声内镜': '超声',
'异物摄取': '异物',
'病例数': '总数'
}

# 定义项目顺序(使用映射后的名称)
project_order = [
'胃镜', '无胃', '肠镜', '无肠', '超声', 'EMR', 'ESD', 'ERCP',
'止血', '异物', '扩张', '其他'
]

# 定义统计函数
def count_stats(df):
stats = {
'胃镜': 0,
'无痛胃镜': 0,
'肠镜': 0,
'无痛肠镜': 0,
'超声内镜': 0,
'EMR/APC': 0,
'ESD': 0,
'ERCP': 0,
'止血术': 0,
'异物摄取': 0,
'扩张术': 0,
'其他': 0
}
for _, row in df.iterrows():
category = str(row['检查类别']).lower().strip()
diagnosis = str(row['镜下诊断']).lower().strip()

# 统计检查类别
if '十二指肠镜' in category or 'ercp' in category:
stats['ERCP'] += 1
elif '胃镜' in category and '无痛' not in category:
stats['胃镜'] += 1
elif '无痛胃镜' in category:
stats['无痛胃镜'] += 1
elif '肠镜' in category and '无痛' not in category:
stats['肠镜'] += 1
elif '无痛肠镜' in category:
stats['无痛肠镜'] += 1
elif '超声内镜' in category:
stats['超声内镜'] += 1
else:
stats['其他'] += 1

# 统计镜下诊断
if '扩张' in diagnosis:
stats['扩张术'] += 1
if 'esd' in diagnosis and 'esd术后' not in diagnosis:
stats['ESD'] += 1
if 'emr' in diagnosis or 'apc' in diagnosis:
stats['EMR/APC'] += 1
if '止血' in diagnosis:
stats['止血术'] += 1
if '异物' in diagnosis:
stats['异物摄取'] += 1

# 计算病例数
stats['病例数'] = (
stats['胃镜'] +
stats['无痛胃镜'] +
stats['肠镜'] +
stats['无痛肠镜'] +
stats['超声内镜'] +
stats['ERCP'] +
stats['其他']
)
return stats

# 获取上月和本月的统计数据
stats_jan = count_stats(df_jan)
stats_feb = count_stats(df_feb)

# 计算同比变化
def calculate_change(current, previous):
if previous == 0:
return 0
return round((current - previous) / previous * 100, 2)

# 创建内镜中心工作量统计 DataFrame
center_data = []
for project in project_order:
original_project = next(
(key for key, value in project_mapping.items() if value == project),
project
)
center_data.append({
'项目': project,
'本月数量': stats_feb.get(original_project, 0),
'上月数量': stats_jan.get(original_project, 0),
'同比变化(%)': calculate_change(stats_feb.get(original_project, 0), stats_jan.get(original_project, 0))
})

center_df = pd.DataFrame(center_data)

# 增加汇总行
summary_row = pd.DataFrame({
'项目': ['汇总'],
'本月数量': [
stats_feb['胃镜'] + stats_feb['无痛胃镜'] + stats_feb['肠镜'] +
stats_feb['无痛肠镜'] + stats_feb['超声内镜'] + stats_feb['ERCP'] + stats_feb['其他']
],
'上月数量': [
stats_jan['胃镜'] + stats_jan['无痛胃镜'] + stats_jan['肠镜'] +
stats_jan['无痛肠镜'] + stats_jan['超声内镜'] + stats_jan['ERCP'] + stats_jan['其他']
],
'同比变化(%)': [calculate_change(
stats_feb['胃镜'] + stats_feb['无痛胃镜'] + stats_feb['肠镜'] +
stats_feb['无痛肠镜'] + stats_feb['超声内镜'] + stats_feb['ERCP'] + stats_feb['其他'],
stats_jan['胃镜'] + stats_jan['无痛胃镜'] + stats_jan['肠镜'] +
stats_jan['无痛肠镜'] + stats_jan['超声内镜'] + stats_jan['ERCP'] + stats_jan['其他']
)],
'备注': ['']
})
center_df = pd.concat([center_df, summary_row], ignore_index=True)

# 统计医生工作量
def count_doctor_stats(df):
doctor_stats = {}
for _, row in df.iterrows():
doctor = row['报告医师']
category = str(row['检查类别']).lower().strip()
diagnosis = str(row['镜下诊断']).lower().strip()

if doctor not in doctor_stats:
doctor_stats[doctor] = {
'胃镜': 0,
'无痛胃镜': 0,
'肠镜': 0,
'无痛肠镜': 0,
'超声内镜': 0,
'ERCP': 0,
'EMR/APC': 0,
'ESD': 0,
'止血术': 0,
'扩张术': 0,
'异物摄取': 0,
'其他': 0,
'病例数': 0
}

if '十二指肠镜' in category or 'ercp' in category:
doctor_stats[doctor]['ERCP'] += 1
elif '胃镜' in category and '无痛' not in category:
doctor_stats[doctor]['胃镜'] += 1
elif '无痛胃镜' in category:
doctor_stats[doctor]['无痛胃镜'] += 1
elif '肠镜' in category and '无痛' not in category:
doctor_stats[doctor]['肠镜'] += 1
elif '无痛肠镜' in category:
doctor_stats[doctor]['无痛肠镜'] += 1
elif '超声内镜' in category:
doctor_stats[doctor]['超声内镜'] += 1
else:
doctor_stats[doctor]['其他'] += 1

if '扩张' in diagnosis:
doctor_stats[doctor]['扩张术'] += 1
if 'esd' in diagnosis and 'esd术后' not in diagnosis:
doctor_stats[doctor]['ESD'] += 1
if 'emr' in diagnosis or 'apc' in diagnosis:
doctor_stats[doctor]['EMR/APC'] += 1
if '止血' in diagnosis:
doctor_stats[doctor]['止血术'] += 1
if '异物' in diagnosis:
doctor_stats[doctor]['异物摄取'] += 1

# 计算病例数
doctor_stats[doctor]['病例数'] = (
doctor_stats[doctor]['胃镜'] +
doctor_stats[doctor]['无痛胃镜'] +
doctor_stats[doctor]['肠镜'] +
doctor_stats[doctor]['无痛肠镜'] +
doctor_stats[doctor]['超声内镜'] +
doctor_stats[doctor]['其他'] +
doctor_stats[doctor]['ERCP']
)
return doctor_stats

# 获取上月和本月的医生统计数据
doctor_stats_jan = count_doctor_stats(df_jan)
doctor_stats_feb = count_doctor_stats(df_feb)

# 创建医生工作量统计 DataFrame
doctor_data = []
for doctor, stats in doctor_stats_feb.items():
doctor_data.append({
'医师': doctor,
**{project_mapping.get(k, k): v for k, v in stats.items()}
})

doctor_df = pd.DataFrame(doctor_data)

# 增加汇总行
summary_row = pd.DataFrame({
'医师': ['汇总'],
**{project_mapping.get(k, k): [doctor_df[project_mapping.get(k, k)].sum()] for k in project_order},
'总数': [doctor_df['总数'].sum()]
})
doctor_df = pd.concat([doctor_df, summary_row], ignore_index=True)

# 统计护士工作量
def count_nurse_stats(df):
nurse_stats = {}
for _, row in df.iterrows():
nurse = row['助手']
category = str(row['检查类别']).lower().strip()
diagnosis = str(row['镜下诊断']).lower().strip()

if nurse not in nurse_stats:
nurse_stats[nurse] = {
'胃镜': 0,
'无痛胃镜': 0,
'肠镜': 0,
'无痛肠镜': 0,
'超声内镜': 0,
'ERCP': 0,
'EMR/APC': 0,
'ESD': 0,
'止血术': 0,
'扩张术': 0,
'异物摄取': 0,
'其他': 0,
'病例数': 0
}

if '十二指肠镜' in category or 'ercp' in category:
nurse_stats[nurse]['ERCP'] += 1
elif '胃镜' in category and '无痛' not in category:
nurse_stats[nurse]['胃镜'] += 1
elif '无痛胃镜' in category:
nurse_stats[nurse]['无痛胃镜'] += 1
elif '肠镜' in category and '无痛' not in category:
nurse_stats[nurse]['肠镜'] += 1
elif '无痛肠镜' in category:
nurse_stats[nurse]['无痛肠镜'] += 1
elif '超声内镜' in category:
nurse_stats[nurse]['超声内镜'] += 1
else:
nurse_stats[nurse]['其他'] += 1

if '扩张' in diagnosis:
nurse_stats[nurse]['扩张术'] += 1
if 'esd' in diagnosis and 'esd术后' not in diagnosis:
nurse_stats[nurse]['ESD'] += 1
if 'emr' in diagnosis or 'apc' in diagnosis:
nurse_stats[nurse]['EMR/APC'] += 1
if '止血' in diagnosis:
nurse_stats[nurse]['止血术'] += 1
if '异物' in diagnosis:
nurse_stats[nurse]['异物摄取'] += 1

# 计算病例数
nurse_stats[nurse]['病例数'] = (
nurse_stats[nurse]['胃镜'] +
nurse_stats[nurse]['无痛胃镜'] +
nurse_stats[nurse]['肠镜'] +
nurse_stats[nurse]['无痛肠镜'] +
nurse_stats[nurse]['超声内镜'] +
nurse_stats[nurse]['其他'] +
nurse_stats[nurse]['ERCP']
)
return nurse_stats

# 获取上月和本月的护士统计数据
nurse_stats_jan = count_nurse_stats(df_jan)
nurse_stats_feb = count_nurse_stats(df_feb)

# 创建护士工作量统计 DataFrame
nurse_data = []
for nurse, stats in nurse_stats_feb.items():
nurse_data.append({
'护士': nurse,
**{project_mapping.get(k, k): v for k, v in stats.items()}
})

nurse_df = pd.DataFrame(nurse_data)

# 增加汇总行
summary_row = pd.DataFrame({
'护士': ['汇总'],
**{project_mapping.get(k, k): [nurse_df[project_mapping.get(k, k)].sum()] for k in project_order},
'总数': [nurse_df['总数'].sum()]
})
nurse_df = pd.concat([nurse_df, summary_row], ignore_index=True)

# 保存更新后的Excel文件
with pd.ExcelWriter(output_filename) as writer:
center_df.to_excel(writer, sheet_name='内镜中心工作量统计', index=False)
doctor_df.to_excel(writer, sheet_name='医生工作量统计', index=False)
nurse_df.to_excel(writer, sheet_name='护士工作量统计', index=False)

if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Process Excel files.')
parser.add_argument('file1', help='Path to the first Excel file')
parser.add_argument('file2', help='Path to the second Excel file')
parser.add_argument('output', help='Path to save the output Excel file')

args = parser.parse_args()

process_files(args.file1, args.file2, args.output)
Posted in Resourse | Leave a comment

PVE安装OpenWrt做主路由后局域网DHCP分配不了地址的修复

通过悟空的日常安装的istoreos设置成主路由后无论如何设置,都会发现DHCP分配不了IPV4地址,无论怎么设置,最后发现:
root@iStoreOS:~# uci show dhcp.lan
dhcp.lan=dhcp
dhcp.lan.interface='lan'
dhcp.lan.start='100'
dhcp.lan.leasetime='12h'
dhcp.lan.dhcpv4='disabled'
dhcp.lan.force='1'
dhcp.lan.limit='116'

uci show 输出可以看出,DHCPv4 被显式禁用了dhcp.lan.dhcpv4='disabled'),这是导致局域网设备无法获取 IP 地址的直接原因。以下是完整的修复步骤:
#启用 DHCPv4 服务
uci set dhcp.lan.dhcpv4='server' # 启用DHCPv4服务
uci commit dhcp
/etc/init.d/dnsmasq restart

Posted in Resourse | Leave a comment

使用monica写了一个自动保存deepseek聊天记录的脚本

根据您提供的调试信息,您在页面中找到了大量的 div 元素,但没有找到包含聊天消息的特定元素。这表明可能需要更具体的选择器来提取聊天内容。以下是一些步骤来帮助您解决这个问题:

步骤 4: 逐步调试

如果您仍然无法找到消息元素,可以尝试逐步调试:

  1. 选择所有 div 元素:
    const allDivs = document.querySelectorAll('div');
    allDivs.forEach(div => {
       console.log(div); // 输出每个 div 的内容
    });
  2. 通过特定属性过滤:
    如果某些 div 有特定的属性(如 data-* 属性),您可以使用这些属性来过滤:

    const userMessages = document.querySelectorAll('div[data-role="message"]'); // 根据实际情况调整

    F12打开浏览器调试窗口,输入1中代码,得到了一些html代码,经过分析得到div标签的标识:
    获取到的标签

聊天的主题标题:d8ed659a
我的提问:fbb737a4
ai的思考过程:e1675d8b
AI的解答过程:ds-markdown ds-markdown--block

问Monica,经过几次调整,最终得到如下结果:

// ==UserScript==
// @name         DeepSeek Chat Content Saver
// @match        https://chat.deepseek.com/*
// @version      1.6
// @description  Save chat content as Markdown with auto-save option
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        BUTTON_ID: 'deepseek-save-btn',
        USER_PREFIX: "你",
        AI_PREFIX: "AI",
        AUTO_SAVE_ENABLED: true, // 配置项:是否启用自动保存
        AUTO_SAVE_INTERVAL: 30000 // 自动保存间隔(毫秒),例如30秒
    };

    function generateMarkdown(messages) {
        let md = `# 聊天记录\n\n`;
        messages.forEach(msg => {
            md += `**${msg.role === 'user' ? CONFIG.USER_PREFIX : CONFIG.AI_PREFIX}**:\n`;
            md += `${msg.text}\n\n---\n\n`;
        });
        md += `> 保存时间: ${new Date().toLocaleString()}\n`;
        return md;
    }

    function downloadFile(content, filename) {
        const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a); // 需要将链接添加到DOM中
        a.click();
        document.body.removeChild(a); // 下载后移除链接
        setTimeout(() => URL.revokeObjectURL(url), 100);
    }

    function extractMessages() {
        const messages = [];

        // 提取聊天主题标题
        const titleElement = document.querySelector('.d8ed659a');
        let title = titleElement ? titleElement.innerText : '聊天记录';

        // 提取用户提问、AI思考和AI回答,按顺序排列
        const userQuestions = document.querySelectorAll('.fbb737a4');
        const aiThoughts = document.querySelectorAll('.e1675d8b');
        const aiAnswers = document.querySelectorAll('.ds-markdown.ds-markdown--block');

        for (let i = 0; i < userQuestions.length; i++) {
            messages.push({
                role: 'user',
                text: userQuestions[i].innerText
            });
            if (i < aiThoughts.length) {
                messages.push({
                    role: 'ai',
                    text: aiThoughts[i].innerText
                });
            }
            if (i < aiAnswers.length) {
                messages.push({
                    role: 'ai',
                    text: aiAnswers[i].innerText
                });
            }
        }

        console.log('提取的消息:', messages); // 调试输出
        return { title, messages };
    }

    function saveChatRecord() {
        const { title, messages } = extractMessages();
        if (messages.length === 0) throw new Error("未找到对话内容");

        const content = generateMarkdown(messages);
        //const timestamp = new Date().toISOString().replace(/[-:.]/g, "_"); // 格式化时间戳
        //const filename = `${title}_${timestamp}.md`; // 文件名包含时间戳
        const filename = `${title}.md`; // 文件名不包含时间戳

        // 下载文件
        downloadFile(content, filename);
        //alert('保存成功!');
    }

    function startAutoSave() {
        if (CONFIG.AUTO_SAVE_ENABLED) {
            setInterval(() => {
                try {
                    saveChatRecord();
                } catch (error) {
                    console.error('自动保存失败:', error);
                }
            }, CONFIG.AUTO_SAVE_INTERVAL);
        }
    }

    function createSaveButton() {
        const button = document.createElement('button');
        button.id = CONFIG.BUTTON_ID;
        button.innerText = '保存对话';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.padding = '15px 30px';
        button.style.fontSize = '16px';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.zIndex = '1000';
        button.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.3)';
        document.body.appendChild(button);
        return button;
    }

    function setupButtonHandler(btn) {
        btn.addEventListener('click', saveChatRecord);
    }

    function initialize() {
        const btn = createSaveButton();
        setupButtonHandler(btn);
        startAutoSave(); // 启动自动保存功能
    }

    // 启动
    if (document.readyState === 'complete') initialize();
    else window.addEventListener('load', initialize);
})();

脚本下载:DeepSeek Chat Content Saver-1.6.user.js

Posted in Resourse | Leave a comment