乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

Android腾讯IM快速集成流程和注意事项

发表于 2022-04-01

前言

最近项目集成了及时通信业务, 采用的是腾讯IM

首先是SDK的快速集成, 步骤如下:

第一步 安装依赖包

1
implementation 'com.tencent.imsdk:imsdk-plus:5.6.1200'

第二步 初始化SDK (需要用到appid)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
V2TIMManager.getInstance()
.initSDK(this, "这里填写appid", V2TIMSDKConfig().apply {
logLevel = V2TIMSDKConfig.V2TIM_LOG_INFO


}, object : V2TIMSDKListener() {
override fun onConnecting() {
Log.i("KIM", "正在连接到腾讯云服务器")
}

override fun onConnectSuccess() {
Log.i("KIM", "已经成功连接到腾讯云服务器")

}

override fun onConnectFailed(code: Int, error: String?) {
Log.i("KIM", "连接腾讯云服务器失败 $code $error")
}
})

第三步 登录IM

1
2
3
4
5
6
7
8
9
10
11
12
V2TIMManager.getInstance().login(
"这里填当前用户id",
"这里填登录密钥 从后台获取",object : V2TIMCallback {
override fun onSuccess() {
Log.i(TAG, "登录成功")

}

override fun onError(code: Int, desc: String?) {
Log.i(TAG, "登录失败 $code $desc")
}
})

第四步 加入群聊房间 (如果是单聊可以跳过该步骤)

1
2
3
4
5
6
7
8
9
10
V2TIMManager.getInstance().joinGroup("这里填房间号 从后台获取", "", object : V2TIMCallback {
override fun onSuccess() {
Log.i(TAG, "加入群组成功")

}

override fun onError(code: Int, desc: String?) {
Log.i(TAG, "加入群组失败 $code $desc")
}
})

第五步 接收消息

如果业务不涉及音视频传输:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
V2TIMManager.getInstance().addSimpleMsgListener(object :
V2TIMSimpleMsgListener() {

// 对应 sendGroupCustomMessage
override fun onRecvGroupCustomMessage(
msgID: String?,
groupID: String?,
sender: V2TIMGroupMemberInfo?,
customData: ByteArray?
) {
super.onRecvGroupCustomMessage(msgID, groupID, sender, customData)
Log.i(TAG, "接收群聊自定义消息")
}

// 对应 sendGroupTextMessage
override fun onRecvGroupTextMessage(
msgID: String?,
groupID: String?,
sender: V2TIMGroupMemberInfo?,
body: String?
) {

Log.i(TAG, "接收群聊文本消息 $body")

}
override fun onRecvC2CTextMessage(msgID: String?, sender: V2TIMUserInfo?, text: String?) {
super.onRecvC2CTextMessage(msgID, sender, text)
Log.i(TAG, "接收单聊文本消息 $text")
}

override fun onRecvC2CCustomMessage(
msgID: String?,
sender: V2TIMUserInfo?,
customData: ByteArray?
) {
super.onRecvC2CCustomMessage(msgID, sender, customData)
Log.i(TAG, "接收单聊自定义消息 ")
}


})

如果业务涉及音视频传输:

1
2
3
4
5
6
V2TIMManager.getMessageManager().addAdvancedMsgListener( object : V2TIMAdvancedMsgListener() {
override fun onRecvNewMessage(msg: V2TIMMessage?) {
Log.i(TAG, "收到消息类型${msg?.elemType} ")

}
})

第六步 发送消息

如果业务不涉及音视频传输:

  1. 单聊 (分为文本消息和自定义消息)

    • 文本消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendC2CTextMessage("这里填需要发送的文本",
      "这里填对方的登录id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送单聊文本消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送单聊文本消息失败 $code $desc")
      }
      })
    • 自定义消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendC2CCustomMessage("这里填需要发送的自定义消息 格式为字节数组",
      "这里填对方的登录id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送单聊自定义消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送单聊自定义消息失败 $code $desc")
      }
      })
  1. 群聊 (分为文本消息和自定义消息)

    • 文本消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendGroupTextMessage("这里填需要发送的文本",
      "这里填群聊房间id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送群文本消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送群文本消息失败 $code $desc")
      }
      })
  • 自定义消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    V2TIMManager.getInstance().sendGroupCustomMessage("这里填需要发送的自定义消息 格式为字节数组",
    "这里填群聊房间id",
    V2TIM_PRIORITY_HIGH,
    object : V2TIMValueCallback<V2TIMMessage> {
    override fun onSuccess(t: V2TIMMessage?) {
    Log.i(TAG, "发送群自定义消息成功")
    }

    override fun onError(code: Int, desc: String?) {
    Log.i(TAG, "发送群自定义消息失败 $code $desc")
    }
    })

如果业务涉及音视频传输:

该方法支持音视频图片 文本和自定义消息

1
V2TIMManager.getMessageManager().sendMessage()

注意事项

这里的坑主要在于消息的收发, 官方给的API比较多, 单聊和群聊串在一块, 看文档有时容易懵, 这里对收发消息的使用做了一个总结, 如下:

1
2
3
4
5
* IM群聊和单聊的消息收发走的是同一套  总共分为两种需求:
* 第一种: 不涉及音视频 只涉及字符串传输 那么监听消息使用addSimpleMsgListener 发消息使用sendC2C(Group)TextMessage和sendC2C(Group)CustomMessage
* 第二种: 涉及音视频等复杂消息传递 那么监听消息使用addAdvancedMsgListener 发消息使用V2TIMManager.getMessageManager().sendMessage()
*
* 注意: setGroupListener不是监听的群聊消息 而是对群组相关的事件监听 比如群创建通知 公告等等

附加

腾讯IM集成文档

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Android集成支付宝支付以及沙箱环境测试注意事项

发表于 2022-03-01

前言

现在很多App都涉及到支付, 毕竟开发APP目的是为了盈利嘛, 通常我们会在App中同时集成支付宝和微信支付, 方便用户消费

集成相关

支付宝的集成相对简单, 前端无需添加APPID和密钥, 签名部分在在后端完成,只要确保应用签名和开发平台填入的保持一致就行, 不像微信不仅要配置APPID, 还要添加WXEntryActivity ,此外包名也必须匹配 少一个都不行

当然两者都需要在开发平台填入一个应用签名, 格式为MD5

沙箱环境

当我们SDK集成完毕, 后台接口也调好后, 开始测试支付, 那么问题来了, 支付可是得花钱的呀, 为了解决这个问题, 支付宝官方给我们提供了一个沙箱环境的支付宝, 方便开发测试

简而言之就是提供了一个测试用的支付宝账号, 里面有很多钱, 可以随便花, 哈哈, 不过仅限于测试

这个测试账号不能直接登录我们平常用的支付宝App, 需要下载一个沙箱用的支付宝App

这里提供了沙箱支付宝App下载地址

沙箱支付宝App下载

沙箱测试

安装完毕后登录沙箱账号, 看看你的应用能否调起支付

此时你会发现支付宝调不起来 ,如果返回错误码4000, 那么就是没有检测到沙箱支付宝App

此时 需要在支付Activity中开启沙箱环境, 代码如下:

1
2
3
4
5
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX)

}

如果依然无效, 那么大概率是后台签名出了问题, 后台网关需要改成沙箱环境地址

支付宝H5支付

当用户手机上未安装支付宝app时也需要支持支付功能的话, 可以采用H5页面支付方案, 此时需要在清单文件中声明相应Activity,如下:

1
2
3
4
5
6
7
8
9
<!--        支付宝客户端未安装时使用H5页面  -->
<activity
android:name="com.alipay.sdk.app.H5PayActivity"
android:exported="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name="com.alipay.sdk.app.H5AuthActivity"
android:exported="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

该功能可选, 如果不需要可不添加

总结

支付宝集成归纳如下:

  • 前端无需配置APPID, 只需确保应用签名正确 包名不一样也能正常调起
  • 沙箱测试, 前端需要使用EnvUtils开启沙箱环境
  • 沙箱支付宝登录使用开发后台提供的买家账号登录, 用于支付

微信支付集成归纳如下:

  • 前端需要配置APPID和密钥 同时包名和应用签名需要正确

  • 还需要再创建一个WXEntryActivity, 并在清单文件中注册

    清单文件中可以考虑使用activity-alias 实现多合一注册, 示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      <activity-alias
    android:name="${applicationId}.wxapi.WXPayEntryActivity"
    android:exported="true"
    android:targetActivity="WeiXinHandlerActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />
    <activity-alias
    android:name="${applicationId}.wxapi.WXEntryActivity"
    android:exported="true"
    android:targetActivity="WeiXinHandlerActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />

注意事项

从Android11开始, 涉及到一个软件可见性适配问题, 也就是说调起第三方应用时需要在清单文件中声明其对应的包名, 否则无法调起支付, 具体代码如下:

1
2
3
4
5
6
7
8
<queries>
<package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 -->
<package android:name="hk.alipay.wallet" /> <!-- AlipayHK -->
<package android:name="com.tencent.mm" /> <!-- 微信支付 -->
<package android:name="com.sina.weibo" /> <!-- 微博 -->
</queries>
<application
...

《Android 11 系统策略更新》

附加

支付宝支付客户端 DEMO&SDK下载

支付宝支付集成文档

微信支付安卓接入指南

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Studio报错解决 Duplicate class kotlinx.android.parcel.IgnoredOnParcel found in modules

发表于 2022-02-17

问题

Android开发过程中给项目添加依赖包时Studio偶尔出现以下问题:

1
Duplicate class kotlinx.android.parcel.IgnoredOnParcel found in modules

原因是依赖冲突

解决方案

排除org.jetbrains.kotlin包, 如下:

1
2
3
implementation ('xxxx:1.0.0'){
exclude group : "org.jetbrains.kotlin"
}

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Git回退部分文件到历史版本

发表于 2022-02-11

前言

有时候我们需要将部分代码或者部分图标恢复到历史某个版本, 此时我们可以采取以下方式

步骤如下

  1. 查看某个文件的历史版本记录

    1
    git log xxx.png

    image-20220211180311813

  2. 对某个文件进行版本回退

    1
    git reset 656fdsf65 xxx.png

    此时你会发现文件并未发生改变, 并提示unStage, 我们需要使用checkout将文件检出

  3. 检出文件内容

    1
    giit checkout xxx.png

    这时文件已经恢复

  4. 最后重新提交push即可

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

关于BottomSheetDialogFragment使用过程中常见的一些问题

发表于 2022-02-10

问题汇总

1.默认白底去除

​ BottomSheetDialogFragment布局默认有个白色背景, 当你给布局加圆角时就会发现这个问题, 如果我们要将这个背景改为透明, 可采用以下做法:

1
2
3
4
5
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//去除默认的白底
(binding.root.parent as? View)?.setBackgroundColor(Color.TRANSPARENT)
}

2.下滑后弹窗未完全关闭 半透明蒙层还在

我们一般会配合setDimAmount()函数来这只弹窗的背景透明度, 比如:

1
2
3
4
override fun onStart() {
super.onStart()
dialog?.window?.setDimAmount(0.6f)
}

如果peekHeight属性值没有设置到位的话, 比如设置为0, 就容易出现下滑关闭弹窗后半透明背景依旧存在, 需要再点一次才会消失, 实际上是因为弹窗未完全关闭导致的, 解决方法是将布局的实际高度赋值给peekHeight, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
val dialog = dialog as BottomSheetDialog
val bottomSheet =
dialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
val behavior = BottomSheetBehavior.from(bottomSheet!!)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.peekHeight = view.height
}
}

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

利用Android Studio快速给测试机截屏录屏

发表于 2022-01-12

前言

开发测试时, 可能会碰到需要给同事发送测试机上的应用截屏录屏, 如果你用的是自己的手机, 那么自然方便, 通过手机自带的截屏录屏功能配合微信QQ等完美搞定

但是, 如果测试机是公司的, 你只是临时一用, 同事要求你把测出的bug复现录个屏, 这时就有些麻烦了, 因为公司的测试机可能没有装微信等通讯软件, 即便装了也没有登录, 那图片视频就没法发送了, 想要解决有以下几种方法:

  • 给测试机装个微信然后登录或者安装局域网通讯应用
  • 将App装到自己手机上
  • 使用Android Studio的Device File Explorer, 将设备中的截图捞出来

以上三种方式, 无论哪一种似乎都有些麻烦

此时的你开始暴躁, 最后索性改用模拟器 哈哈

事实上 如果你是Andorid开发, 那么我们的Android Studio可以完美解决以上的问题, Studio不仅可以给手机截屏, 还能录屏!!!

是的 你没听错, Android Studio 就是这么滴强大 嘿嘿

准备条件

将手机调试模式连接至Android Studio

截屏

  1. 第一种方式 使用layout Inspector

    image-20220112171043000

    这是Studio自带的应用布局查看器, 可以快速查看当前应用打开界面

  2. 第二种方式 使用logcat工具 (推荐)

    image-20220112171505186

    点击截屏后:

    image-20220112171555143

    这个工具可以随意截取手机上任意一个页面, 不仅能对截取的图片进行旋转操作, 还可以添加手机边框, 去除阴影等等:

    image-20220112171922931

录屏

同样是logcat选项卡中的一个小工具:

image-20220112172036776

​ 点击后可以对录制的码率可宽高进行提前设置:

image-20220112172132571

最后导出的格式是Mp4, 配合Mp4在线转gif工具, 我们可以很方便地给文章配测试动态图, 下面是录屏效果图:

device-2022-01-12-173145

总结

对于Studio这两个功能, 我个人是非常满意的, 测试机截屏录屏分分钟搞定, 不用担心图片不方便传输了

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

python中播放音频的若干种方式

发表于 2022-01-11

前言

最近在做一个音乐自动化的工具, 涉及到背景音乐播放, 这里总结几种播放音频的方法, 如下

第一种 使用pygame模块

  1. 模块安装

    1
    pip install pygame
  2. 播放代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import pygame

    def playMusic(fileName):
    # 初始化
    pygame.mixer.init()
    # 加载音频文件
    pygame.mixer.music.load(fileName)
    # 设置音量 范围为0.0到1.0
    pygame.mixer.music.set_volume(0.5)
    # 开始播放
    pygame.mixer.music.play()

优点: 支持wav和mp3格式

缺点: 声音是单声道的, 失真比较严重

第二种 使用pyaudio模块

  1. 模块安装

    1. 由于pyaudio的运行需要依赖portaudio, 因此需要先安装

      Mac平台直接运行以下指令即可

      1
      brew install portaudio

      其他平台可参考pyaudio官方说明文档 点击进入

    2. 安装pyaudio

      1
      pip install pyaudio
  2. 播放代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import pyaudio
    import wave

    def playMusic(fileName):

    chunk = 1024
    wf = wave.open(fileName, 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
    channels=wf.getnchannels(),
    rate=wf.getframerate(),
    output=True)
    data = wf.readframes(chunk)
    while len(data) > 0:
    stream.write(data)
    data = wf.readframes(chunk)
    stream.stop_stream()
    stream.close()
    p.terminate()

优点: 支持音频录制, 回放为立体声, 支持Windows, Mac和Linux平台

缺点: 不能播放mp3格式音频, 模块安装相对费劲

第三种 使用simpleaudio

  1. 模块安装

    1
    pip install simpleaudio
  2. 播放代码

    1
    2
    3
    4
    5
    import simpleaudio as sa
    def playMusic3(fileName):
    wave_obj = sa.WaveObject.from_wave_file(fileName)
    play_obj = wave_obj.play()
    play_obj.wait_done()

优点: 声音为立体声 安装简单

缺点: 不能播放mp3格式音频

第四种 综合版

上面几种方式总是不太完美, 有没有一种既支持mp3和wav格式, 又能立体声播放, 同时使用起来又方便的方式, 答案是 没有 哈哈

不过 我们可以借助一个音频格式转换工具然后配合simpleaudio, 就能实现我们的完美需求, 这个工具就是pydub

pydub不仅可以对音频进行格式转换, 还可以对音频进行切片(比如我播放音乐的前10秒 ), 淡入淡出, 以及获取音频总时长

更多用法可以参见github :

GitHub

多格式音频播放实现
  1. 模块安装

    1
    pip install pydub
  2. 格式转换 比如mp3转wav

    1
    2
    3
    4
    5
    from pydub import AudioSegment

    def trans_mp3_to_wav(filepath):
    song = AudioSegment.from_mp3(filepath)
    song.export("now.wav", format="wav")
  3. 配合simpleaudio播放音频

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    from pydub import AudioSegment
    import simpleaudio as sa

    def trans_mp3_to_wav(filepath):
    song = AudioSegment.from_mp3(filepath)
    song.export("./now.wav", format="wav")

    def playMusic(fileName):
    wave_obj = sa.WaveObject.from_wave_file(fileName)
    play_obj = wave_obj.play()
    play_obj.wait_done()

    if __name__ == '__main__':
    trans_mp3_to_wav("bgm2.mp3")
    playMusic("now.wav")

注意事项: 由于pydub这个模块, 过分依赖ffmpeg, 因此我们在使用时如果报错:

FileNotFoundError: [Errno 2] No such file or directory: 'ffprobe'

那么需要先安装ffmpeg

各大平台安装ffmpeg方式:

  1. Mac平台

    1
    brew install ffmpeg
  2. Windows平台

    1. 从Windows binaries provided here下载并提取libav;
    2. 添加libav /bin 文件夹到你的环境变量(PATH)
  3. Linux平台

    1
    apt-get install ffmpeg

总结

除了以上几种之外还有一些其他的音频播放模块, 不过大多数都有各自的局限性, 结合自身需求进行选用即可

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

如何免费下载苹果商店收费软件

发表于 2022-01-07

前言

用惯了盗版软件, 突然要付费购买软件, 还真有些不太习惯, 最近看上了某款软件, 不过App Store售价惊人, 在网上找了一番, 没有找到相应的破解版本

于是我打开了拼夕夕和某宝, 直接搜关键字苹果软件, 找到一大堆

image-20220107110945194

原本售价几十几百的应用, 只需要几块钱

这样一来, 心里立马就舒服多了, 几百块钱不舍得花, 几块钱嘛, 还是愿意出的, 这跟免费没啥区别, 哈哈

以后但凡遇到付费的产品, 可以先去网上店铺看看有没有倒卖的, 嘿嘿

后话

当然有条件的话还是尽量购买正版, 享受正版服务, 至少软件更新啥的可以有保证, 同时也免去了各种繁琐的操作

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

用python写一个自动化喊麦 恭喜您发财

发表于 2022-01-05

前言

春节将近, 好运连连, 这里先祝大家在新的一年里, 身体健康, 万事如意, 财源滚滚~

春节这样一个喜庆的节日, 肯定是要嗨起来呀, 搞氛围最好的手段就是音乐, 不同的音乐, 不同的情绪

喊麦, 是一个非常接地气而且能快速调动人们情绪的一种音乐形式, 那么, AV Body 让我们开始躁起来吧, 哈哈哈

环境准备

  • Python
  • 喊麦诗词
  • 喊麦BGM
喊麦词
喊麦BGM

喊麦步骤

第一步 播放背景音乐
  1. 首先确保pip为最新

    1
    pip3 install --upgrade pip
第二步 播放人声

既然是自动化, 那么这里的人声用文字转语音代替, 这里我们需要使用到

  1. 工具安装

    1
    2
    3
    4
    5
    python3 -m pip install pyttsx3

    或者

    pip3 install pyttsx3
  2. 朗读文本

    1
     

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

一款跨平台的SSH工具

发表于 2022-01-04

前言

有时候电脑不在身边, 想快速进入远程服务器解决问题, 此时我们可以借助手机进行SSH服务器连接

只需要下载一个App即可, 该应用名为Termius

image-20220104115045524

点击进入官网

软件特色

  • 免费
  • 跨平台: 支持Mac Windows Linux IOS和Android 五大平台

软件下载

image-20220104114433602

  • Mac:

  • Windows: Termius

  • Linux: Termius

  • IOS: Termius

  • Android: Termius

国内下载通道

Android版官方下载渠道是谷歌商店的, 国内用户无法下载, 我这边已经将APK上传至国内网盘, 有需要的可以直接下载:

点击下载(访问密码:312306)

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…161718…50

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

491 日志
143 标签
RSS
© 2025 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%