阅读视图

道阻且长

都道:“一步错,步步错”。虽然有时候是无心之失,却也在不经意间制造了太多的问题。就想撒了个谎,后面需要无数的谎言来解释当初这个谎言。最开始开发 app 的时候,对于所谓的前段并没那么清晰深入的理解,毕竟之前做 ios 和安卓的开发还是基于 oc 和 java 来搞。

之前虽然也看过不少 uni 的项目,但是真正开始的时候。当时还是选择了 vue2 的矿建,虽然那时候3 也依然有些规模。鉴于当时找到的很多组件都是 vue2 的不支持 3,所以开始的时候就入了这个坑,直接基于 2 的各种组件搭起来了现在的这个闺蜜圈的项目。

当然,后续再开发过程中也发现了很多问题,虽然是 vue2 的各种组件,然而很多满足不了自己的需求,在不断的开发过程中也在不断的修改这些组件,甚至很多组件到现在已经改的完全不是原来的样子了。重写了大量的代码,也修复了很多组件的问题。所谓的生态,国内的生态环境并不是看到的那么好。各种插件市场非常活跃,插件也非常多。但是实际上出了问题大概率得不到作者的解答,只能自己去读代码,理解逻辑之后重写有问题的部分。

再后来,鸿蒙 next 横空出世,uni 也承诺会支持鸿蒙系统。当然后来也确实是支持了,不过仅支持 vue3 的项目的鸿蒙系统编译。于是这原来的 vue2 的项目就面临着一次大版本的升级和兼容处理。在开发了一年之后,如果重新搭架子从 vue3 重写,成本固然是太高了,不在可接受的范围之内。最终的方案是直接将 vue2 的项目迁移到 vue3。

在去年uni 开放鸿蒙的体验版本兼容编译之后,就尝试将项目迁移到 vue3,不过那时候仅限于能编译运行。可以登录到首页,而其他的页面全部都没处理。在这次鸿蒙的技术支持介入之后,也是下定决心将项目迁移到 vue3 进一步迁移到原生鸿蒙。到原生鸿蒙才是最终目的。

不过在迁移过程中也发现一些问题:

1.导航栏 button 无效:

HarmonyOS 基于uniapp的方式开发HarmonyOS应用时候,通过app-plus:titleNView 配置页面右上角按钮未生效(API12+)
【问题描述】:HarmonyOs基于uniapp的方式开发HarmonyOS应用的时候,通过app-plus:titleNview配置页面右上角按钮不生效

【问题现象】:使用app-plus:titleNview配置导航栏右上角button不显示

【版本信息】:hbuilder 版本4.8.5、DevEco Studio 6.0.1 Release

【复现代码】:{

  "path": "pages/besties",

  "style": {

    "navigationBarTitleText": "XXX",

    "navigationBarBackgroundColor": "#ff4f87",

    "app-plus": {

      "bounce": "none",

      "titleNView": {

        "buttons": [{

          "fontSize": "18px",

          "text": "+",          // 直接写加号字符

          "color": "#FFFFFF"

        }]

      }

    }

  }

}

官方给的解决方案:

在基于 UniApp 开发 HarmonyOS 应用时,app-plus:titleNView配置未生效的核心原因是:app-plus是 UniApp 针对 Android/iOS 端设计的原生导航栏配置,HarmonyOS 平台的导航栏实现逻辑与 Android/iOS 不同,因此该配置暂未适配 HarmonyOS 平台。

解决方案:使用「自定义导航栏」替代(推荐,跨平台兼容)
由于 HarmonyOS 不支持 UniApp 的titleNView配置,建议通过隐藏原生导航栏 + 自定义导航栏组件的方式实现右上角按钮,同时兼容跨平台场景。

步骤 1:隐藏原生导航栏
在pages.json中,给目标页面的style添加navigationStyle: "custom",隐藏原生导航栏:

json

{
  "path": "pages/besties",
  "style": {
    "navigationBarTitleText": "XXX",
    "navigationBarBackgroundColor": "#ff4f87",
    "navigationStyle": "custom", // 隐藏原生导航栏
    "app-plus": {
      "bounce": "none"
    }
  }
}
步骤 2:自定义导航栏组件(实现右上角按钮)
在页面中手动编写导航栏布局,包含标题和右上角按钮,示例代码(Vue3 语法):

vue

<template>
  <!-- 自定义导航栏 -->
  <view class="custom-nav-bar" :style="{backgroundColor: '#ff4f87'}">
    <!-- 标题 -->
    <view class="nav-title">XXX</view>
    <!-- 右上角按钮 -->
    <view class="nav-right-btn" @click="handleRightBtnClick">
      <text :style="{fontSize: '18px', color: '#FFFFFF'}">+</text>
    </view>
  </view>

  <!-- 页面内容 -->
  <view class="page-content">
    <!-- 你的页面内容 -->
  </view>
</template>

<script setup>
// 右上角按钮点击事件
const handleRightBtnClick = () => {
  console.log("右上角按钮被点击");
  // 这里写按钮的业务逻辑
};
</script>

<style scoped>
/* 自定义导航栏样式(适配HarmonyOS状态栏高度) */
.custom-nav-bar {
  width: 100%;
  height: var(--status-bar-height) + 44px; /* 状态栏高度 + 导航栏高度 */
  padding-top: var(--status-bar-height); /* 适配状态栏 */
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 16px;
  box-sizing: border-box;
}

.nav-title {
  color: #FFFFFF;
  font-size: 18px;
  font-weight: bold;
}

.nav-right-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
}

/* 页面内容区,避免被导航栏遮挡 */
.page-content {
  padding-top: calc(var(--status-bar-height) + 44px);
}
</style>
补充说明(针对 UniApp X 项目)
如果你的项目是UniApp X(专门适配 HarmonyOS 的 UniApp 模式),可以直接使用 HarmonyOS 原生的Navigation组件配置右上角按钮(需在uts页面中实现):

ts

// pages/besties/besties.uts
@Entry
@Component
struct BestiesPage {
  build() {
    Navigation() {
      // 页面内容
      Column() {
        Text("页面内容")
      }
    }
    .title("XXX") // 导航栏标题
    .titleMode(NavigationTitleMode.Free)
    .backgroundColor("#ff4f87")
    // 配置右上角按钮
    .toolBar({
      items: [
        {
          value: "+",
          textColor: "#FFFFFF",
          fontSize: 18,
          action: () => {
            console.log("右上角按钮被点击");
            // 业务逻辑
          }
        }
      ]
    })
  }
}
关键注意事项
app-plus配置的局限性:app-plus是 UniApp 对 Android/iOS 端的扩展配置,HarmonyOS 平台的导航栏、原生组件等逻辑与 Android/iOS 不同,因此大部分app-plus配置在 HarmonyOS 上不生效。
状态栏高度适配:自定义导航栏时,需通过var(--status-bar-height)获取 HarmonyOS 设备的状态栏高度,避免导航栏被状态栏遮挡。
UniApp X 的适配:若使用 UniApp X 开发 HarmonyOS 应用,建议直接使用 HarmonyOS 原生组件(如Navigation),兼容性和体验更好。
通过自定义导航栏或 UniApp X 的原生Navigation组件,即可实现 HarmonyOS 应用中页面右上角按钮的需求,同时保证跨平台兼容性或原生体验。

官方答复地址:https://developer.huawei.com/consumer/cn/forum/topic/0203200053870018576?fid=0109140870620153026&pid=0314200135195141595

当然,这个问题解决最终也是通过自绘导航栏解决的,不过目前还有个问题,就是与 uni 自带的原生导航栏样式稍微有些区别,主要问题在导航栏高度计算,以及导航栏字体渲染问题。这个如果要想彻底解决可能就得全部重写几个页面的导航栏了。这个不影响使用,稍微往后放一点。

另外一个问题,那就是 ai 助理迁移问题,迁移到鸿蒙系统之后,由于部分属性不支持,导致页面直接白屏了。目前虽然就解决了大多数的问题,单机 markdown 渲染问题,以及聊天窗口自动滚动到最新消息依然有问题,在没有全屏的时候是没问题的,消息列表全屏之后,滚动总是少一块。

而至于上篇文章提到的,google play 测试的问题,目前也有好多宝子参与进来了。也反馈了很多问题,当然,很多东西自己觉得习以为常,但是很多人第一次使用的时候却并没有看到相应的效果,或者对于操作步骤有疑惑。

记录:
1.增加异常状态种类,支持多选
2.优化日历样式
3.增加用户徽章
4.针对不同设备处理流式数据
5.优化生日选择的日期选择器
6.优化爱爱记录操作方式
7.优化日历操作方式,更简单明了 删除记录 结束记录逻辑调整
todo

todo:
个人信息–手机号,随便在那里输点东西再去修改昵称, 修改昵称那个输入框变成修改手机号的输入框了。✓
修改完手机号点一下邮箱–取消,在点击昵称 ,输入框变成修改邮箱输入框了✓
我的–高级设置–安全设置 点击密码没反应,改不了密码 ✓
青少年模式不能记录新的了,但是已经记录的💊在日历上还是显示的。
数据分析页面:
年没上限
月只有60天。而且英文界面出错提示是中文。
体重和体温也没有边界值,甚至可以输入负数。 ✓
please拼错账 pleace ✓
中文用户名长度判断问题,中文允许两个字符 ✓

目前已经更新发布了一版新的,并且 ios 和安卓终于版本号同步了。之前由于种种问题,ios 的版本号稍微落后了一点。

当然,正式的测试还得找个时间一起处理。多人长测的确是个问题,需要投入那么点精力。

然而,由于 google 原来的 appid 用不了了,只能换个新的 id,然而新的 id 就会出现 pushid 和 unicloud 不一致的问题,最终只能在 google 包开了 1.0 的 push 临时解决了这个问题。最终没有再搞个分支,不同的分支维护真的太麻烦了。

本来想着一套代码搞定,现在就已经拆分成了鸿蒙的 vue3 和通用版本的 vue2。修复一点问题,现在就得维护两套代码。这个也是属实是无奈之举了。

之前上架华为应用市场的版本是没有 ai 助理的,也就是一个屏蔽了 ai 助理的版本。对于这种不完美事情,总是觉得有些欠缺。继续咨询了一下相关的问题,https://developer.huawei.com/consumer/cn/forum/topic/0203200246757472747?fid=0109140870620153026 给出的答案倒是和之前的区别不大,意思是可以上,但是有些限制。

除了生成的内容要有明显的标识:https://developer.huawei.com/consumer/cn/doc/app/50111-10

另外一个就是使用这种功能需要进行实名认证:

目前看来对于这种认证最简单的侵入性最小的还是手机号认证。为了进行手机号认证,也看了几种方案,一种是基于 uni 自带的证号认证功能,

https://doc.dcloud.net.cn/uniCloud/uni-rpia/mobile-verify/intro.html

相关费用报价其实略贵一些:

真题来说两毛多钱一次,这价格属实是贵了点。随便认证个几百人,这几十块钱就没了。虽然不多,但是毕竟作为一个不挣钱的项目该省的还是要省的。那么,除此之外,那就是直接短信验证码了,uni的短信验证码费用为 0.0355,这个价格比上面的便宜了很多。

然而,这个资源包太大了,有效期一年:

看了下阿里的,虽然大家贵点,但是相对来说资源包可以买的小一些,用完再买,也不会存在浪费的问题:

至于切换,那等短信需求量大了再说吧,毕竟现在只有上架华为应用市场的需要实名,并且不知道这种实名认证是不是满足需求。如果满足不了,那就得再换别的方式了。

既然,已经到了这里,也不在乎多折腾一个应用商店了,登录三星的开发者后台,不得不说,三星还是简单,除了提示改密码,别的什么都没问题。添加应用,补充资料。上传 app 提交审核,然而,晚上收到一封邮件,说审核被拒了,而至于为什么被拒,简单粗暴给了个 apk 扫描没通过的链接。

按照文档的提示,给 google 发送扫描误报请求。结果添加到 google 邮件里的附件又被扫描了,禁止下载。这尼玛真是陷入死循环了,

只好压缩之后重新给 google 发送文件,这尼玛是属于套娃了吧。真是服了。

zhongling@MacBookPro Pycharm_projects % cd dayima-vue3
zhongling@MacBookPro dayima-vue3 % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      49
zhongling@MacBookPro dayima-vue3 % cd ..
zhongling@MacBookPro Pycharm_projects % cd dayima_backend
zhongling@MacBookPro dayima_backend % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      50
zhongling@MacBookPro dayima_backend % cd ../dayima_uniapp
zhongling@MacBookPro dayima_uniapp % echo "=== 2025年12月提交次数 ===" && git log --since="2025-12-01" --until="2025-12-31" --oneline | wc -l
=== 2025年12月提交次数 ===
      89

 

最近为了处理这些问题,最近这段时间确实比较忙,白天公司的事情,晚上自己的事情,差不多每天都到十一点多,偶尔一天十一点上床了,对象问了一句:“今天咋这么早?!”

再次感谢最近参与测试,提出 bug 的宝子们。

大致小新笔记坊云无心天天向上vind、夏末~、乖、lgin、jason、jungle
以及其他最新入群的宝子,后面几个我对不上号,或者有遗漏的也补充下哈可以给我留一下地址哦,测试结束后给每人发红包,嘻嘻。

尽管道阻且长,

但是行则将至,不是吗?

  •  

免费领取2个月天翼云电脑8核16G

昨天收到了个短信,可以免费领取2个月云电脑,奈何以前贼想拥有的时候咋不来个活动让我先体验一把,前几个月已经整了个长期的。。。
想玩玩的可以领取一下,微信扫码跳转到小程序可以领取2个月,8核16G内存120G硬盘,有需要的朋友可以体验一把。
我之前自己的是每1个小时不连接的话就会自动休眠,搞了个不关机插件可以达到长期运行,这个免费试用的好像是不自动休眠,我昨天领取到现在也没显示休眠。
1
2

  •  

蜀道难 — 有偿招募 Google Play 测试用户

懒惰,有时候带来的负面效果,后期想要修复的时候,要付出的代驾比当时处理要复杂的多。更恐怖的是,哪怕付出了这么多的代码,依然无法达到最开始的效果。

当 google playstore 开发者给我发邮件提示账号快过期的时候,并没有太留意,后来就给忘了。当然,gmail 的邮箱已经收到了数次提醒,但是由于那段时间不怎么翻墙,导致并没有看到这些邮件,等看到邮件的时候账号已经被用了。

看到这个停用的原因,真的是让人崩溃,这 tmd,当时但凡翻墙了,也就是点几下鼠标的事情。现在好了,重新注册账号依然面临一系列的问题。appid 被占用,旧应用无法下架,无法转让。现在连上线发布都需要面临另外一个问题,那就是需要开启封闭测试,只有封闭测试通过之后才能有发布正式版的权限。

google play开发者给出的答案是:

针对新创建的个人账号的测试要求简介
测试是应用开发流程中不可或缺的一环。通过持续对应用运行测试,您可以在公开发布应用之前验证其正确性、功能行为和易用性。这能让您及时解决发现的技术问题或用户体验问题,最大限度地降低这些问题对用户的影响,从而确保您在 Google Play 中发布的是应用的最佳版本。如果开发者在发布应用之前经常使用 Play 管理中心的测试工具进行测试,他们的应用将能够带给用户更优质的使用体验,从而在 Google Play 上赢得更高的评分,取得更大的成就。

为了帮助所有开发者确保提供高品质的应用,我们提出了新的测试要求。如果开发者使用的是 2023 年 11 月 13 日之后创建的个人账号,则其应用需要先经过测试,然后才能在 Google Play 上发布和分发。若应用未经过测试,系统会停用 Play 管理中心内的部分功能,例如正式版(测试和发布 > 正式版)和预注册(测试和发布 > 测试 > 预注册),直到开发者满足相关要求为止。

测试要求概览
如果您使用的是新创建的个人开发者账号,则必须对您的应用运行封闭式测试,且至少有 12 名测试人员在过去至少 14 天内选择持续参与测试。满足上述条件后,您便可以在 Play 管理中心的信息中心申请正式版发布权限,以便最终在 Google Play 上分发您的应用。申请时,您需要回答一些问题,帮助我们了解您的应用、测试流程及正式版发布准备情况。

下文详细介绍了不同类型的测试轨道和相关要求,以及关于申请正式版发布权限的更多详情。

了解不同的测试轨道和相关要求
Play 管理中心提供不同类型的测试轨道,以便您逐步扩大测试范围并改进应用,力求达到适合面向数十亿 Google Play 用户发布的水平。

内部测试:在完成应用设置之前,您可以自行将 build 快速分发给少量可信测试员。这有助于您排查问题并收集早期反馈。通常,build 被添加到 Play 管理中心几秒钟后,就会向测试人员开放。虽然内部测试并非强制性要求,但我们建议您从这里开始。
封闭式测试:利用封闭式测试,您可以与由您控管的众多用户分享应用。这样一来,您可以在发布前修复问题,并确保应用符合 Google Play 政策的要求。您必须先运行封闭式测试,然后才能申请发布正式版应用。当您申请正式版发布权限时,必须至少有 12 名测试人员选择参与您的封闭式测试。他们必须在过去 14 天内选择持续参与。完成应用设置后,您即可启动封闭式测试。
开放式测试:让您可在 Google Play 中发布测试版应用。进行开放式测试时,任何人都可以加入您的测试计划并向您提交非公开反馈。请先确保您的应用和商品详情已经准备好在 Google Play 上架,然后再选择该选项。如果您拥有正式版发布权限,则可以使用开放式测试。
正式版:让您可通过 Google Play 面向数十亿用户发布应用。您需要先运行符合我们条件的封闭式测试,然后才能申请将应用发布为正式版。在申请时,您还需要回答一些与封闭式测试相关的问题。当您申请正式版发布权限时,必须至少有 12 名测试人员选择参与您的封闭式测试。他们必须在过去 14 天内选择持续参与。

如果开发者使用的是 2023 年 11 月 13 日之后创建的个人账号,则其应用需要先经过测试,然后才能在 Google Play 上发布和分发。若应用未经过测试,系统会停用 Play 管理中心内的部分功能

测试要求概览

如果您使用的是新创建的个人开发者账号,则必须对您的应用运行封闭式测试,且至少有 12 名测试人员在过去至少 14 天内选择持续参与测试。

重要提示:向测试人员强调,他们需要选择持续参与至少为期 14 天的封闭式测试。

说实话就是现在google 的做法是越来越看不懂了,关键是很多操作也不知道后续该如何操作。这就很蛋疼了,这种事情当然更好的办法是去咸鱼之类的找个专门做测试的。

测试资格需要加入这个 google 群组(需要加入群组之后才能参与测试):guimiquan@googlegroups.com

https://groups.google.com/g/guimiquan

我也没咋用过这个东西,不清楚怎么去处理这个破玩意儿。有时候感觉老外设计的东西,的确是不怎么符合自己的使用习惯。

ap测试地址:

https://play.google.com/apps/testing/gma.dayi.app

app商店地址:

https://play.google.com/store/apps/details?id=gma.dayi.app

网上依然能找到这种测评服务了,价格大约 200+。

现在的问题在于重新注册开发者账号话费了 30 美元,现在不上架恶心,上架也恶心。既然如此那还是折腾一下吧,不过鉴于自己手上没那么多的设备,想请各位宝子帮帮忙测试一下,想参与的加一下 google 的群组。需要 12 名测试人员,根据提供的测试账号数量,在测试完成后给于相应的报酬,如果有时间,或者闲着没事的希望可以板帮我哦。

目前我自己还在修改一些问题,希望大家除了加下 google group,顺便加下群哦,QQ群:777692924obaby@mars

需要大家开始帮忙测试了会统一通知一下的。

都说蜀道难,现在发版比蜀道还难,蜀道难都是看得到的难,而这发版,完全是不知道的陷阱。

最后说下测试报酬哈,鉴于价格大约都是 200+,12 个账号,按照每个账号 20 元红包来计算(提供几个测试账号,会获得相应账号数量的红包)。如果不想要红包的,可以寄写真照片(3 张),如果喜欢的话。

如果种种原因,到时最后测试没通过,红包依然会发放,不过金额就变成 10 元了哈。毕竟不想让大家白费精力,这个钱属实不多。聊表心意,主要是这个应用也没啥盈利能力,全靠用爱发电,小女子先行写过啦。

  •  

脑电波

本人在地球上目前接触到的一些魔法:
磁铁(电磁力)除了引力之外的魔法,本身就细思极恐,而且这个力居然在宏观尺度都那么强大,有时候还很难分开,而且稀土矿是国内优势,物美价廉呐!
活性炭(分子间作用力) 碳这个元素实在是太特殊了,四个抓手,吸附力max,而且还能做成很薄很薄无限表面积,脏东西的引力黑洞!而且这个元素遍地都是,任何东西添加一点活性炭都会事半功倍,比如猫砂除味,冰箱除味,水质净化,空气净化,这不是量子力学,这是真的科学!

本文发表于鸦鸦的巢穴,感谢您的订阅!如需评论请前往脑电波

  •  

果然一体检全身毛病

 年底了,公司安排年度体检,之前一直没通知,上周才知道,就约了个周六去了,公司在美年大健康订购的职工体检套餐,只需要每个人在线预约,然后到时间去就行了,约的周六早上八点,我这附近还没有这个体检机构,只能坐地铁去最近的庐阳区的一家,周六还起了个大早,六点半就爬起来了,收拾好坐一个小时地铁,刚好七点五十到那,好巧,还碰到了另外两个同事。
 到那也是先抽血,然后就是一些常规检查,还有就是彩超和DR,这几个还算比较有用的吧,前前后后也搞了一上午的时间,正好套餐里带了个早餐,体检完吃个饭,算是午饭了吧,哈。
1
 今天上午发来短信说是体检报告出来了,就下载来看了下,好家伙,去年都还没啥呢,今年这一年造的,出现了各种小毛病,特别是脂肪肝和肾结石前兆有点出乎意料了,归根结底还是吃出来的病,人只要一胖就各种毛病,特别是我们这工作,还经常久坐,都想找坐办公室的工作,坐办公室的工作也不是那么好干的啊。
 看了下报告,都指向一个原因,胖,需要多运动,后面晚上吃饭一定得注意点了,尽量少吃,然后出去走走活动一下。
2

  •  

完美主义

小时候跟姐姐学的用扑克牌算命,洗牌就那么一张一张的抽出来排上去,从 1 开始一直排到 13,不同的花色对应着好或者不好,每个数字代表不用的意义,事业啊、婚姻啊、学历啊等等。有时候,为了抽到一排红心,自己会将扑克牌在洗牌之后重新排号,按照一个特定的顺序,抽几张扔几张。最后总是能拿到一排红心。

慢慢长大之后发现,不要说一排红心,哪怕能拿到一颗红心已经实属不易。为了这颗所谓的红心,要付出的实在是太多太多。

反常的气温,忽然又升高到了十七度。给人一种暖春的感觉,周五的时候,对象说给宝子约了周六的牙医。宝子的这个牙,已经成了一种非常严重的问题。由于之前一直吃手,后来用这种手段干预,戴手套,贴嘴等等,虽然不吃了,但是舌头还是不自觉的就往前顶,现在牙齿已经有些变形了,甚至连骨头都开始过度生长。

去妇幼、齐鲁去问诊,给的建议都是等牙齿换完再处理,但是现在似乎依然等不到那个时候了,再不干预,以后要该起来就更难了。原本想着,简单的咨询一下,但是在经过一系列的检查之后,觉得还算靠谱,给的医疗建议也在接受范围之内。决定不再折腾了直接在这里处理,费用六千五。为这些所谓的坏习惯的付出依然不止这些。

有时候懒惰真的会付出代驾,小的时候想下狠心纠正这种坏习惯,但是宝子的姥姥各种觉得残忍,阻挠。最后的结果就是,虽然当时是痛快了。后患却没那么容易消除。虽然现在自己依然不是一个完美主义者,但是,这种过失,现在想起来却也时常后悔。

周末,有时候感觉时间是真的少,各种乱七八糟的事情就占据掉了大半。剩下一点点的时间,来处理下那些乱七八糟微不足道的事情。之前 google play store 的账号,因为长时间没登录被停用了,导致原有的闺蜜圈 app 也被下架。在重新注册开发者账号,想要重新发布应用的时候,提示 appid 被占用了,给 google 发邮件申请转移,给的答案是账号可以解除封禁,可以登录,但是转移却是一直失败的,根本没有转移权限。

既然如此,那暂时也就不再尝试使用原来的 appid 了,毕竟,这个流程一直持续下去,也不知道会到猴年马月能结束,就酱紫吧。完美主义,自己坚持有个 p 用,还得条件允许才能完美。

除了 google play,其实还有一个平台是自己之前也想上的,那就是鸿蒙,uniapp 刚支持打包鸿蒙 应用的时候,自己就尝试过向鸿蒙的迁移。然而,由于项目框架较老,需要做的工作不止一点点,需要先将 vue2 升级到 vue3,然后在将 vue3 版本打包成鸿蒙的 app。

升级这一步就不是很顺利,作为一个初学前端(vue 框架)的菜鸟,最开始项目建立的时候,代码结构设计的并不是非常好,并且硬编码了很多 vue 2 only的一些代码。升级到 vue3 之后,勉强编译通过,运行到了鸿蒙系统上。

再后来,这件事情也确实没什么动力,就不了了之了。然而,就在上周又接到了一个广东深圳的电话,接起来之后说是鸿蒙开发者中心的。问有没有app 升级或者开发计划,说看到在应用商店上架的闺蜜圈 app了,并且说帮忙给建立技术支持服务群,协助将项目从 vue2 到鸿蒙系统 app 的发版。 

在接到这一通电话之后,总觉得不做点什么真的对不起鸿蒙生态的付出。(这个电话打了很多次了,一直没接)之前,自己的那种完美主义追求,想要在国内的各大应用市场上架。然而在多年以后,所有的手机应用市场都关上了针对个人开发者的大门,除了华为。国内的个人开发者,真的连狗都不如。

在自己开发第一款 app 的时候,国内应用商店华为、小米、锤子、魅族还是针对个人开发者开放的,只是现在小米把个人开发者推出了门外,魅族也关闭了那扇大门,锤子死了。只剩下华为还算是对个人开发者开放,更何况现在,人家都找上门了,自己又有什么理由不做出点努力呢?

而至于完美主义,现在依然不可能了,如果要做,也只能部分完美。让那些自己有能力去完美的地方,能稍微完美一点吧。

为了能升级到 vue3 和支持最新版的鸿蒙开发工具,将 hbuilder 升级到了最新版,切换到原来 vue3 的分支,不得不说,最新版的 hbuilder 在鸿蒙的支持上友好了很多。

配置好一系列工具和插件之后,甚至应不需要在使用鸿蒙开发者工具打包就可以直接运行到模拟器了,虽然提示只支持 arm 架构的模拟器,但是运行是完全没有问题的。

当然,现在升级最大的优势在于,通过 cursor 可以帮忙解决大部分的 vue代码升级问题。

的确减少了自己的大部分工作量,只需要关注那些 ai 解决不了的问题就 ok 了。两天陆陆续续的修复,最终还是在鸿蒙系统上运行起来了,也修复了大部分的错误,当然,这个升级之后的功能,还需要进一步的细致测试。

实际运行效果:

有的事情,开始固然是艰难了一些,甚至,很长时间都看不到方向,然而,做了也就那样,没什么做不了。也没什么做不到。

网上总是说 hbuiler 这不好,那不好,性能太差,不如原生。有哪有啊完美无瑕的工具或者框架,如果通过这个工具或者框架实现了自己的目的,那么这个框架或者工具就是足够优秀的,哪怕不完美。国外的东西不见得就是好的,国内的东西也不见得就是不好。很多程序员为了争论 emacs 和 vi 到底哪个更好,能口诛笔伐。甚至连 vi 党和 vim 党都能同室操戈,我作为一个实用主义党是在不明白这种争论的意义和价值。

当然,其实这些年我说 hbuiler 好不是一次了,我也是目光短浅,没用过 flutter 之类的其他的跨平台语言。仅仅局限于自己的鼠目寸光,与我而言,这解决了我的问题,就足够了。通过自学,两个月的时间,能让我通过这门语言或者工具来做一款产品,这就够了。

只是,现在我站在了自己写的屎山代码上,有太多的东西需要优化,有太多的结构需要调整。

或许,是时候放弃完美了,对于用户来说,你的代码是不是屎并没那么重要,只要给用户呈现的不是💩就完了。

  •  

28岁破壳日冒泡

 半个月没有更新了,回归了平静的生活,经过上次公司“放假”回老家待了几天,带回去的东西都收拾的差不多了又独自一人返回合肥了,开始了宿舍集体生活。
 回来的第二天就找房东把之前租房那里的钥匙还回去了,退租过程也很和谐,水电燃气费用在回老家之前,媳妇已经全部计算好并且充值进去了,退租的时候房东又每个表都当面确认了一遍,也没啥问题,然后就是一手交钱一手交钥匙,过程也比较愉快。
 由于宿舍这里已经有宽带了,自己原来办理的360包年的300M就不需要了,上周六吧,又跑了一趟联通营业厅,把宽带销户掉,八月底交的一年的费用,应该还剩个近300块,注销之后营业员说下个月10号之后才能把剩余的费用退回,只能下个月再去一趟了,还好可以退,不然白瞎一年的宽带费用了,回老家之前还想着挂闲鱼租给有需要的人呢,这下可以直接注销,挺好。
 由于这次封网时间较长,回来之后也是开启了贼忙碌的工作,一个人的生活好像没有了色彩一样,两点一线,每天就发视频给家里人讲讲话,其他时间感觉说话都变少了。
 平平无奇的日子也没啥好玩的事情可以记录了,今天是人生中的第28个破壳日,也是冒个泡吧,来了一周了也没出去吃饭,都是回来自己倒腾随便做点吃的,今天在各位家人的赞助下,也是吃上了喜姐炸串的大鸡腿,也是吃上肉了,哈哈。
1

  •  

绽放

晚上下班之后,沿着那条跑过无数次的公路奔跑

路边的树上依然没了绿意

一阵北风吹来

树上的黄叶控制不住的在空中飞舞

身不由己的在空中划出一条不规则的曲线

落在地面上

一阵更猛烈的风吹来

地上的树叶又被裹挟着飞到了空中

耳机里伴随着风声传来的是张韶涵的《阿刁》

想大口呼吸

却不禁让狂风灌满了整个胸腔

虽然已经解开了薄羽绒服的扣子

微微出汗的身体依然想要抛弃这沉重的枷锁

只是啊

或许这被嫌弃的枷锁

是多少孩子的爱而不得

或许

我们生不逢时

或许

我们一路荆棘

但是

孩子啊,我们还是要努力的绽放

  •  

意义

中午吃完饭,打开电脑下载了点乱七八糟的东西,期间传来了 iphone 的两次短信提示音。平时垃圾短信、垃圾电话一大堆所以也没去看到底是什么东西。等到要出门上班的时候,看了下 p70 手机,推送栏有一条消息:“……驾驶 中型以上……汽车…… 超速 ……不超过 20% ……”,都不用仔细看就知道是超速了。

距离上次收到超速短信已经过了数年,最近几年除了路边停车收到的罚单,以及对象在快速路上超速吃了一张罚单。从来没再收到过超速罚单,倒不是因为没超速,只是山东高速的容错率还是蛮高的。

看了下拍的现场图片,应该是在原来的济潍高速被拍的。区间限速路段 22km 长度,限速 120km/h,实际的区间速度为 134km/h。之前跑 G20的时候,基本区间限速都是压着 140 左右跑,但是却没被拍过。只有回老家的时候才会跑济潍高速,跑的时候一般是压着 130 跑,然而这次稍微快了点。这一下,两百块没了,说实话就是这两百干啥不好呢,为什么要超速呢。

带着家人跑高速的时候,对象总是提示说,不要超速,被罚款太不值了。有时候被说,心里也感觉不爽,但是真的压着 120 跑,总是觉得有些憋屈。然而,等真被拍了之后,交了两百块钱,现在也会想着 200 花的有意义么?平均时速降 2km/h 就完全可以省掉这 200 快了。也常在网上看到有人说,限速 120 超速 20% 不要紧,然而,这个超速 20% 以内不要紧,不是所有的高速都适用。如果要超速,还是贴着 10% 之内超吧,毕竟,交的罚款都赶上来回的油费了。

周日带着宝子去上网球课,终于有重新能达到隔网接球了。跟着前一个教练学的内容,扔了一年多的钱,学了个不标准的动作。现在每节课都在纠正之前的错误,也在承担之前这错误的姿势带来的影响。目前已经又消耗掉 20 节课时费了。好在现在纠正的还算可以,最起码就是球能打实了,能听到打球的声音了。总说及时止损,有时候却因为自己的懒惰和松懈,浪费了太多的时间在那些错误的地方,然而这些错误最终的买单人还是自己。

有时候也不得不思考究竟什么是有意义的,学习?工作?玩耍?还是什么其他的东西?

然而,有时候想想这些都没什么意义,或许真正有意义的事情是做自己喜欢的事情,让自己快乐。然而,这种快乐又从何而来呢?是兴趣爱好还是什么其他的东西?亦或者说是一种无所谓的坚持?

坚持有意义吗?答案就是我也不知道有没有意义,只是从某种意义上在坚持一件事情。最近晚上跳绳的时候,有时候会一边看电视一边跳,这么跳的好处是看到入迷的时候,跳绳反而觉得没那么累了,甚至很容易就跳到 4000+。

昨晚跳的时候,看的第一集电视剧已经过半,在第一集结束之后,继续后面一集。既然开始跳了,自然是要坚持下去的。就这样,刚好差不多一个小时多一点的时间,完成了一万个。

虽然最开始的 4000 还能有点感觉,但是到了 6000+以后反而没什么太大的感觉了,除了有点出汗,并没有太吃力。或许这就是坚持的意义吧,只有牛马有个一个好身体,才能赚更多的牛马费不是吗?也只有更多的牛马费,才能给宝子营造更好的生活环境。

总说年轻人不愿意结婚生子,之前忘了在谁那里看到的一个专家的言论说,那年轻人不愿意生孩子,并不是年轻人没有责任心,而是现在的年轻人责任心比上一辈高太多了,知道为自己的孩子负责,知道为他们创造更好的生活条件。自己没有能力的时候,不想生而不养。

而这个好的条件,有需要付出多少的努力?然而,这些所谓的努力又有多少事有意义的?对于多数人来说,从来不会希望你过得比他们好。上周回去奔丧的时候,在殡仪馆,大姐夫跟二姑家的堂哥在那里聊天,自己去厕所的时候路过。堂哥玩笑了一句:“发财了,不认识亲戚了?”

我幽怨的回了一句:“嗐,发个屁才啊,不单工资没涨,还降工资了……”

简单的聊了几句就去洗手间了。努力或许从来都没什么意义,别人看到的都是你现在的表象,至于是不是他们希望的,大概率并不是。而之所以说这句话,大概率是因为这次回去自己没有开大白而是开粉皮回去的。毕竟这个时候在一堆车牌号鲁 V 和鲁 G 的车里,找到一辆鲁 B 的车还是蛮容易的,自己下车的时候他们大概率也是看到了。虽然不是什么豪车,但是也能戳痛某些人吧。不管承认不承认,有时候看到别人开着跑车出现,自己的内心也会触动,那种内心的由衷的羡慕,因为这些东西大概率自己这辈子无法体会了。而这些东西,不是自己努力就能得到的,如果要按这个来平评判,自己的努力就没有任何的意义。

穷,固然会被人看不起,但是你过得比他好,大概率他会记恨你。

于是,多数时候,我总是习惯展示给他们自己想看的那个自己。更像一个虚伪的骗子,哪怕是自己的家人,有时候竟然也会带着一份伪装,只是不知道这份伪装如果真的去掉,会变成什么样子?大概率不是自己想要的样子。

自己不断追求各种所谓的意义,以前却很少追求快乐。在某些时候,甚至为了单纯的快乐竟然有一些负罪的感觉。就想那个拉磨的驴,偶然从磨盘上下来之后,也会怀疑自己存在的意义和价值。内心的不自信,太多的时候需要所谓的外界的认可来证明自己的价值。诚然,这种病态从来没有真正消除过。

跟大多数人一样,人生的轨迹似乎也没什么太多的变化。上学、工作、结婚、生子,在父辈的言语中,听到的最多的就是多生几个孩子。这一辈子什么都不会留下,能留下的只有自己的孩子。然而,留下那么多孩子干嘛呢?如果给不了好的生活,宁可不要孩子。自己摸爬滚打从按个小村子里爬出来,难道要让自己的孩子继续重蹈覆辙吗?

四叔家的姐姐得了脑瘤,在鬼门关走了一遭之后,心态变了很多。自己很喜欢这个小姐姐,小时候经常在一起,长大了之后见的少了,但是感觉依然很亲切。然而也只有过年的时候才有时间去看看她,身体瘦削,这几年气色好了很多。她说:“有时候读书太多也不好,你看你跑那么远都见不到你。”

“这哪里算远,一点都不远”我答道,我握着她的手,能感到那只手冰冰凉凉。我想努力的给她暖一下,就这么紧紧的握着。

也许从来没什么所谓的感同身受,哪怕经历过同样的事情,每个人依然有每个人的感觉。而自己能做的,也仅仅是尽量去体会他人的内心的感觉。

人生,或许从来没什么意义。也许只有自己能给自己的人生赋予意义,快乐活着,痛快生活。

只是,说起来从来都简单,然而,追寻却从来不易。

  •  

如何下载电报 Telegram 视频

多数时候,要下载电报的视频并不需要太复杂的操作,直接点击视频资源的 save to gallery 就可以保存视频了。

然而,并不是所有的群组都能保存视频,有的群组甚至连截屏和分享的权限都没有,自然也就没法下载视频了。

这种群组不单没有开放下载群贤,连复制和转发的权限都没有。所以要想下载这种视频,之前我尝试了几种办法,包括但不限于:

1.通过浏览器插件下载

2.通过 tg 的开发者账号权限,通过接口获取数据

3.tdl

然而,第一种方法,最大的问题在于很多插件都是需要付费使用的。最多能下载四五个视频,超过这个数量就要收费了。另外,今天又试了一下发现插件失效了,应该是本地插件没有更新导致的。

之前用的时候还是可以的。

第二种方法最大的局限在于注册 tg 开发者大概率会直接失败,基于 tg bot 的实现方式,没有开发者账号是无法实现的。而注册失败,就一个弹窗提示 error,就很沙雕。也不告诉你为什么,看网上分析说什么各种风控,就一个破聊天工具,哪里那么多的风控?

原因:
• 当前 IP 地址地理位置受限,或是当前ip被滥用导致被feng控,使用代理可能会被 Telegram 拦截。
• 账户状态异常,例如被标记为垃圾账号、未绑定手机号、账号过新等。
• 多次重复尝试触发风控机制,系统会暂时禁止继续操作。
• 浏览器环境异常,如关闭了 JavaScript、使用了隐私插件或禁用了 Cookie。
解决方案:
• 使用稳定、纯净的住宅代理 IP
• 避免使用数据中心代理、JC节点这类 IP 容易被识别为爬虫来源。
• 建议使用真实住宅宽带出口的代理(如美国、欧洲地区),确保 IP 没有黑历史
• 优先选择长期未被用于 Telegram 操作的 IP,降低触发风控的概率
• 使用指纹浏览器
• 隔离的浏览器环境
• 每个指纹配置都是独立的“浏览器环境”

在尝试了无数次之后,只能放弃这种方法。

直到某一天发现了 tdl,不得不说,这是个好东西啊。

温馨提示: 此处隐藏内容需要发表评论,并且审核通过后才能查看。
(发表评论请勾选 在此浏览器中保存我的显示名称、邮箱地址和网站地址,以便下次评论时使用。
(请仔细检查自己的昵称和评论内容,以免被识别为垃圾评论而导致无法正常审核。)

Features:
Single file start-up
Low resource usage
Take up all your bandwidth
Faster than official clients
Download files from (protected) chats
Forward messages with automatic fallback and message routing
Upload files to Telegram
Export messages/members/subscribers to JSON

不过在实际使用的时候,发现几个问题,就是直接用 channel 的方式下载,下载失败了,所以,尝试导出记录的方式下载:

导出的 js 文件会包含所有的媒体资源:

命令参考上图的写法即可,导出之后,使用 json 文件进行下载即可:

tdl.exe dl -f result1.json  --skip-same

  •  

申请免费通配符证书

之前有个项目,用了个域名带下划线,结果申请证书的时候就悲剧了,嗯,你问为什么不买个通配符的?当然是为了省钱,不想花钱。

刚开始还以为是阿里的问题,后来去腾讯发现也不行,换了几家都不行。最后搜索发现这么个情况:

由于受CAB出台的新规(证书中所包含的域名不能有下划线)影响,从2018年12月7日起,所有新签发的证书的域名中不能包含下划线,在2018年12月7日之前如有签发过下划线的域名,则需要在2019年1月14日进行强制吊销。
如有证书用户受到影响,可以通过下述方法进行解决:
1.请用户将含有下划线的域名进行调整,然后CA对该老证书进行重签发,将新的标准FQDN添加到冲签发的证书中。
2.用Wildcard对原老证书进行替换(但如果老证是EV SSL证书,则Wildcard证书不适用于本解决方案,因为Wildcard是OV类型证书,不支持EV)。
域名命名规范 (RFC标准)
互联网的核心技术规范由IETF(互联网工程任务组)通过一系列名为RFC(意见征求稿)的文档来定义。关于域名如何命名的规则,主要在 RFC 1035 中明确规定。

合法字符集:RFC 1035规定,域名中的“标签”(例如 www、example、com 都是独立的标签)只能使用以下字符:

字母 a-z (不区分大小写)

数字 0-9

连字符 -

关键限制:连字符 - 不能出现在标签的开头或结尾。

根据这个标准,下划线 _ 根本不在允许的字符集之内。所以,从技术规范上讲,server_name.com 本身就是一个无效的域名。

所以,虽然之前能创建这种二级或者三级域名,但是,在申请对应的证书的时候就悲剧了。

所以为了不影响业务,尝试申请免费的通配符证书,还是通过 acmes.sh 搞吧。

1.安装

curl https://get.acme.sh | sh -s email=my@example.com

2.配置环境变量:

export Ali_Key="LTA**************6yn"
export Ali_Secret="q435*************EBSaDba5"

3.申请证书:

acme.sh --issue --dns dns_ali -d example.com -d *.example.com --debug

需要注意的是,通配符证书只能通过配置信息自动校验,不能通过添加解析的方式校验,所以要配置 key 和 secret。如果是不同的解析服务商,设置不同的环境变量即可。

zhongling@MacBookPro .acme.sh % export Ali_Key="LTA**************6yn"
export Ali_Secret="q435*************EBSaDba5"
zhongling@MacBookPro .acme.sh % ./acme.sh --issue -d lang.bi -d '*.lang.bi' -k ec-256 --dns dns_ali --dnssleep 60
[2025年11月21日 星期五 16时53分34秒 CST] Using CA: https://acme.zerossl.com/v2/DV90
[2025年11月21日 星期五 16时53分34秒 CST] Multi domain='DNS:lang.bi,DNS:*.lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Getting webroot for domain='lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Getting webroot for domain='*.lang.bi'
[2025年11月21日 星期五 16时53分41秒 CST] Adding TXT value: fcTxHx2osERz8mqJFaV2c0yKvo6vUSMA4SH1FR95PMQ for domain: _acme-challenge.lang.bi
[2025年11月21日 星期五 16时53分44秒 CST] The TXT record has been successfully added.
[2025年11月21日 星期五 16时53分44秒 CST] Sleeping for 60 seconds to wait for the the TXT records to take effect
[2025年11月21日 星期五 16时54分46秒 CST] lang.bi is already verified, skipping dns-01.
[2025年11月21日 星期五 16时54分46秒 CST] Verifying: *.lang.bi
[2025年11月21日 星期五 16时54分47秒 CST] Processing. The CA is processing your order, please wait. (1/30)
[2025年11月21日 星期五 16时54分52秒 CST] Success
[2025年11月21日 星期五 16时54分52秒 CST] Removing DNS records.
[2025年11月21日 星期五 16时54分52秒 CST] Removing txt: fcTxHx2osERz8mqJFaV2c0yKvo6vUSMA4SH1FR95PMQ for domain: _acme-challenge.lang.bi
[2025年11月21日 星期五 16时54分54秒 CST] Successfully removed
[2025年11月21日 星期五 16时54分54秒 CST] Verification finished, beginning signing.
[2025年11月21日 星期五 16时54分54秒 CST] Let's finalize the order.
[2025年11月21日 星期五 16时54分54秒 CST] Le_OrderFinalize='https://acme.zerossl.com/v2/DV90/order/7SLmDTCNs_Qw7zls2HFDpA/finalize'
[2025年11月21日 星期五 16时54分56秒 CST] Order status is 'processing', let's sleep and retry.
[2025年11月21日 星期五 16时54分56秒 CST] Sleeping for 15 seconds then retrying
[2025年11月21日 星期五 16时55分12秒 CST] Polling order status: https://acme.zerossl.com/v2/DV90/order/7SLmDTCNs_Qw7zls2HFDpA
[2025年11月21日 星期五 16时55分13秒 CST] Downloading cert.
[2025年11月21日 星期五 16时55分13秒 CST] Le_LinkCert='https://acme.zerossl.com/v2/DV90/cert/bvCTHYFrpbcye-ASpKoS5g'
[2025年11月21日 星期五 16时55分15秒 CST] Cert success.
-----BEGIN CERTIFICATE-----
MIID/zCCA4WgAwIBAgIQS5gLQdZXhrEHdsgVdwPdgzAKBggqhkjOPQQDAzBLMQsw
CQYDVQQGEwJBVDEQMA4GA1UEChMHWmVyb1NTTDEqMCgGA1UEAxMhWmVyb1NTTCBF
Q0MgRG9tYWluIFNlY3VyZSBTaXRlIENBMB4XDTI1MTEyMTAwMDAwMFoXDTI2MDIx
OTIzNTk1OVowFzEVMBMGA1UEAxMMaGFpa2VodWkubmV0MFkwEwYHKoZIzj0CAQYI
KoZIzj0DAQcDQgAEehCGvspbOuBBQjuauz9ghdv9bmvPGJmlz/LttbMjBlBi31Wh
****************************************************************
qaiMNTAnBgNVHREEIDAeggxoYWlrZWh1aS5uZXSCDiouaGFpa2VodWkubmV0MAoG
CCqGSM49BAMDA2gAMGUCMHlmfYvfKEWtJ/CM7UNx6sJPwzu5fU1c5j8v2Oj4REQh
/KE0yJHo3YZkXegvxlSAPAIxAOPw+ZwRsatCaRL8yEGp4mX0umkKx+XbtTlus5NK
aBIOcZiS307CH5mXKOb1jXMPpg==
-----END CERTIFICATE-----
[2025年11月21日 星期五 16时55分15秒 CST] Your cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/lang.bi.cer
[2025年11月21日 星期五 16时55分15秒 CST] Your cert key is in: /Users/zhongling/.acme.sh/lang.bi_ecc/lang.bi.key
[2025年11月21日 星期五 16时55分15秒 CST] The intermediate CA cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/ca.cer
[2025年11月21日 星期五 16时55分15秒 CST] And the full-chain cert is in: /Users/zhongling/.acme.sh/lang.bi_ecc/fullchain.cer
zhongling@MacBookPro .acme.sh % start
/Users/zhongling/.acme.sh/
zsh: command not found: start
zsh: permission denied: /Users/zhongling/.acme.sh/
zhongling@MacBookPro .acme.sh % start /Users/zhongling/.acme.sh/
zsh: command not found: start
zhongling@MacBookPro .acme.sh % open /Users/zhongling/.acme.sh/
zhongling@MacBookPro .acme.sh % open /Users/zhongling/.acme.sh/

最终,省去了更换域名的麻烦。先将就用着吧。

 

  •  

送别

周二早上,刚换好衣服准备去上班。手机响了,一个陌生的号码,归属地显示的是潍坊。之前也会接到各种陌生号码的电话,为了解决 iphone 的骚扰电话,开了自动录音功能。但是,这个功能并不稳定,录音有时候显示不完整,有时候对方打了很多次电话,手机却连响都不会响。周末的时候把这个功能关掉了。

电话接起来,对面传来一个熟悉的声音,是四叔家的哥哥:“二大爷(二伯)走了,明天的公事……”

“好的,我知道了,我明天回”简单的答复之后就挂断了电话。

快到中午的时候,又接到电话,通知了下时间,早上八点半。这个时间,如果早上从青岛走,六点之前就得出发。决定下班之后,往回赶。工作日的晚上,路上的车并不多。七点多出发,到家的时候也九点多了,好在提前远程开了空调,到家的时候,温度也到了 20 多度了。

客厅的桌子上还放着国庆走的时候落在上面的零食,袋子开着,并没封口。试了一下,已经潮了。把桌子上的东西都收拾了一下扔到了垃圾桶里。

好在冰箱里还有上次回来的时候放的饮料,就住一晚。没开电视,也没开饮水机,两瓶魔爪下肚之后,感觉也没那么渴了。躺床上,刷会儿手机,眨眼已经十一点多了,想着早上不能起太晚,简单洗刷就睡了。

睁眼看了下时间,七点,爬起来收拾下,准备出门。收拾好一切一定七点半了,刚要出门的时候姐姐打电话问出门了没。

开上车,去古城煎包吃个早餐。到小学门口的时候,依然堵的不动了。果断调头往另外一个方向走,只是,这县城的生活节奏,慢吞吞,一个红绿灯都过不了几辆车。那种闲庭信步的悠闲感,真的适应不了。

要了两个煎包,一碗豆浆。到角落的桌子边坐下来,开始吃,一口下去,竟然齁咸。一向味道不错的煎包,今天的确有失水准。好在还有碗豆浆,草草吃完,继续往殡仪馆赶。

等自己赶到的时候,依然很多然都到了。只是,现在很多人都变了模样,年轻的孩子们,自己甚至都已经认不出谁是谁。自己同辈的姐姐、哥哥们却基本都没怎么变样,很多人也老了很多。

这是自己第三次来这个地方,第一次是送自己的父亲,第二次是送对象的爷爷,这第三次是送自己的二伯。

第一次来的时候,依然太久远了,一眨眼二十年了。很多的事情都忘记了,记忆中仅剩的一点点记忆,就是领骨灰出来的时候,给的骨灰盒太小。烧完的骨灰,竟然没法完全装到骨灰盒里。有那么一块骨头还翘在外面。二伯用脚踩着骨灰盒才把骨灰盒给扣上,那小小的骨灰盒啊,竟然死了都那么不体面。因为父亲生病欠了太多的钱,那时也确实买不起更好的骨灰盒,更好的骨灰盒会更大一些。

现在二伯自己也走了,国庆期间自己又去探望了一次,说稍微好转了一点。尽管如此,但是这么久了没从重症监护室出来,总觉得,也是早晚的事情,这一天总是会来的。

到现在,父亲兄弟四人,现在也仅剩下四叔一人了。二伯在老家没有什么家产,也没房子,最终的选择就是县城的公墓。之前总是远远的看到过公墓,或者在电视上看到。自己走进公墓之后才发现,真的是一排一排,写满了名字。所有的人,最后终将成为一抔黄土,埋没在这黄土之中,两代之后,再也没人记得这个人曾经存在过。此时,这是个就真的从这个世界消失了。

一切结束后,回程的路上,想着去壳牌加油站加个油,预付卡还有两百多,依然不够一次了。又充了500 进去,结果到加油站 发现加油站给围起来了,不知道是在装修还是干嘛。这 500 块钱,不知道猴年马月才能再用上了。

今天早上出门的时候,看到绿化带的月季依然坚强的绽放着,虽然已是寒冬,哪怕不合时宜,只是赶上了这样的时间,也要努力的绽放。至于其他的,由他去吧。

  •  

又是计划赶不上变化的一周,启程回家

 发这篇博文的时候已经躺在了河南老家的床上,本来还没想这周回来的,前几天还计划这周末再去哪里玩一下,下周22号再回来了,结果突然来了个变故,不知道是哪位人才想出来的歪点子,由于第十五届全运会的原因,近期我们这不让干活,从5号到22号,本来好好的摸鱼几天,周三晚上公司突然通知开会,让从13号开始一直到23号,直接放假了,然后工作日不记工资,也就是周四周五,还有下周5天,美名其曰放假了,这不是纯纯停薪留职嘛,这样搞和临时工有什么区别,这次既然有这个开头,后面我感觉再有这种长时间的不让操作的时间说不定都会实行这种方式,那到时候次数多了可就不像这次这么坦然接受了。
 这整得正好赶上搬家的时间,既然已经这样了,那就更改计划,直接开始收拾东西,该装车装车,大件装不下的发快递,昨天差不多收拾了一下又寄了300块的德邦,其他一些重的东西都装在车上了,然后我的东西都搬到给我安排的公司宿舍了,前几天也去看了下宿舍环境,本来觉得空间啥的还都挺大的,唯一美中不足的就是进去没有个门,结果送过去东西之后也是摆了一地还没收拾显得乱七八糟也没那么大空间了。。。过几天回去再收拾吧。
1
2
 上午差不多收拾完东西还带宝宝去中科大拍了个照片,也是证明来过了,以后可是要冲着这目标去的,哈哈。
3
4
 收拾完又打扫了下租房子这里的卫生,喊房东过来看了下,整得干干净净的也没说啥,上次整的洗手台她也能接受,说挺好,不掉就行。中午又在外面简单吃了个饭,12点多开始加满油出发,不得不说非节假日跑高速就是舒服,一路畅通无阻,差不多430公里,四个半小时就到了,到家天还没黑,挺好,不得不说后备箱+副驾驶+一个后座,塞的满满当当,回来卸车的时候用三轮车拉都拉了三车,回来待个三四天吧,计划周二高铁返程合肥,就先留宝宝她们在家吧。
5

  •  

如此有才的秀恩爱

  秀恩爱见的多,如此有才的秀恩爱却少见,看看我朋友夫妻俩在文学刊物上发文给对方致信及回信。

  彼岸和马哥是夫妻,一对十分有才情的夫妻,在我小小的朋友圈里算是才气值最高的了。忘记了是几个月以前,得知他俩作品发表到刊物了便向其索书,彼岸说等期刊出来之后寄我两本,我就等着了,期间还问过一两次书出来没,最近终于出来了,我如愿收到。她还收集了几片美丽的银杏叶赠我,刻意找纯黄无瑕疵的叶子,用精致的小相框夹好了。真是有心!

神泉&银杏

  马哥与我并未谋面,只见于彼岸的叙述里。马哥是一位职业画家,作画是个辛苦活,还经常要去外地好多天,我见过他的工笔花鸟画,虽然我不懂但也能感到十分惊艳。彼岸跟我是快二十年的老朋友了,当时在厦门认识的,也是我的湖北老乡,比我小好几岁。那时的她就已经展露她的才气了,圈子里颇有名气。我们有一些共同的朋友,她在家“大宴宾客”的时候,我也蹭过饭。这么些年了,还能保持联系,属实不容易的,除了珍视友谊,也还需要一些幸运的。

  收到书的第一天就仔细读了马哥的长文《写给妻子的一封信》,又搁了几天读了彼岸的回信《雨天回马先生家书》,还有马哥的几首小诗。十分赞叹他俩文笔,也十分钦佩他俩的十多年的感情,还有他俩对人生的态度。文字版的全文发在《房县文艺》公众号,本文末尾有链接。

  丈夫马哥《写给妻子的一封信》的开头和结尾两段拿出来感受一下。

当我就着窗外的月光将这些承载着心意的文字轻轻叠合,忽然发现每一次倾诉里,都藏着我们灵魂共振的频率。原来爱从不是单向的奔赴,而是两颗心在时光里互为镜像,温柔生长。
……
……
往后的日子,愿你、我,还有我们的小小马继续在时光里种满“一起”的故事,用理解作帆,信任为锚,让每一个现在都成为未来回忆里的糖。那时的我们或许不知道,这缕月光会一路照亮小院的蔷薇、房县的街巷、郊外的蓝天碧草山山水水,以及此刻“一起”生长的时光。你看,春天在发芽,夏天在开花,而我们,正在彼此的生命里,长成最美好的模样。

  妻子彼岸的回信《雨天回马先生家书》也摘录两段与诸君共赏。

你说“云与天空的诀别”,这般意象美得令人心头发紧——原来每一场雨都是云朵写给苍穹的绝笔诗啊。而人间所有未尽的言语,大抵也都化作氤氲水汽升腾至九霄,只待某日倾盆而下,淋湿某个猝不及防的归人。
……
……
“月有阴晴圆缺”恰是道不尽的东方智慧。你看此时窗外那被雨水洗过的月亮,残缺时清辉更甚圆满。若把人生看作长卷,彼此分隔两地的时光不过是留白处的题跋,而所有未完成的句点,终将在回忆里长出新的枝桠。恰如你笔下的云,纵使消散成雨,亦会以朝露的姿态重返花瓣,以薄雾的形貌再吻青山。

《写给妻子的一封信》1

《写给妻子的一封信》2

《雨天回马先生家书》

  十多年的夫妻,仍然有这样的感情浓度,甜不甜,醇不醇,羡慕不羡慕?最好的感情,不是520的红包,不是节日礼物的仪式感,不是18万的彩礼,而是真正感恩对方的付出,心里总是装着对方。他们生活在小县城,经济条件并不是十分优越,家里也无法助力,凭借两个人的努力工作和对生活的热爱,虽然辛苦,也过得幸福美满。

  多年夫妻,过得仍然相安无事的就算凤毛麟角了,多数是一地鸡毛,就如我自己也是痛苦拉扯分手收场的反面典型。上乘的婚姻不需要经营,因为双方契合有格局,各自按照自己的方式自然而然的生活,就能过得很幸福。中等的婚姻需要经营,需要至少一方有能力和智慧,克服人性的弱点,扬长避短获取生活的幸福,然而大多数人都是不具备这项能力的。

  婚姻这么重要而且需要艺术的事情,我们往往是未经任何学习和研究就茫然入场了。起初只看到了爱情的光芒,以为自己也能以爱情为矛,刺破所有挡路的盾,待到跌跌撞撞伤痕累累才知自己曾经多么傲慢无知。当然也有许多人并不知自我反思反而归咎于外,等闲变却故人心,却道故人心易变。也有许多人再婚的时候,并没有「吃一堑长一智」,反而更加傲慢无知。当两人有爱情的时候,算计得失尚且会伤感情,而爱情不足的两个人算计更多市侩得失,结果就可想而知了。

  应当相信爱情,同时也应当相信人性。认清生活的真相后,依然热爱生活。「取法乎上,仅得其中。取法乎中,仅得其下。取法乎下,无所得矣。」以十分的热情对待爱情和生活,未必能够收获十分的回报,但以五分的热情对待爱情和生活,也许就只能得到三分回报七分失望。

  有兴趣看上述两篇文章的完整文字版的,可以打开下面的链接。

《写给妻子的一封信》

《雨天回马先生家书》

  •  

龙葵

龙葵花语的核心寓意包括“沉不住气”象征冲动与不稳重‌,以及‌“执着与坚定”代表对目标、爱情和信仰的坚持,同时涵盖危险与诱惑、孤傲坚韧等多元象征。‌‌

龙葵这种植物,在自己小时候就见过,并且也吃过无数次。那时候,并不知道她的名字。直到后来玩到一款游戏《仙剑奇侠传 3》,看到里面的任务名字,龙葵,搜索了一下,才知道原来是它。

而游戏中各种人物名字,景天、雪见、长卿、重楼,也剧是药材名称。那时候才发现,自己所谓的常识是何其浅薄而又无知。

阳台上的花,花开花谢,落叶枯萎,来来回回换了无数次的花,阳台上空闲的花盆也越来越多。终于,有一天看淘宝的时候,看到了一个熟悉的身影,那就是龙葵。紫色的浆果,跟小时候看到的一模一样,只是,现在哪怕回到村里竟也不常见了。买了两包种子,回来之后收拾了一下花盆,饶有兴致的种了下去。

后来就是浇浇水,倒也不用特别的照顾。同时还买了一包蛇莓的种子,只是,这个种子自从种下去之后,就再也没见过,两个花盆里看不出有任何发芽的迹象,也找不到了那些种子。

终于某一天,自己去给从发芽的地瓜上拔下来的芽浇水的时候。看到了花盆里漏出头的龙葵,欣欣向荣,或许,她更像野草吧。

前段时间还怕没有蜜蜂授粉,龙葵没法结果。前段时间自己还用棉签尝试给花朵授粉,现在看来,似乎也没必要,哪怕没有蜜蜂授粉,现在也结满了小果子,圆圆的,绿绿的。

小的时候,这龙葵的果实,其实不单可以吃,还可以用来当玩具。夏天麦收的时候,也是龙葵大量结果的时候。这下一小段麦秆,去掉两头的关节,就剩下一条通透的小管子。

用指甲把麦秆的一头剥开成对称的十字,弯曲的时候不要太平,稍微有点幅度,把龙葵圆溜溜的果实去掉果蒂,放到那个十字口里。竖着举起来,仰起头,把麦秆的另外一头放进嘴里,向上吹气,龙葵的果子就会随着气流在麦秆的头上转动跳舞。

而至于为什么能跳舞,这个就该去问伯努利了,毕竟伯努利的流体力学方程能解答这个疑惑。

找到成熟的紫色的果子,就会摘下来吃掉。虽然小小的,但是味道真的很好。

然而,并不是所有的东西都有这种顽强的生命力,昨天的时候发现评论呢亲密度标签全部都挂了。

打开网站发现已经暂停服务了:

不是所有的服务都像自己的博客一样,有这么顽强的生命力,到目前位置已经熬死了无数的服务,好用的,不好用的,免费的,收费的。看着无数的服务倒下去。

好在这个东西是开源的,于是干脆自己又部署了一套。

昨天看到 枋柚梓 发布的 umami 升级的文章,之前的 umami 是通过 mysql 编译安装的。而现在这个新版本已经放弃了 mysql,尽管每天都看到升级提示,是在没动力去升级,每次升级编译是个问题,数据库升级也是个问题。于是放弃了所有的历史数据,干脆 docker 重新装了一个,只是换了 pg 数据库之后,确实麻烦了一些。

这几天的访问量和各种指标也的确离谱,应该还是有沙雕在陆陆续续的攻击。

尽管部署好了,但是还有一些问题没解决。现在却也不想解决了,例如那个网站图标不显示的问题。强迫症的问题,等以后哪天想处理了再处理吧,毕竟,还有一切其他的乱七八糟的事情要做。

有时候想想龙葵的花语也挺矛盾的,沉不住气,与执着。然而,这种矛盾又无比和谐,尽管沉不住气,但是却有着坚定的信仰。

挺好的,希望自己也能一直坚持下去。

  •  

来自故乡湖北的桔子

  前些日,收到湖北老乡朋友寄来的一箱桔子,非常感激。写这篇文字除了表达一下感激之外,也是想借此机会抒发一点其它的感想。

  这桔子是生长于风景秀美的丹江口水库之滨,今年夏季湖北大旱之后又大涝,非正常的气候其实不太利于桔子成长,朋友说可能不那么好吃,不过我吃第一个的时候,感觉非常好吃,清甜多汁而且无籽无渣,后续偶有吃到微酸的但口感仍然不错。这些桔子是朋友自家地里种的,也是朋友自己采摘的,因为担心快递损伤还逐个挑选比较结实的果子,果然到深圳打开的时候一个都没有伤了或坏了的。这些桔子并不值许多钱,但这情谊无价,能吃到的人自然也是幸福感满满。

  这棵桔的冬天、秋天、果实。
桔

  风景秀美的丹江口水库。
丹江

  我想到,母亲数年前在家里也种了几棵橘子树,几棵树经历种种意外,今年还剩一棵活着,而且这一棵在今年被收割机在收割其它作物时打掉了半边,剩下的一半今年也还结了桔子。可惜我没有吃到过自己家种的桔子,因为这些年我都没有在这个季节回过老家,而让父母寄快递也是有很现实的困难不必多叙。前阵子和母亲通电话她还说起家里的橘子树和柚子树,而我只能凭空想象。什么时候退休啊,我要去吃自家的桔子。

  幼时家中菜园种过几棵橘子树,是姨妈家送的果树苗,然而种下不久就被邻里偷走移栽到他们家地里去了,但是我们家没亲眼看到没亲手抓到就没用,毕竟在村里的环境,武力值我们家比不过他们,只能认栽,这种事太常见了。我对家乡有各种惦记,但对这些邻里并没有太多好感,大概这也是重要原因之一吧。

  之前还收到过湖北的同学给我寄的伦晚脐橙,也很美味。湖北盛产柑橘,以前没什么了解,离开湖北这么多年,反而真切感受了。湖北不仅柑橘长得好,人也重情谊。

  •  

未获取商用授权?! — 这很百度

上午的时候,收到 梦不见的梦 的一条 qq 消息,说出现了授权问题。

看了下提示域名,大概率就是 tm 百度地图弹的,那个域名做了个足迹应用就这么放着。

就在上个月自己更换 ssl 证书的时候还一切正常,结果现在来了这么一出。本来就是个人开发者,纯自用的东西,还经常收到百度的电话让升级企业认证,之前就是不小心升级了,结果一年要五万的授权费用。

我 tm 就自己玩的,还需要花钱,真 tm 服了。看来这戏破玩意儿都完犊子之后,最后能玩的也就只剩下天地图了。

刚开始是以为嵌入的问题,看了下嵌入页面都会提示:

各种提示信息:

【d45a31】未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314&title=908

并且后来发现,不单纯是弹窗在地图的贴图上也会出现授权提示,不得不说。这狗皮膏药贴的有水平。

对应的 js:

地址:
https://api.map.baidu.com/?qt=cen&b=7597813.822562976%2C687420.7063757228%3B15519477.822562976%2C7879996.706375723&l=5&ie=utf-8&oue=1&fromproduct=jsapi&ak=BxlnBNX55clLsUHVFZlaukyJesN5F5VI&callback=BMapGL._rd._cbk28033&v=gl&seckey=hNJCxM58roJGqVuMKRuYPEZfDZ%2FdhlL4Pp7JxBsDoLNUSc3QN6CRIbBdAJ%2FOt7zPayXwFYMsbLGx0%2BZUValnOg%3D%3D%2ChNJCxM58roJGqVuMKRuYPEZfDZ_dhlL4Pp7JxBsDoLPlabo3s2HGFnIPFXI8e1esM9-LzywgHJdkZjwHcr89ZaWfdPB6XAgd7DE4lcFgxfu8J0x_GywX0u2he7lW2roGgTrZQMyK7kcSPMHFwMyFrx45ktBewEco-xsnR-zdIctg5lIvP6h-iihbsl6ehY9AbrnGwefDt0BtO6gKCedJ8PeXNIUPh2rilmKFv6PZRd0&timeStamp=1763012808396&sign=07d57e493b29
内容:
/**/BMapGL._rd._cbk28033 && BMapGL._rd._cbk28033({"result":{"b":"7597813.822562976,687420.7063757228;15519477.822562976,7879996.706375723","callback":"BMapGL._rd._cbk28033","catalogID":0,"count":0,"current_null":1,"db":0,"error":503,"error_msg":"未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314\u0026title=908","fromproduct":"jsapi","ie":"utf-8","jump_back":0,"l":"5","op_gel":0,"oue":"1","popup":1,"qt":"cen","requery":"","res_l":-1,"res_x":"0.000000","res_y":"0.000000","return_query":"","seckey":"hNJCxM58roJGqVuMKRuYPEZfDZ/dhlL4Pp7JxBsDoLNUSc3QN6CRIbBdAJ/Ot7zPayXwFYMsbLGx0+ZUValnOg==,hNJCxM58roJGqVuMKRuYPEZfDZ_dhlL4Pp7JxBsDoLPlabo3s2HGFnIPFXI8e1esM9-LzywgHJdkZjwHcr89ZaWfdPB6XAgd7DE4lcFgxfu8J0x_GywX0u2he7lW2roGgTrZQMyK7kcSPMHFwMyFrx45ktBewEco-xsnR-zdIctg5lIvP6h-iihbsl6ehY9AbrnGwefDt0BtO6gKCedJ8PeXNIUPh2rilmKFv6PZRd0","sign":"07d57e493b29","spec_dispnum":0,"time":0,"timeStamp":"1763012808396","total":0,"tp":0,"type":11,"v":"gl","wd":"","wd2":"","what":"","where":""},"current_city":{"code":0,"geo":"","level":0,"name":"","sup":0,"sup_bus":0,"sup_business_area":0,"sup_lukuang":0,"sup_subway":0,"type":0,"up_province_name":""},"hot_city":["北京市|131","上海市|289","广州市|257","深圳市|340","成都市|75","天津市|332","南京市|315","杭州市|179","武汉市|218","重庆市|132"]})
地址:
https://api.map.baidu.com/?qt=verify&v=gl&type=webgl&ak=BxlnBNX55clLsUHVFZlaukyJesN5F5VI&time=1763012794639&callback=BMapGL.bmapVerifyCbk
内容:
/**/BMapGL.bmapVerifyCbk && BMapGL.bmapVerifyCbk({"error":503,"error_msg":"未获取商用授权,平台资源与服务稳定性受限;详情信息请前往:https://lbs.baidu.com/faq/search?id=314\u0026title=908","popup":1})

我这个破玩意儿就是纯粹个玩具啊,你何苦这么狠心呢?!

对于这个东西,其实我也没啥好办法,刚开始登录百度 lbs 地图后台提示账号要年审。结果进行账号年审之后依然提示这个错误:

这尼玛就离谱了啊,既然你年审不能解决,那就直接 hook 大法:

<!-- 拦截百度地图弹窗相关请求和Hook方法 -->
    <script>
        (function() {
            // 1. 拦截 fetch 请求
            const originalFetch = window.fetch;
            window.fetch = function(...args) {
                const url = args[0];
                if (typeof url === 'string' && url.includes('api.map.baidu.com')) {
                    // 检查是否是弹窗相关的请求
                    if (url.includes('qt=verify') || url.includes('qt=cen')) {
                        console.log('拦截百度地图弹窗请求:', url);
                        // 返回一个模拟的成功响应,避免弹窗
                        return Promise.resolve(new Response(JSON.stringify({
                            error: 0,
                            error_msg: "",
                            popup: 0,
                            result: {
                                error: 0,
                                popup: 0
                            }
                        }), {
                            status: 200,
                            headers: { 'Content-Type': 'application/json' }
                        }));
                    }
                }
                return originalFetch.apply(this, args);
            };

            // 2. 拦截 XMLHttpRequest
            const originalXHROpen = XMLHttpRequest.prototype.open;
            const originalXHRSend = XMLHttpRequest.prototype.send;
            
            XMLHttpRequest.prototype.open = function(method, url, ...rest) {
                this._url = url;
                if (typeof url === 'string' && url.includes('api.map.baidu.com')) {
                    if (url.includes('qt=verify') || url.includes('qt=cen')) {
                        console.log('拦截百度地图弹窗XHR请求:', url);
                        // 标记为已拦截,在send时处理
                        this._intercepted = true;
                    }
                }
                return originalXHROpen.apply(this, [method, url, ...rest]);
            };

            XMLHttpRequest.prototype.send = function(...args) {
                if (this._intercepted) {
                    // 模拟成功响应
                    Object.defineProperty(this, 'status', { value: 200, writable: false });
                    Object.defineProperty(this, 'statusText', { value: 'OK', writable: false });
                    Object.defineProperty(this, 'responseText', { 
                        value: JSON.stringify({
                            error: 0,
                            error_msg: "",
                            popup: 0,
                            result: {
                                error: 0,
                                popup: 0
                            }
                        }), 
                        writable: false 
                    });
                    Object.defineProperty(this, 'readyState', { value: 4, writable: false });
                    
                    // 触发事件
                    if (this.onreadystatechange) {
                        this.onreadystatechange();
                    }
                    if (this.onload) {
                        this.onload();
                    }
                    return;
                }
                return originalXHRSend.apply(this, args);
            };

            // 3. 拦截 JSONP 回调(百度地图使用JSONP)
            const originalCreateElement = document.createElement;
            const originalAppendChild = Node.prototype.appendChild;
            const originalInsertBefore = Node.prototype.insertBefore;
            
            document.createElement = function(tagName, ...rest) {
                const element = originalCreateElement.apply(this, [tagName, ...rest]);
                
                // 处理div元素,防止创建弹窗
                if (tagName.toLowerCase() === 'div') {
                    const originalSetAttribute = element.setAttribute;
                    const originalAddClass = element.classList?.add;
                    
                    // Hook setAttribute,检查可能用于弹窗的属性
                    element.setAttribute = function(name, value) {
                        if (typeof value === 'string' && (
                            value.includes('商用授权') || 
                            value.includes('business_accredit') ||
                            value.includes('lbs.baidu.com') ||
                            (name === 'class' && (value.includes('dialog') || value.includes('modal') || value.includes('popup')))
                        )) {
                            console.log('拦截百度地图弹窗元素创建:', name, value);
                            // 不设置属性,或者设置为隐藏
                            if (name === 'style') {
                                return originalSetAttribute.call(this, name, 'display:none !important;');
                            }
                            return; // 不设置属性
                        }
                        return originalSetAttribute.apply(this, arguments);
                    };
                    
                    // Hook classList.add
                    if (element.classList && originalAddClass) {
                        const classListAdd = element.classList.add;
                        element.classList.add = function(...tokens) {
                            for (let token of tokens) {
                                if (typeof token === 'string' && (
                                    token.includes('dialog') || 
                                    token.includes('modal') || 
                                    token.includes('popup') ||
                                    token.includes('alert')
                                )) {
                                    console.log('拦截百度地图弹窗class添加:', token);
                                    continue; // 跳过这个class
                                }
                            }
                            return classListAdd.apply(this, tokens);
                        };
                    }
                    
                    // Hook appendChild,如果添加了包含授权相关文本的子元素,则隐藏
                    const originalDivAppendChild = element.appendChild;
                    element.appendChild = function(child) {
                        if (child && child.textContent) {
                            const text = child.textContent;
                            if (text.includes('商用授权') || 
                                text.includes('business_accredit') ||
                                text.includes('lbs.baidu.com') ||
                                text.includes('未完成商用授权')) {
                                console.log('拦截百度地图弹窗内容添加');
                                // 隐藏元素而不是添加
                                if (child.style) {
                                    child.style.display = 'none';
                                }
                            }
                        }
                        return originalDivAppendChild.apply(this, arguments);
                    };
                }
                
                if (tagName.toLowerCase() === 'script') {
                    const originalSetAttribute = element.setAttribute;
                    const originalSetProperty = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src')?.set;
                    
                    // Hook setAttribute
                    element.setAttribute = function(name, value) {
                        if (name === 'src' && typeof value === 'string' && value.includes('api.map.baidu.com')) {
                            // 拦截所有包含callback参数的JSONP请求(包括缩放等操作触发的请求)
                            // 只要包含callback参数就拦截,确保所有百度地图API回调都被处理
                            if (value.includes('callback=')) {
                                console.log('拦截百度地图JSONP请求 (setAttribute):', value);
                                value = modifyJsonpCallback(value);
                            }
                        }
                        return originalSetAttribute.apply(this, arguments);
                    };
                    
                    // Hook src 属性设置
                    if (originalSetProperty) {
                        Object.defineProperty(element, 'src', {
                            set: function(value) {
                                if (typeof value === 'string' && value.includes('api.map.baidu.com')) {
                                    // 拦截所有包含callback参数的JSONP请求
                                    if (value.includes('callback=')) {
                                        console.log('拦截百度地图JSONP请求 (src property):', value);
                                        value = modifyJsonpCallback(value);
                                    }
                                }
                                originalSetProperty.call(this, value);
                            },
                            get: function() {
                                return this.getAttribute('src');
                            },
                            configurable: true
                        });
                    }
                    
                    // 标记这个script元素,以便在添加到DOM时检查
                    element._isBaiduMapScript = true;
                }
                
                return element;
            };
            
            // Hook appendChild 和 insertBefore,在添加到DOM前最后检查
            Node.prototype.appendChild = function(child) {
                if (child && child._isBaiduMapScript && child.src) {
                    // 拦截所有包含callback的百度地图请求
                    if (child.src.includes('api.map.baidu.com') && child.src.includes('callback=')) {
                        // 确保已经修改了回调
                        if (!child.src.includes('_baidu_map_popup_blocker_')) {
                            child.src = modifyJsonpCallback(child.src);
                        }
                    }
                }
                return originalAppendChild.apply(this, arguments);
            };
            
            Node.prototype.insertBefore = function(newNode, referenceNode) {
                if (newNode && newNode._isBaiduMapScript && newNode.src) {
                    // 拦截所有包含callback的百度地图请求
                    if (newNode.src.includes('api.map.baidu.com') && newNode.src.includes('callback=')) {
                        // 确保已经修改了回调
                        if (!newNode.src.includes('_baidu_map_popup_blocker_')) {
                            newNode.src = modifyJsonpCallback(newNode.src);
                        }
                    }
                }
                return originalInsertBefore.apply(this, arguments);
            };
            
            // 修改JSONP回调的辅助函数
            function modifyJsonpCallback(url) {
                // 匹配 callback=xxx 或 callback=xxx.xxx.xxx 格式
                return url.replace(
                    /callback=([^&]+)/,
                    (match, callbackName) => {
                        // 创建一个包装函数,修改返回数据
                        const wrapperName = '_baidu_map_popup_blocker_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
                        const callbackPath = callbackName.split('.');
                        
                        // 创建包装回调函数
                        window[wrapperName] = function(data) {
                            // 深度修改所有可能的弹窗相关字段
                            function removePopupFields(obj) {
                                if (!obj || typeof obj !== 'object') return;
                                
                                // 修改当前对象的字段
                                if (obj.popup !== undefined) {
                                    obj.popup = 0;
                                }
                                if (obj.error !== undefined && obj.error === 503) {
                                    obj.error = 0;
                                    obj.error_msg = "";
                                }
                                
                                // 递归处理嵌套对象
                                for (let key in obj) {
                                    if (obj.hasOwnProperty(key) && typeof obj[key] === 'object') {
                                        removePopupFields(obj[key]);
                                    }
                                }
                            }
                            
                            if (data && typeof data === 'object') {
                                removePopupFields(data);
                            }
                            
                            // 调用原始回调
                            let callback = window;
                            for (let i = 0; i < callbackPath.length; i++) {
                                if (callback && callback[callbackPath[i]]) {
                                    callback = callback[callbackPath[i]];
                                } else {
                                    callback = null;
                                    break;
                                }
                            }
                            if (callback && typeof callback === 'function') {
                                try {
                                    callback(data);
                                } catch(e) {
                                    console.error('Error calling original callback:', e);
                                }
                            }
                            
                            // 延迟清理包装函数,确保回调已执行
                            setTimeout(function() {
                                try {
                                    delete window[wrapperName];
                                } catch(e) {}
                            }, 1000);
                        };
                        
                        return 'callback=' + wrapperName;
                    }
                );
            }

            // 4. Hook BMapGL 相关方法(如果已经加载)
            function hookBMapGLMethods() {
                if (window.BMapGL) {
                    // Hook bmapVerifyCbk 回调
                    if (window.BMapGL.bmapVerifyCbk) {
                        const originalVerifyCbk = window.BMapGL.bmapVerifyCbk;
                        // 深度修改弹窗字段的辅助函数
                        function removePopupFieldsDeepVerify(obj) {
                            if (!obj || typeof obj !== 'object') return;
                            if (obj.popup !== undefined) obj.popup = 0;
                            if (obj.error !== undefined && obj.error === 503) {
                                obj.error = 0;
                                obj.error_msg = "";
                            }
                            for (let key in obj) {
                                if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
                                    removePopupFieldsDeepVerify(obj[key]);
                                }
                            }
                        }
                        
                        window.BMapGL.bmapVerifyCbk = function(data) {
                            if (data && typeof data === 'object') {
                                removePopupFieldsDeepVerify(data);
                            }
                            return originalVerifyCbk ? originalVerifyCbk.call(this, data) : undefined;
                        };
                    }
                    
                    // Hook 动态回调(如 _rd._cbk28033)
                    if (window.BMapGL._rd) {
                        const originalRd = window.BMapGL._rd;
                        
                        // Hook _cbk 方法(用于创建动态回调)
                        if (originalRd._cbk) {
                            const originalCbk = originalRd._cbk;
                            // 深度修改弹窗字段的辅助函数(局部定义)
                            function removePopupFieldsDeepLocal(obj) {
                                if (!obj || typeof obj !== 'object') return;
                                if (obj.popup !== undefined) obj.popup = 0;
                                if (obj.error !== undefined && obj.error === 503) {
                                    obj.error = 0;
                                    obj.error_msg = "";
                                }
                                for (let key in obj) {
                                    if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
                                        removePopupFieldsDeepLocal(obj[key]);
                                    }
                                }
                            }
                            
                            originalRd._cbk = function(callbackName) {
                                const originalCallback = originalCbk.call(this, callbackName);
                                if (typeof originalCallback === 'function') {
                                    return function(data) {
                                        if (data && typeof data === 'object') {
                                            removePopupFieldsDeepLocal(data);
                                        }
                                        return originalCallback.call(this, data);
                                    };
                                }
                                return originalCallback;
                            };
                        }
                        
                        // 使用 Proxy 拦截所有动态创建的回调属性
                        try {
                            // 深度修改弹窗字段的辅助函数(用于Proxy)
                            function removePopupFieldsDeepProxy(obj) {
                                if (!obj || typeof obj !== 'object') return;
                                if (obj.popup !== undefined) obj.popup = 0;
                                if (obj.error !== undefined && obj.error === 503) {
                                    obj.error = 0;
                                    obj.error_msg = "";
                                }
                                for (let key in obj) {
                                    if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
                                        removePopupFieldsDeepProxy(obj[key]);
                                    }
                                }
                            }
                            
                            const rdProxy = new Proxy(originalRd, {
                                set: function(target, prop, value) {
                                    if (typeof prop === 'string' && prop.startsWith('_cbk') && typeof value === 'function') {
                                        // 包装回调函数
                                        target[prop] = function(data) {
                                            if (data && typeof data === 'object') {
                                                removePopupFieldsDeepProxy(data);
                                            }
                                            return value.call(this, data);
                                        };
                                        return true;
                                    }
                                    target[prop] = value;
                                    return true;
                                }
                            });
                            window.BMapGL._rd = rdProxy;
                        } catch(e) {
                            console.log('Proxy not supported, using fallback method');
                        }
                    }
                    
                    // 深度修改弹窗字段的辅助函数
                    function removePopupFieldsDeep(obj) {
                        if (!obj || typeof obj !== 'object') return;
                        
                        // 修改当前对象的字段
                        if (obj.popup !== undefined) {
                            obj.popup = 0;
                        }
                        if (obj.error !== undefined && obj.error === 503) {
                            obj.error = 0;
                            obj.error_msg = "";
                        }
                        
                        // 递归处理嵌套对象
                        for (let key in obj) {
                            if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) {
                                removePopupFieldsDeep(obj[key]);
                            }
                        }
                    }
                    
                    // 定期检查并Hook新创建的回调函数
                    const callbackMonitor = setInterval(function() {
                        if (window.BMapGL && window.BMapGL._rd) {
                            // 检查所有以_cbk开头的属性
                            for (let key in window.BMapGL._rd) {
                                if (key.startsWith('_cbk') && typeof window.BMapGL._rd[key] === 'function') {
                                    const originalCbk = window.BMapGL._rd[key];
                                    // 如果还没有被Hook过
                                    if (!originalCbk._hooked) {
                                        window.BMapGL._rd[key] = function(data) {
                                            if (data && typeof data === 'object') {
                                                removePopupFieldsDeep(data);
                                            }
                                            return originalCbk.call(this, data);
                                        };
                                        window.BMapGL._rd[key]._hooked = true;
                                        console.log('Hook BMapGL callback:', key);
                                    }
                                }
                            }
                        }
                        
                        // 也检查BMapGL上的其他回调
                        if (window.BMapGL) {
                            for (let key in window.BMapGL) {
                                if ((key.includes('cbk') || key.includes('Cbk') || key.includes('callback') || key.includes('Callback')) 
                                    && typeof window.BMapGL[key] === 'function' && !window.BMapGL[key]._hooked) {
                                    const originalCbk = window.BMapGL[key];
                                    window.BMapGL[key] = function(data) {
                                        if (data && typeof data === 'object') {
                                            removePopupFieldsDeep(data);
                                        }
                                        return originalCbk.call(this, data);
                                    };
                                    window.BMapGL[key]._hooked = true;
                                    console.log('Hook BMapGL callback:', key);
                                }
                            }
                        }
                    }, 100); // 更频繁的检查(100ms)
                    
                    // 30秒后停止监控(给足够时间处理所有回调)
                    setTimeout(function() {
                        clearInterval(callbackMonitor);
                    }, 30000);
                }
            }

            // 5. 监听百度地图API加载完成
            const checkInterval = setInterval(function() {
                if (window.BMapGL) {
                    hookBMapGLMethods();
                    clearInterval(checkInterval);
                }
            }, 100);

            // 6. 全局拦截弹窗显示方法
            // 拦截 alert
            const originalAlert = window.alert;
            window.alert = function(message) {
                if (typeof message === 'string' && (
                    message.includes('商用授权') || 
                    message.includes('lbs.baidu.com') ||
                    message.includes('business_accredit') ||
                    message.includes('未完成商用授权')
                )) {
                    console.log('拦截百度地图授权弹窗 (alert):', message);
                    return;
                }
                return originalAlert.apply(this, arguments);
            };
            
            // 拦截 confirm
            const originalConfirm = window.confirm;
            window.confirm = function(message) {
                if (typeof message === 'string' && (
                    message.includes('商用授权') || 
                    message.includes('lbs.baidu.com') ||
                    message.includes('business_accredit') ||
                    message.includes('未完成商用授权')
                )) {
                    console.log('拦截百度地图授权弹窗 (confirm):', message);
                    return true; // 返回true避免阻塞
                }
                return originalConfirm.apply(this, arguments);
            };
            
            // 拦截 prompt
            const originalPrompt = window.prompt;
            window.prompt = function(message, defaultText) {
                if (typeof message === 'string' && (
                    message.includes('商用授权') || 
                    message.includes('lbs.baidu.com') ||
                    message.includes('business_accredit')
                )) {
                    console.log('拦截百度地图授权弹窗 (prompt):', message);
                    return null;
                }
                return originalPrompt.apply(this, arguments);
            };
            
            // 7. 拦截可能用于显示弹窗的方法
            window.addEventListener('load', function() {
                // 拦截可能用于显示弹窗的全局函数
                const popupKeywords = ['商用授权', 'business_accredit', 'lbs.baidu.com', '未完成商用授权'];
                
                // 监控DOM变化,移除包含授权相关文本的元素
                const observer = new MutationObserver(function(mutations) {
                    mutations.forEach(function(mutation) {
                        mutation.addedNodes.forEach(function(node) {
                            if (node.nodeType === 1) { // Element node
                                const text = node.textContent || '';
                                const className = node.className || '';
                                const id = node.id || '';
                                
                                // 检查是否包含授权相关文本
                                if (popupKeywords.some(keyword => 
                                    text.includes(keyword) || 
                                    className.includes(keyword) || 
                                    id.includes(keyword)
                                )) {
                                    console.log('检测到百度地图弹窗元素,正在移除:', node);
                                    // 立即移除或隐藏
                                    if (node.parentNode) {
                                        node.style.display = 'none';
                                        // 延迟移除,避免影响其他功能
                                        setTimeout(function() {
                                            if (node.parentNode) {
                                                node.parentNode.removeChild(node);
                                            }
                                        }, 100);
                                    }
                                }
                            }
                        });
                    });
                });
                
                // 开始观察DOM变化
                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
                
                // 定期检查并移除弹窗元素
                const popupChecker = setInterval(function() {
                    const allElements = document.querySelectorAll('*');
                    allElements.forEach(function(el) {
                        const text = el.textContent || '';
                        const className = el.className || '';
                        const id = el.id || '';
                        const style = window.getComputedStyle(el);
                        
                        // 检查是否是可见的弹窗元素
                        if (popupKeywords.some(keyword => 
                            (text.includes(keyword) || className.includes(keyword) || id.includes(keyword)) &&
                            style.display !== 'none' &&
                            (style.position === 'fixed' || style.position === 'absolute') &&
                            (parseInt(style.zIndex) > 1000 || style.zIndex === 'auto')
                        )) {
                            console.log('检测到百度地图弹窗,正在移除:', el);
                            el.style.display = 'none';
                            setTimeout(function() {
                                if (el.parentNode) {
                                    el.parentNode.removeChild(el);
                                }
                            }, 100);
                        }
                    });
                }, 500);
                
                // 30秒后停止检查
                setTimeout(function() {
                    clearInterval(popupChecker);
                }, 30000);
            });
        })();
    </script>

实际效果:

不过既然是 hook,存在一定的稳定性问题,有可能会失效。

等哪天彻底被恶心够了,就直接换地图 sdk 了!

  •  

基于 wp-cron.php 的拒绝服务攻击

这几天不知道是发生什么事了,说是不知道什么事情,但是大概率是被打了。只是这次打的挺高级的,外层的 eo 貌似也没什么反应。只是那个访问量通过 umami 看,直接爆炸了。

平常几百的访问量,昨天的时候,结果到了 2000 多,当然这不是最奇怪的,奇怪的是服务器过了会儿卡死了。之前都是因为请求太多 php-fpm 耗尽 cpu 资源卡死了,这次以为还是同样的问题。然而,并不是,发现 mysql 把 cpu 跑满了,查看日志的时候发现大量的 wp-cron.php 的请求,这尼玛,请求直接透传过来了。

另外还有一大堆 bot 的请求,包括 bing 以及一些乱起八糟的爬虫遍历。

最开始没想到什么好办法,简单粗暴的把 wp-cron.php 改名了,暂时解决了这个问题。

不过这个方法的确是高明,带着参数透传过来,wp 就是疯狂的执行,一条没执行完就到了下一条。然而,对于这种事情直接改名的确是可以解决办法,不过后来想了一下还是直接从 eo 下手吧。

尽管 eo 防住了 22 万次的攻击,但是,这些透传的请求,直接让 mysql 耗尽了 cpu 资源,也是个不错的办法,甚至请求频率都不用太高。流量到了 144g,这也不知道是哪个哥们又闲的蛋疼了,如果真的蛋疼来找姐姐啊,姐姐帮你治疗,直接给你割下来,塞你自己嘴里!

昨天晚上发现这个情况的时候,本来是想去处理下的,结果对象在用电脑,自己又不想去开笔记本,就用手机处理了一下,简单的改下了文件名。

今天早上才处理了一下,加到了 eo 的访问规则里:

尽管如此,还是对这几天的访问记录比较好奇,想看看请求了多少次。去拉 nginx 日志的时候发现文件已经 1.5G 了。直接截取这几天的记录,用 goaccess 跑了一下,但是比较奇怪的是这个 wp-cron.php 的请求竟然没有。

暂时放弃 goaccess 直接使用 ngxtop 进行数据分析:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
使用ngxtop分析Nginx日志中的POST请求
提供交互式菜单和多种分析选项
"""

import subprocess
import sys
import os
from pathlib import Path


def run_ngxtop(cmd_args):
    """运行ngxtop命令"""
    venv_python = Path(__file__).parent / "venv" / "bin" / "python"
    ngxtop_script = Path(__file__).parent / "venv" / "bin" / "ngxtop"
    
    if not ngxtop_script.exists():
        print("错误: ngxtop未安装,请先运行: source venv/bin/activate && pip install ngxtop")
        sys.exit(1)
    
    try:
        result = subprocess.run(
            [str(ngxtop_script)] + cmd_args,
            capture_output=True,
            text=True,
            check=False
        )
        print(result.stdout)
        if result.stderr and "error" in result.stderr.lower():
            print(result.stderr, file=sys.stderr)
        return result.returncode == 0
    except Exception as e:
        print(f"错误: {e}", file=sys.stderr)
        return False


def show_menu():
    """显示菜单"""
    print("\n" + "="*60)
    print("Nginx日志POST请求分析 - ngxtop工具")
    print("="*60)
    print("1. POST请求总览")
    print("2. 按URL统计POST请求 (Top 20)")
    print("3. 按IP统计POST请求 (Top 20)")
    print("4. 按状态码统计POST请求")
    print("5. POST请求中状态码为404的URL")
    print("6. POST请求中状态码为200的URL")
    print("7. 可疑POST请求 (xmlrpc, wp-login等)")
    print("8. POST请求详情示例")
    print("9. 自定义查询")
    print("0. 退出")
    print("="*60)


def analyze_post_requests(log_file):
    """分析POST请求"""
    if not os.path.exists(log_file):
        print(f"错误: 日志文件 {log_file} 不存在")
        return
    
    base_args = ["-l", log_file, "--no-follow", "-i", 'request.startswith("POST")']
    
    while True:
        show_menu()
        choice = input("\n请选择分析选项 (0-9): ").strip()
        
        if choice == "0":
            print("退出分析")
            break
        elif choice == "1":
            print("\n【POST请求总览】")
            print("-" * 60)
            run_ngxtop(base_args + ["--limit", "0"])
        elif choice == "2":
            print("\n【按URL统计POST请求 (Top 20)】")
            print("-" * 60)
            run_ngxtop(base_args + ["--group-by", "request_path", "--limit", "20"])
        elif choice == "3":
            print("\n【按IP统计POST请求 (Top 20)】")
            print("-" * 60)
            run_ngxtop(base_args + ["--group-by", "remote_addr", "--limit", "20"])
        elif choice == "4":
            print("\n【按状态码统计POST请求】")
            print("-" * 60)
            run_ngxtop(base_args + ["--group-by", "status", "--limit", "0"])
        elif choice == "5":
            print("\n【POST请求中状态码为404的URL (Top 10)】")
            print("-" * 60)
            run_ngxtop(["-l", log_file, "--no-follow", 
                       "-i", 'request.startswith("POST") and status == 404',
                       "--group-by", "request_path", "--limit", "10"])
        elif choice == "6":
            print("\n【POST请求中状态码为200的URL (Top 10)】")
            print("-" * 60)
            run_ngxtop(["-l", log_file, "--no-follow",
                       "-i", 'request.startswith("POST") and status == 200',
                       "--group-by", "request_path", "--limit", "10"])
        elif choice == "7":
            print("\n【可疑POST请求统计】")
            print("-" * 60)
            run_ngxtop(["-l", log_file, "--no-follow",
                       "-i", 'request.startswith("POST") and (request_path == "/xmlrpc.php" or request_path == "/wp-login.php" or request_path.startswith("/wp-admin"))',
                       "--group-by", "request_path", "--limit", "0"])
        elif choice == "8":
            print("\n【POST请求详情示例 (前10条)】")
            print("-" * 60)
            run_ngxtop(base_args + ["print", "remote_addr", "time_local", "request", "status", "bytes_sent", "--limit", "10"])
        elif choice == "9":
            print("\n【自定义查询】")
            print("-" * 60)
            print("示例查询:")
            print("  - 查看特定URL: ngxtop -l <file> -i 'request.startswith(\"POST\") and request_path == \"/wp-cron.php\"'")
            print("  - 查看特定IP: ngxtop -l <file> -i 'request.startswith(\"POST\") and remote_addr == \"114.66.247.160\"'")
            print("  - 查看错误请求: ngxtop -l <file> -i 'request.startswith(\"POST\") and status >= 400'")
            print("\n请输入自定义ngxtop命令参数 (用空格分隔):")
            custom_args = input("> ").strip().split()
            if custom_args:
                run_ngxtop(["-l", log_file, "--no-follow"] + custom_args)
        else:
            print("无效的选择,请重试")
        
        input("\n按回车键继续...")


def main():
    """主函数"""
    if len(sys.argv) < 2:
        # 查找默认日志文件
        log_files = list(Path(".").glob("*.txt"))
        if log_files:
            default_log = str(log_files[0])
            print(f"未指定日志文件,使用默认: {default_log}")
            log_file = default_log
        else:
            print("用法: python analyze_with_ngxtop.py <日志文件路径>")
            print("示例: python analyze_with_ngxtop.py 11-08_org.txt")
            sys.exit(1)
    else:
        log_file = sys.argv[1]
    
    analyze_post_requests(log_file)


if __name__ == "__main__":
    main()

运行命令:

python3 analyze_with_ngxtop.py 11-08_org.txt

分析结果:

【按URL统计POST请求 (Top 20)】
------------------------------------------------------------

running for 7 seconds, 23670 records processed: 3508.50 req/sec

Summary:
|   count |   avg_bytes_sent |   2xx |   3xx |   4xx |   5xx |
|---------+------------------+-------+-------+-------+-------|
|   23670 |         2381.924 |  4678 |    21 | 18574 |   397 |

Detailed:
| request_path                    |   count |   avg_bytes_sent |   2xx |   3xx |   4xx |   5xx |
|---------------------------------+---------+------------------+-------+-------+-------+-------|
| /wp-cron.php                    |   16454 |          731.309 |  3413 |     0 | 13034 |     7 |
| /xmlrpc.php                     |    3102 |          416.754 |   248 |     0 |  2853 |     1 |
| /wp-login.php                   |    2519 |        15204.250 |     0 |     0 |  2519 |     0 |
| /wp-admin/admin-ajax.php        |    1017 |          542.043 |   971 |     0 |    44 |     2 |
| /wp-comments-post.php           |     401 |         2551.357 |     0 |    14 |     0 |   387 |
| /xmrpc.php                      |      41 |          915.000 |     0 |     0 |    41 |     0 |
| /tslogin                        |      20 |        30543.150 |    16 |     4 |     0 |     0 |
| /alfacgiapi/perl.alfa           |      11 |        51292.455 |     0 |     0 |    11 |     0 |
| /ALFA_DATA/alfacgiapi/perl.alfa |      11 |        51323.636 |     0 |     0 |    11 |     0 |
| /index.php                      |      10 |        34570.900 |    10 |     0 |     0 |     0 |
| /wp-plain.php                   |       9 |         1331.000 |     0 |     0 |     9 |     0 |
| /                               |       9 |        28609.556 |     7 |     0 |     2 |     0 |
|                                 |       8 |          415.000 |     8 |     0 |     0 |     0 |
| /flow.php                       |       7 |          915.000 |     0 |     0 |     7 |     0 |
| /wp-admin/async-upload.php      |       5 |          736.000 |     5 |     0 |     0 |     0 |
| /php-cgi/php-cgi.exe            |       4 |        33911.500 |     0 |     0 |     4 |     0 |
| /graphql                        |       4 |        33469.750 |     0 |     0 |     4 |     0 |
| /wp-admin/post.php              |       3 |            5.000 |     0 |     3 |     0 |     0 |
| /member/success.aspx            |       2 |        16784.500 |     0 |     0 |     2 |     0 |
| /e/aspx/upload.aspx             |       2 |        16628.500 |     0 |     0 |     2 |     0 |

【按IP统计POST请求 (Top 20)】
------------------------------------------------------------
running for 7 seconds, 23670 records processed: 3586.40 req/sec

Summary:
|   count |   avg_bytes_sent |   2xx |   3xx |   4xx |   5xx |
|---------+------------------+-------+-------+-------+-------|
|   23670 |         2381.924 |  4678 |    21 | 18574 |   397 |

Detailed:
| remote_addr    |   count |   avg_bytes_sent |   2xx |   3xx |   4xx |   5xx |
|----------------+---------+------------------+-------+-------+-------+-------|
| 221.204.26.162 |    4407 |          696.960 |  1125 |     1 |  3279 |     2 |
| 221.204.26.233 |    4291 |          738.947 |  1054 |     1 |  3235 |     1 |
| 101.71.101.44  |    3168 |          686.088 |   911 |     4 |  2252 |     1 |
| 101.71.101.106 |    2564 |          868.693 |   183 |     2 |  2379 |     0 |
| 43.174.53.229  |    2094 |         7795.611 |     6 |     0 |  2088 |     0 |
| 43.174.53.236  |    2090 |         7811.496 |     4 |     0 |  2086 |     0 |
| 114.66.247.160 |    1810 |          743.818 |   520 |     1 |  1288 |     1 |
| 114.66.246.149 |    1123 |          507.375 |   538 |     1 |   582 |     2 |
| 101.71.105.47  |     104 |          574.404 |    57 |     0 |    47 |     0 |
| 43.175.19.192  |      29 |         5430.241 |     1 |     0 |    15 |    13 |
| 43.175.17.169  |      26 |         2520.500 |     0 |     0 |     8 |    18 |
| 43.175.18.81   |      25 |         2049.720 |     1 |     0 |     6 |    18 |
| 43.175.18.253  |      25 |         1835.800 |     1 |     0 |     8 |    16 |
| 43.175.18.195  |      25 |         5997.720 |     0 |     0 |     8 |    17 |
| 43.175.18.137  |      25 |         2101.840 |     1 |     0 |     5 |    19 |
| 43.175.17.87   |      24 |         2210.208 |     0 |     0 |     5 |    19 |
| 43.175.17.47   |      23 |         7488.043 |     0 |     0 |     9 |    14 |
| 43.175.18.51   |      22 |         3213.455 |     0 |     0 |     8 |    14 |
| 43.175.17.205  |      21 |         7011.381 |     1 |     0 |    10 |    10 |
| 43.175.169.137 |      16 |         1386.562 |     3 |     0 |     6 |     7 |

而至于这些 IP 地址,多数都是国内的,这个倒是也在意料之内,毕竟国外的被拦截的概率会更高一些。

然而,goaccess 就无法分析吗?也可以,添加忽略请求参数的参数就可以了:

#!/bin/bash
# 使用goaccess的--no-query-string参数移除查询参数
# 不需要修改日志文件!

LOG_FILE="${1:-11-08_org.txt}"
OUTPUT_FILE="${2:-goaccess_no_query_report.html}"

if [ ! -f "$LOG_FILE" ]; then
    echo "错误: 日志文件 $LOG_FILE 不存在"
    exit 1
fi

echo "=========================================="
echo "使用GoAccess分析(移除查询参数)"
echo "=========================================="
echo "日志文件: $LOG_FILE"
echo "输出文件: $OUTPUT_FILE"
echo ""
echo "使用参数: --no-query-string (或 -q)"
echo "这将移除URL中的查询参数,只保留路径"
echo ""

# 使用--no-query-string参数
goaccess "$LOG_FILE" \
  --log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u"' \
  --date-format='%d/%m/%Y' \
  --time-format='%H:%M:%S' \
  --no-query-string \
  -o "$OUTPUT_FILE"

if [ $? -eq 0 ]; then
    echo ""
    echo "✅ 报告生成成功: $OUTPUT_FILE"
    echo ""
    echo "现在wp-cron.php应该能正确合并统计了!"
    echo ""
    echo "在浏览器中打开报告查看:"
    echo "  open $OUTPUT_FILE    # macOS"
    echo "  xdg-open $OUTPUT_FILE  # Linux"
    echo ""
    echo "在交互界面中使用:"
    echo "  goaccess $LOG_FILE \\"
    echo "    --log-format='%h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"' \\"
    echo "    --date-format='%d/%m/%Y' \\"
    echo "    --time-format='%H:%M:%S' \\"
    echo "    --no-query-string"
else
    echo "❌ 报告生成失败"
    exit 1
fi

主要就是:–no-query-string参数。

实际效果:

文件没改名之前:

文件改名之后:

虽然加起来之后不到两万次,但是却让 mysql 把 cpu 资源耗尽了,这的确不失为一个低成本的攻击方式。

爬虫占比:

这几天也不知道爬虫是发什么疯

今天的访问量:

百度的统计:

咱就是说,有点时间干点正事不好吗?真是闲的。

 

  •  

京东账号从封禁到解封

 9月底的时候找人开了个8核16G的天翼云电脑,本来想着能运行个VM虚机,装个debian呢,结果开通了之后发现云电脑系统就是WinServer,本来就是虚拟化的,不能再装虚机软件,白搞了,其他也没啥能发挥作用的,就想起来之前玩过的蜂鸟舆情,挂淘宝抖音京东账号用来刷商品点击率用来赚取一些收益,就折腾了一下,搞了几个账号挂上了,本来以为能一天挂个几块回回本呢,结果好景不长,挂了一周左右,淘宝和抖音相继跳验证,基本上执行不了任务了,就剩下京东还在坚持,差不多能一天收益一块多吧,反正不让云电脑闲着。
1
2
 稳定挂了差不多一个月,11.3号白天登上云电脑看看账号掉了没,结果登录不上了,提示账号由于安全原因被封禁,可以发邮件申诉,对于我这个10年老号,5年PLUS的用户来说,那不炸了吗,好多电器啥的买的都有保险,登不上岂不是没办法申请售后了,而且6号那天还给我发短信让我还白条,3号当天就赶紧发邮件申诉,好像等了两天才回复,一看就是机械性回复,不予解封,又重新发了一封邮件,结果等来的还是一模一样的回复,同步也打了京东950618客服电话,结果打了3天,每次都是等十几分钟也没接进去人工。
3
 在7号收到第二次邮件回复之后也是沉不住气了,投诉,必须投诉,不管我干啥了,你不能没有任何通知的情况下直接封禁我的账号,而且还没有解封途径,其他账号冻结了还能真人认证啥的解封呢,这你直接告诉我不予解封,重新申请账号。。。然后就打了12315消费者热线进行投诉,现在12315都合并到12345了,这里刚开始打了个本地的12345,解释一通之后,合肥12345说要打京东注册地的12345,然后加北京010重新打了12345,说明情况之后,直接给我转接到了京东人工客服,果然还是投诉好使,和京东客服说明了情况,刚开始也是回复说由于频繁异地登录,出于安全考虑,系统自动封禁的,无法解封,后来我又说了些不通知的情况下封账号和白条,还有保险售后的一系列问题,最后也是松口了,说给申请一下,让过几天再试一下,第二天也就是8号又有电话回访配合先让线下转账把白条还了,然后又有客服回访说已经申请了,过几天也就是11号就可以登了,今天试了下,终于可以登上了,行吧,也不挂机了,这下云电脑彻底空闲了,不知道有啥好玩的可以回回血的。

  •  

不了了之

已经记不清楚入冬多长时间了,只是隐约记得冬天的节气一个接一个,似乎依然过了好几个了。

这气温也跟着起起起伏伏,从前天开始,暖气片竟然有一些温度了,不在那么冰冷。或许这冬天真的是来了,不过自己的体温似乎一点变化都没有,不管冷还是热,在这个冬天始终稳定在一个冰凉的状态,多少次想找个体温枪量一下自己脚的温度,却总是上床之后才想起来,又不想在去找体温枪。如果量一下的话应该是显示个 low 吧。

前段时间去拍的照片,依然有段时间了。百度网盘没有vip,所以下载照片基本都是挂在群晖的自动同步上下载。尽管速度只有 100k 左右,但是只要时间够长通知能下载完的,自动同步的好处是不用管到底下载到什么进度了。然而,想着看下下载的照片,当把内网的端口映射出来之后,各种扫描接踵而至,如逐臭的苍蝇一般,让人不胜其烦。

仅仅开了不到一天的时间就如此,这扫描也是无缝不入。虽然群晖自带防火墙,但是每次添加规则只能添加 15 个。这逻辑也是服了。 为了屏蔽国外的这些扫描器,只好添加一堆规则:

只是,这一系列昨晚之后。总觉得还是少了点东西,看了下系统上的年假还有四五天。这四五天年假,等过了元旦也就过期了。既然不能折现,何不浪费了他。

打定了主意,这大好的时光自然也不该浪费。尽管依然是冬天,但是树上的叶子依然是深绿色,枫树、银杏却早已变得火红或者金黄,尽情的飘散在这初冬之中。曾想着,等哪天下雪之后,穿一袭红色长裙站在皑皑白雪中,享受那种冰冷的热烈。

青岛的冬天,却不常见到雪,更不曾留住雪。一切都有些可遇不可求,现在的时光却是刚刚好,又怎堪辜负这大好的时光。

选了几套衣服,摄影师表示那件红的有点太突出了,可以拍个圣诞神马的。最终换了一条粉色的长裙,而至于拍摄的地点,原本是想去世博园的,摄影师却说之前去的时候,拍摄的姐妹,穿了件轻婚纱,在入口被拦住了不让进。现在这好景致,市南的公园,以及红岛的万亩枫林看比人发的都挺好看的,然而问题是太远了。尤其是从近城阳的地方开车过去也是比较费时间的,最后选择了世纪公园。

找个停车场停好车,步行到公园门口。门口一撮撮的大爷凑到一起,有的在下棋,有的在打扑克。这样的老年生活也挺好的,轻松自由。不过既然是拍照,那自然选几个场地就可以啦。

就在入口处转了转,拍了几套。这身打扮在这个季节还算是比较正常的。

等换上那条粉色的长裙之后,事情就变得没那么简单了,毕竟在这个季节还穿裙子的也的确是少数。补妆的时候,一只蚊子落在了胳膊上,化妆师小姐姐,眼疾手快直接给拍死了。没想到,冬天还有蚊子,蚊子也没想到,冬天还有穿的这么干净的,漏一大片的。

除了拍照也的确没有这种穿搭了,尽管在一周之前还看到不少拍婚纱照的,可能与工作日也有关系,可能也许是公园太偏僻,并没看到太多专程来拍照的。

直到看到这拍完的成片才发现,左下角那个姐妹的包包也入镜了。

拍照的时候,她就一直蹲在那个包包的旁边。过了几分钟,可能觉得无聊了,从包里拿出一包烟来,抽出来一只点上,抽了一口之后,就把烟夹在手指上,继续蹲那里看手机。拍照的时候,也不断的有人过来,可能都觉得这是个拍照的好地方吧。

等排到后面人越来越多了,再也没有多少空闲的地方。之前还想着往里走走,只是拍了一段时间,去湖边又拍了几站过之后却不想往里走了。总觉得有无数的好风光,但是这好风光却难以揽入怀中,毕竟时间终是有限,抱着这长裙在公园里面走也确实是有些累。

化妆的小姐姐说,裙子从后面已经扎到最紧了。感觉还是有点往下掉,有点走光。

减肥的事情依然在继续,这漫长的一个月有没有太大的进展。前天晚上,一边看《唐朝诡事录》一遍跳绳,一集电视剧看完,刚好跳了7000 多个。

昨天晚上想继续跳的时候,发身身体有点沉,跳不大动的样子,就跳了 2000 个就结束了。

状态不好的时候,真的跳不动。有的时候,挺想坚持一件事情,只是,稍微懈怠,这件事情就那么不了了之。再也没了后续,这坚持,又能坚持多久。

偶尔从镜子里看到自己,感觉似乎没那么老,但是,通过相机那高清的照片,看到自己的脸的时候,那种苍老感,却再也遮挡不住了。或许真的垂垂老矣。年龄这个东西,就这么一步步的赶尽杀绝,也不给任何喘息的机会。

的确是很多事情,再不做,可能就真的做不了了,在等待的时候,太多的事情拖到了终点,甚至是无疾而终。

多年以后,自己依然叛逆,那种自私而又无所畏惧的性格,或许,永远都不会变吧。

做的太多的事情,总是在补偿自己的童年,少年,年轻的时候那些所谓的爱而不得。

终于,现在能有能力补偿那个孩子的时候,有的时候又觉得太过于奢侈,或者不该去浪费。那种矛盾从来都没解决过,那种不了了之之后的失望更让人后悔。

不过是最终下定了决心:

我讨厌等待,我更讨厌爱而不得!

  •