<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>一个欲儿的博客</title><link>https://pan.anyuer.club/</link><description>一个欲儿的博客</description><item><title>造梦西游3再续天庭 存档修改</title><link>https://pan.anyuer.club/?id=297</link><description>&lt;p&gt;《再续天庭1.1》的存档解密逻辑主要经历了四个步骤：&lt;/p&gt;&lt;p&gt;第一步，程序读取指定的加密存档文件，并去除其中的换行和空格，然后使用自定义的 Base64 字符表“zYxWvUtSrQpOnMlKjIhGfEdCbAaBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890+/=”对文本进行变体 Base64 解码，将其还原为原始的字节数组；&lt;/p&gt;&lt;p&gt;第二步，程序对该字节数组中的每一个字节进行异或（XOR）运算，固定的异或密钥为十进制的 90（即十六进制的 0x5A），这一步利用了异或运算的自反性来逆转混淆；&lt;/p&gt;&lt;p&gt;第三步，程序进入一个最多执行 10 次的循环，尝试使用 Zlib 库进行多层解压缩，期间会兼顾处理包含或不包含标准标头的压缩流数据，直到无法继续解压、触发异常时自动跳出循环；&lt;/p&gt;&lt;p&gt;第四步，程序进行 AMF3 协议的数据反序列化解析，首先校验第一个字节是否为数字 6（代表字符串对象），验证通过后通过特定的位移运算读取紧随其后的 AMF3 变长整数（U29）以确定 XML 字符串的实际长度，将其右移 1 位得到真正的长度值，并根据该长度截取并使用 UTF-8 编码解码出 XML 明文，最终利用 minidom 库对 XML 进行美化排版并保存为以 .xml 结尾的文本文件。&lt;/p&gt;&lt;p&gt;附上python代码&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;import&amp;nbsp;os
import&amp;nbsp;zlib
import&amp;nbsp;xml.dom.minidom

#&amp;nbsp;完美对齐解密出来的自定义&amp;nbsp;Base64&amp;nbsp;字符表
CUSTOM_ALPHABET&amp;nbsp;=&amp;nbsp;&amp;quot;zYxWvUtSrQpOnMlKjIhGfEdCbAaBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890+/=&amp;quot;
XOR_KEY&amp;nbsp;=&amp;nbsp;90&amp;nbsp;&amp;nbsp;#&amp;nbsp;0x5A

def&amp;nbsp;custom_b64decode(s:&amp;nbsp;str)&amp;nbsp;-&amp;gt;&amp;nbsp;bytearray:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;变体&amp;nbsp;Base64&amp;nbsp;解码逻辑&amp;nbsp;(函数&amp;nbsp;o)&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s&amp;nbsp;=&amp;nbsp;s.strip().replace(&amp;quot;\r&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;).replace(&amp;quot;\n&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;).replace(&amp;quot;&amp;nbsp;&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;bytearray()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(0,&amp;nbsp;len(s),&amp;nbsp;4):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chunk&amp;nbsp;=&amp;nbsp;s[i:i+4]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c&amp;nbsp;=&amp;nbsp;[CUSTOM_ALPHABET.index(char)&amp;nbsp;if&amp;nbsp;char&amp;nbsp;in&amp;nbsp;CUSTOM_ALPHABET&amp;nbsp;else&amp;nbsp;64&amp;nbsp;for&amp;nbsp;char&amp;nbsp;in&amp;nbsp;chunk]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;len(c)&amp;nbsp;&amp;lt;&amp;nbsp;4:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c.append(64)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;b&amp;nbsp;=&amp;nbsp;(c[0]&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;2)&amp;nbsp;|&amp;nbsp;((c[1]&amp;nbsp;&amp;amp;&amp;nbsp;48)&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;4)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d&amp;nbsp;=&amp;nbsp;((c[1]&amp;nbsp;&amp;amp;&amp;nbsp;15)&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;4)&amp;nbsp;|&amp;nbsp;((c[2]&amp;nbsp;&amp;amp;&amp;nbsp;60)&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g&amp;nbsp;=&amp;nbsp;((c[2]&amp;nbsp;&amp;amp;&amp;nbsp;3)&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;6)&amp;nbsp;|&amp;nbsp;c[3]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data.append(b)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;c[2]&amp;nbsp;!=&amp;nbsp;64:&amp;nbsp;data.append(d)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;c[3]&amp;nbsp;!=&amp;nbsp;64:&amp;nbsp;data.append(g)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;data

def&amp;nbsp;custom_b64encode(g:&amp;nbsp;bytearray)&amp;nbsp;-&amp;gt;&amp;nbsp;str:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;变体&amp;nbsp;Base64&amp;nbsp;编码逻辑&amp;nbsp;(函数&amp;nbsp;p)&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;b&amp;nbsp;in&amp;nbsp;range(0,&amp;nbsp;len(g),&amp;nbsp;3):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c&amp;nbsp;=&amp;nbsp;min(3,&amp;nbsp;len(g)&amp;nbsp;-&amp;nbsp;b)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d&amp;nbsp;=&amp;nbsp;g[b]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;e&amp;nbsp;=&amp;nbsp;g[b&amp;nbsp;+&amp;nbsp;1]&amp;nbsp;if&amp;nbsp;c&amp;nbsp;&amp;gt;&amp;nbsp;1&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f&amp;nbsp;=&amp;nbsp;g[b&amp;nbsp;+&amp;nbsp;2]&amp;nbsp;if&amp;nbsp;c&amp;nbsp;&amp;gt;&amp;nbsp;2&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;h&amp;nbsp;=&amp;nbsp;(d&amp;nbsp;&amp;amp;&amp;nbsp;252)&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;2
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i&amp;nbsp;=&amp;nbsp;(d&amp;nbsp;&amp;amp;&amp;nbsp;3)&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;4&amp;nbsp;|&amp;nbsp;(e&amp;nbsp;&amp;amp;&amp;nbsp;240)&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;4
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j&amp;nbsp;=&amp;nbsp;(e&amp;nbsp;&amp;amp;&amp;nbsp;15)&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;2&amp;nbsp;|&amp;nbsp;(f&amp;nbsp;&amp;amp;&amp;nbsp;192)&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;6
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;k&amp;nbsp;=&amp;nbsp;f&amp;nbsp;&amp;amp;&amp;nbsp;63
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&amp;nbsp;+=&amp;nbsp;CUSTOM_ALPHABET[h]&amp;nbsp;+&amp;nbsp;CUSTOM_ALPHABET[i]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&amp;nbsp;+=&amp;nbsp;CUSTOM_ALPHABET[j]&amp;nbsp;if&amp;nbsp;c&amp;nbsp;&amp;gt;&amp;nbsp;1&amp;nbsp;else&amp;nbsp;CUSTOM_ALPHABET[64]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&amp;nbsp;+=&amp;nbsp;CUSTOM_ALPHABET[k]&amp;nbsp;if&amp;nbsp;c&amp;nbsp;&amp;gt;&amp;nbsp;2&amp;nbsp;else&amp;nbsp;CUSTOM_ALPHABET[64]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;a

def&amp;nbsp;read_amf3_integer(data:&amp;nbsp;bytearray,&amp;nbsp;pos:&amp;nbsp;int):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;AMF3&amp;nbsp;变长整数&amp;nbsp;(U29)&amp;nbsp;读取逻辑&amp;nbsp;(函数&amp;nbsp;m)&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(4):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;b&amp;nbsp;=&amp;nbsp;data[pos]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pos&amp;nbsp;+=&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;3:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;(result&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;7)&amp;nbsp;|&amp;nbsp;(b&amp;nbsp;&amp;amp;&amp;nbsp;127)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(b&amp;nbsp;&amp;amp;&amp;nbsp;128)&amp;nbsp;==&amp;nbsp;0:&amp;nbsp;break
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;(result&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;8)&amp;nbsp;|&amp;nbsp;b
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result,&amp;nbsp;pos

def&amp;nbsp;write_amf3_integer(c:&amp;nbsp;int)&amp;nbsp;-&amp;gt;&amp;nbsp;bytearray:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;AMF3&amp;nbsp;变长整数&amp;nbsp;(U29)&amp;nbsp;写入逻辑&amp;nbsp;(函数&amp;nbsp;n)&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;c&amp;nbsp;&amp;lt;&amp;nbsp;128:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.push(c)&amp;nbsp;if&amp;nbsp;hasattr(a,&amp;nbsp;&amp;#39;push&amp;#39;)&amp;nbsp;else&amp;nbsp;a.append(c)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;c&amp;nbsp;&amp;lt;&amp;nbsp;16384:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;7&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;amp;&amp;nbsp;127)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;c&amp;nbsp;&amp;lt;&amp;nbsp;2097152:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;14&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;7&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;amp;&amp;nbsp;127)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;22&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;15&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;8&amp;nbsp;&amp;amp;&amp;nbsp;127&amp;nbsp;|&amp;nbsp;128)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.append(c&amp;nbsp;&amp;amp;&amp;nbsp;255)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;bytearray(a)

def&amp;nbsp;format_xml(xml_str:&amp;nbsp;str)&amp;nbsp;-&amp;gt;&amp;nbsp;str:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;对&amp;nbsp;XML&amp;nbsp;进行美化排版&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dom&amp;nbsp;=&amp;nbsp;xml.dom.minidom.parseString(xml_str)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;dom.toprettyxml(indent=&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;xml_str

def&amp;nbsp;compress_xml(xml_str:&amp;nbsp;str)&amp;nbsp;-&amp;gt;&amp;nbsp;str:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;去除&amp;nbsp;XML&amp;nbsp;格式化引入的换行和缩进，还原紧凑字符串&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lines&amp;nbsp;=&amp;nbsp;[line.strip()&amp;nbsp;for&amp;nbsp;line&amp;nbsp;in&amp;nbsp;xml_str.splitlines()&amp;nbsp;if&amp;nbsp;line.strip()]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;&amp;quot;.join(lines)

def&amp;nbsp;cmd_decrypt():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path&amp;nbsp;=&amp;nbsp;input(&amp;quot;\n请输入要解密的存档路径&amp;nbsp;(如&amp;nbsp;2.sav):&amp;nbsp;&amp;quot;).strip().strip(&amp;#39;&amp;quot;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;os.path.exists(path):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;[-]&amp;nbsp;错误：找不到指定文件。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(path,&amp;nbsp;&amp;quot;r&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw_content&amp;nbsp;=&amp;nbsp;f.read()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;1.&amp;nbsp;Base64&amp;nbsp;还原
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bytes_data&amp;nbsp;=&amp;nbsp;custom_b64decode(raw_content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;2.&amp;nbsp;XOR&amp;nbsp;90&amp;nbsp;解密
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(len(bytes_data)):&amp;nbsp;bytes_data[i]&amp;nbsp;^=&amp;nbsp;XOR_KEY
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;3.&amp;nbsp;多层&amp;nbsp;Zlib&amp;nbsp;解压
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;_&amp;nbsp;in&amp;nbsp;range(10):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:&amp;nbsp;bytes_data&amp;nbsp;=&amp;nbsp;zlib.decompress(bytes_data,&amp;nbsp;-zlib.MAX_WBITS)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;zlib.error:&amp;nbsp;bytes_data&amp;nbsp;=&amp;nbsp;zlib.decompress(bytes_data)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;zlib.error:&amp;nbsp;break

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;4.&amp;nbsp;AMF3&amp;nbsp;解析
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;bytes_data[0]&amp;nbsp;!=&amp;nbsp;6:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;[-]&amp;nbsp;校验失败：该文件不是合法的&amp;nbsp;AMF3&amp;nbsp;存档数据。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;amf3_value,&amp;nbsp;next_pos&amp;nbsp;=&amp;nbsp;read_amf3_integer(bytes_data,&amp;nbsp;1)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;str_len&amp;nbsp;=&amp;nbsp;amf3_value&amp;nbsp;&amp;gt;&amp;gt;&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xml_bytes&amp;nbsp;=&amp;nbsp;bytes_data[next_pos&amp;nbsp;:&amp;nbsp;next_pos&amp;nbsp;+&amp;nbsp;str_len]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xml_text&amp;nbsp;=&amp;nbsp;xml_bytes.decode(&amp;#39;utf-8&amp;#39;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;美化排版并保存
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pretty_xml&amp;nbsp;=&amp;nbsp;format_xml(xml_text)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out_path&amp;nbsp;=&amp;nbsp;path&amp;nbsp;+&amp;nbsp;&amp;quot;.xml&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(out_path,&amp;nbsp;&amp;quot;w&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;out_f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out_f.write(pretty_xml)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;[+]&amp;nbsp;解密成功！明文已格式化保存至:&amp;nbsp;{out_path}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;你可以直接用记事本打开修改它。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;[-]&amp;nbsp;解密过程中发生异常:&amp;nbsp;{e}&amp;quot;)

def&amp;nbsp;cmd_encrypt():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path&amp;nbsp;=&amp;nbsp;input(&amp;quot;\n请输入修改后的&amp;nbsp;XML&amp;nbsp;明文路径&amp;nbsp;(如&amp;nbsp;2.sav.xml):&amp;nbsp;&amp;quot;).strip().strip(&amp;#39;&amp;quot;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;os.path.exists(path):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;[-]&amp;nbsp;错误：找不到指定文件。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(path,&amp;nbsp;&amp;quot;r&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xml_content&amp;nbsp;=&amp;nbsp;f.read()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;压缩掉美化排版的空格和换行
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clean_xml&amp;nbsp;=&amp;nbsp;compress_xml(xml_content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xml_bytes&amp;nbsp;=&amp;nbsp;clean_xml.encode(&amp;#39;utf-8&amp;#39;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;1.&amp;nbsp;AMF3&amp;nbsp;序列化打包&amp;nbsp;(函数&amp;nbsp;l)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;amf3_len_code&amp;nbsp;=&amp;nbsp;(len(xml_bytes)&amp;nbsp;&amp;lt;&amp;lt;&amp;nbsp;1)&amp;nbsp;|&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;amf3_len_bytes&amp;nbsp;=&amp;nbsp;write_amf3_integer(amf3_len_code)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;amf3_packet&amp;nbsp;=&amp;nbsp;bytearray([6])&amp;nbsp;+&amp;nbsp;amf3_len_bytes&amp;nbsp;+&amp;nbsp;xml_bytes
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;2.&amp;nbsp;Zlib&amp;nbsp;压缩
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;compressed&amp;nbsp;=&amp;nbsp;zlib.compress(amf3_packet)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;compressed_bytes&amp;nbsp;=&amp;nbsp;bytearray(compressed)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;3.&amp;nbsp;XOR&amp;nbsp;90&amp;nbsp;加密
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;i&amp;nbsp;in&amp;nbsp;range(len(compressed_bytes)):&amp;nbsp;compressed_bytes[i]&amp;nbsp;^=&amp;nbsp;XOR_KEY

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;4.&amp;nbsp;变体&amp;nbsp;Base64&amp;nbsp;编码
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;final_string&amp;nbsp;=&amp;nbsp;custom_b64encode(compressed_bytes)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;保存为新存档
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out_path&amp;nbsp;=&amp;nbsp;path.replace(&amp;quot;.xml&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)&amp;nbsp;if&amp;nbsp;path.endswith(&amp;quot;.xml&amp;quot;)&amp;nbsp;else&amp;nbsp;path&amp;nbsp;+&amp;nbsp;&amp;quot;.sav&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;out_path&amp;nbsp;==&amp;nbsp;path:&amp;nbsp;out_path&amp;nbsp;+=&amp;nbsp;&amp;quot;.new&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(out_path,&amp;nbsp;&amp;quot;w&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;out_f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;out_f.write(final_string)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;[+]&amp;nbsp;加密成功！新存档已打包输出至:&amp;nbsp;{out_path}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;你可以将此文件改回游戏要求的名字放回存档目录。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;[-]&amp;nbsp;加密过程中发生异常:&amp;nbsp;{e}&amp;quot;)

def&amp;nbsp;main():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;=&amp;quot;&amp;nbsp;*&amp;nbsp;45)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;《再续天庭1.1》存档算法逆向破解工具&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;=&amp;quot;&amp;nbsp;*&amp;nbsp;45)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n[可用指令]&amp;nbsp;&amp;nbsp;1:&amp;nbsp;加密打包存档&amp;nbsp;&amp;nbsp;2:&amp;nbsp;解密分析存档&amp;nbsp;&amp;nbsp;0:&amp;nbsp;退出&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;choice&amp;nbsp;=&amp;nbsp;input(&amp;quot;请输入指令数字:&amp;nbsp;&amp;quot;).strip()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;choice&amp;nbsp;==&amp;nbsp;&amp;quot;1&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmd_encrypt()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;choice&amp;nbsp;==&amp;nbsp;&amp;quot;2&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmd_decrypt()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;choice&amp;nbsp;==&amp;nbsp;&amp;quot;0&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;退出程序。&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;[-]&amp;nbsp;无效指令，请重新输入。&amp;quot;)

if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;main()&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Sat, 27 Jun 2026 15:28:10 +0800</pubDate></item><item><title>造梦西游3再续天庭CE之旅</title><link>https://pan.anyuer.club/?id=296</link><description>&lt;h1&gt;一、常识基础&lt;br/&gt;&lt;/h1&gt;&lt;p&gt;在 Flash 虚拟机中，为了快速区分一个 4 字节（32位）的内存数据到底是一个对象指针还是一个普通整数，它会利用整型数值的低 3 位（Bit）来做“类型标记”。&lt;/p&gt;&lt;p&gt;对于游戏中的普通整数（int / uint 类型），Flash 的规则是：将真实的数值向左移动 3 位（相当于乘以 8），然后在空出来的低 3 位里填入标记代码 110（十进制的 6）。&lt;/p&gt;&lt;p&gt;这就诞生了 Flash 游戏修改的黄金公式：&lt;/p&gt;&lt;p&gt;内存实际值 = 游戏显示值 × 8 + 6&lt;/p&gt;&lt;h1&gt;二、无限积分&lt;br/&gt;&lt;/h1&gt;&lt;p&gt;点开右上角大转盘，宠物分类， 看到子鼠了吗？13000积分 经过公式换算 13000 x 8 + 6 = 104006，因此ce载入进程搜索4字节准确数值 104006&lt;/p&gt;&lt;p&gt;出来的数据不会很多，直接全部改为-50000积分，即买子鼠还倒给玩家50000，经过公式换算-50000 x 8 + 6 =&amp;nbsp;-399994&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606231782144691662634.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;其中如果你的ce数值那一栏不是负数，是一串非常大的数据，请右键然后选中显示带符号的&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606231782144774179603.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后此时，返回游戏， 点击时装，再点击宠物，就会看到子鼠的购买价格变成负数了，点击购买即可&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606231782144965775869.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Tue, 23 Jun 2026 00:04:44 +0800</pubDate></item><item><title>mac 系统下的短语板</title><link>https://pan.anyuer.club/?id=295</link><description>&lt;h1&gt;一、环境部署&lt;/h1&gt;&lt;p&gt;1.桌面新建snippet-panel 文件夹&lt;/p&gt;&lt;p&gt;2.首次需要创建虚拟环境和依赖&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;cd&amp;nbsp;~/Desktop/snippet-panel
python3&amp;nbsp;-m&amp;nbsp;venv&amp;nbsp;venv
source&amp;nbsp;venv/bin/activate
pip&amp;nbsp;install&amp;nbsp;PyQt6&amp;nbsp;pyperclip&lt;/pre&gt;&lt;p&gt;之后只需要&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;cd&amp;nbsp;~/Desktop/snippet-panel
source&amp;nbsp;venv/bin/activate
python3&amp;nbsp;app.py&lt;/pre&gt;&lt;h1&gt;二、mac快捷启动&lt;/h1&gt;&lt;p&gt;桌面新建start_snippet_panel.command&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;#!/bin/bash

cd&amp;nbsp;~/Desktop/snippet-panel

source&amp;nbsp;venv/bin/activate

mkdir&amp;nbsp;-p&amp;nbsp;log

nohup&amp;nbsp;python3&amp;nbsp;app.py&amp;nbsp;&amp;gt;log/app.log&amp;nbsp;2&amp;gt;&amp;amp;1&amp;nbsp;&amp;amp;&lt;/pre&gt;&lt;p&gt;之后每次启动，只需要双击该脚本即可。&lt;/p&gt;&lt;h1&gt;三、代码&lt;/h1&gt;&lt;p&gt;app.py&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;import&amp;nbsp;sys
import&amp;nbsp;json
import&amp;nbsp;os
import&amp;nbsp;pyperclip
import&amp;nbsp;ctypes
import&amp;nbsp;ctypes.util

from&amp;nbsp;PyQt6.QtWidgets&amp;nbsp;import&amp;nbsp;(
QApplication,
QWidget,
QListWidget,
QListWidgetItem,
QVBoxLayout,
QPushButton,
QHBoxLayout,
QLineEdit,
QLabel,
QDialog,
QTextEdit,
QDialogButtonBox,
QMenu,
)
from&amp;nbsp;PyQt6.QtCore&amp;nbsp;import&amp;nbsp;Qt,&amp;nbsp;QSize,&amp;nbsp;QEvent,&amp;nbsp;pyqtSignal,&amp;nbsp;QObject,&amp;nbsp;QPoint,&amp;nbsp;QTimer
from&amp;nbsp;PyQt6.QtGui&amp;nbsp;import&amp;nbsp;QPainter,&amp;nbsp;QFontMetrics,&amp;nbsp;QAction


APP_DIR&amp;nbsp;=&amp;nbsp;os.path.expanduser(&amp;quot;./data&amp;quot;)
os.makedirs(APP_DIR,&amp;nbsp;exist_ok=True)
DATA_FILE&amp;nbsp;=&amp;nbsp;os.path.join(APP_DIR,&amp;nbsp;&amp;quot;data.json&amp;quot;)


def&amp;nbsp;load_data():
if&amp;nbsp;not&amp;nbsp;os.path.exists(DATA_FILE):
return&amp;nbsp;[]
try:
with&amp;nbsp;open(DATA_FILE,&amp;nbsp;&amp;quot;r&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
return&amp;nbsp;json.load(f)
except:
return&amp;nbsp;[]


def&amp;nbsp;save_data(data):
with&amp;nbsp;open(DATA_FILE,&amp;nbsp;&amp;quot;w&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf-8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
json.dump(data,&amp;nbsp;f,&amp;nbsp;ensure_ascii=False,&amp;nbsp;indent=2)


STYLE_SHEET&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
QWidget&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#eef0f7;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-family:&amp;nbsp;&amp;quot;PingFang&amp;nbsp;SC&amp;quot;,&amp;nbsp;&amp;quot;Microsoft&amp;nbsp;YaHei&amp;quot;,&amp;nbsp;sans-serif;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
}

#titleLabel&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
}

#searchBox&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;12px;
}
#searchBox:focus&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#3a6df0;
}

#snippetList&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;outline:&amp;nbsp;none;
}
#snippetList::item&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;10px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;0px;
}
#snippetList::item:selected&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;2px&amp;nbsp;solid&amp;nbsp;#1f2329;
}

#copyTip&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#00b42a;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;13px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
}

#cardWidget&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
}

#cardTitle&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;15px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
}

#cardLabel&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#646a73;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;13px;
}

#cardActionBtn,&amp;nbsp;#cardActionDangerBtn&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;2px&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;12px;
}
#cardActionBtn:hover,&amp;nbsp;#cardActionDangerBtn:hover&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#f0f1f3;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
}
#cardActionBtn:pressed,&amp;nbsp;#cardActionDangerBtn:pressed&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#e6e7ea;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
}

#titleBarBtn&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;4px&amp;nbsp;2px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;18px;
}
#titleBarBtn:hover&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#f0f1f3;
}
#titleBarBtn:pressed&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#e6e7ea;
}

QMenu&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#eef0f7;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#d9dce8;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;5px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;6px;
}
QMenu::item&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;6px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
}
QMenu::item:selected&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#dfe2ee;
}

QPushButton&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;14px;
}
QPushButton:hover&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#f5f6fa;
}
QPushButton:pressed&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#e9ebf2;
}

#dialogFieldLabel&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#646a73;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;13px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
}

#dialogTitleInput&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;12px;
}
#dialogTitleInput:focus&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#3a6df0;
}

#dialogContentInput&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;12px;
}
#dialogContentInput:focus&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#3a6df0;
}

#confirmDialog&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
}
#confirmTitle&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;16px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
}
#confirmMessage&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;transparent;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;none;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#646a73;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:&amp;nbsp;14px;
}
#confirmCancelBtn&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#e3e6ee;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#1f2329;
}
#confirmCancelBtn:hover&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#f5f6fa;
}
#confirmCancelBtn:pressed&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#e9ebf2;
}
#confirmDangerBtn&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#f53f3f;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:&amp;nbsp;1px&amp;nbsp;solid&amp;nbsp;#f53f3f;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-radius:&amp;nbsp;8px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:&amp;nbsp;8px&amp;nbsp;18px;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;color:&amp;nbsp;#ffffff;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:&amp;nbsp;600;
}
#confirmDangerBtn:hover&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#e63333;
}
#confirmDangerBtn:pressed&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:&amp;nbsp;#cc2d2d;
}
&amp;quot;&amp;quot;&amp;quot;


class&amp;nbsp;ElidedLabel(QLabel):
&amp;quot;&amp;quot;&amp;quot;多行文本超出后以省略号结尾。&amp;quot;&amp;quot;&amp;quot;

MAX_LINES&amp;nbsp;=&amp;nbsp;2

def&amp;nbsp;__init__(self,&amp;nbsp;text=&amp;quot;&amp;quot;,&amp;nbsp;parent=None,&amp;nbsp;pad_x=0,&amp;nbsp;pad_y=0):
super().__init__(parent)
self._full_text&amp;nbsp;=&amp;nbsp;text
self._pad_x&amp;nbsp;=&amp;nbsp;pad_x
self._pad_y&amp;nbsp;=&amp;nbsp;pad_y
self.setWordWrap(True)

def&amp;nbsp;setText(self,&amp;nbsp;text):
self._full_text&amp;nbsp;=&amp;nbsp;text
super().setText(text)

def&amp;nbsp;paintEvent(self,&amp;nbsp;event):
painter&amp;nbsp;=&amp;nbsp;QPainter(self)

pad_x,&amp;nbsp;pad_y&amp;nbsp;=&amp;nbsp;self._pad_x,&amp;nbsp;self._pad_y
rect&amp;nbsp;=&amp;nbsp;self.rect().adjusted(pad_x,&amp;nbsp;pad_y,&amp;nbsp;-pad_x,&amp;nbsp;-pad_y)

metrics&amp;nbsp;=&amp;nbsp;QFontMetrics(self.font())
line_height&amp;nbsp;=&amp;nbsp;metrics.lineSpacing()

lines&amp;nbsp;=&amp;nbsp;[]
current&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;
for&amp;nbsp;ch&amp;nbsp;in&amp;nbsp;self._full_text:
if&amp;nbsp;ch&amp;nbsp;==&amp;nbsp;&amp;quot;\n&amp;quot;:
lines.append(current)
current&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;
continue
test&amp;nbsp;=&amp;nbsp;current&amp;nbsp;+&amp;nbsp;ch
if&amp;nbsp;metrics.horizontalAdvance(test)&amp;nbsp;&amp;gt;&amp;nbsp;rect.width()&amp;nbsp;and&amp;nbsp;current:
lines.append(current)
current&amp;nbsp;=&amp;nbsp;ch
else:
current&amp;nbsp;=&amp;nbsp;test
if&amp;nbsp;current:
lines.append(current)

if&amp;nbsp;len(lines)&amp;nbsp;&amp;gt;&amp;nbsp;self.MAX_LINES:
lines&amp;nbsp;=&amp;nbsp;lines[:self.MAX_LINES]
lines[-1]&amp;nbsp;=&amp;nbsp;metrics.elidedText(
lines[-1]&amp;nbsp;+&amp;nbsp;&amp;quot;…&amp;quot;,
Qt.TextElideMode.ElideRight,
rect.width(),
)

y&amp;nbsp;=&amp;nbsp;rect.top()&amp;nbsp;+&amp;nbsp;metrics.ascent()
for&amp;nbsp;line&amp;nbsp;in&amp;nbsp;lines:
painter.drawText(rect.left(),&amp;nbsp;y,&amp;nbsp;line)
y&amp;nbsp;+=&amp;nbsp;line_height

painter.end()


class&amp;nbsp;SnippetDialog(QDialog):
&amp;quot;&amp;quot;&amp;quot;新增/编辑弹窗：标题单行，内容多行（固定高度可滚动）。&amp;quot;&amp;quot;&amp;quot;

def&amp;nbsp;__init__(self,&amp;nbsp;parent=None,&amp;nbsp;window_title=&amp;quot;新增&amp;quot;,&amp;nbsp;title=&amp;quot;&amp;quot;,&amp;nbsp;content=&amp;quot;&amp;quot;):
super().__init__(parent)

self.setWindowTitle(window_title)
self.setMinimumWidth(420)
self.setStyleSheet(STYLE_SHEET)

layout&amp;nbsp;=&amp;nbsp;QVBoxLayout(self)
layout.setContentsMargins(20,&amp;nbsp;20,&amp;nbsp;20,&amp;nbsp;20)
layout.setSpacing(8)

title_tip&amp;nbsp;=&amp;nbsp;QLabel(&amp;quot;标题&amp;quot;)
title_tip.setObjectName(&amp;quot;dialogFieldLabel&amp;quot;)
layout.addWidget(title_tip)

self.title_input&amp;nbsp;=&amp;nbsp;QLineEdit(title)
self.title_input.setObjectName(&amp;quot;dialogTitleInput&amp;quot;)
self.title_input.setPlaceholderText(&amp;quot;请输入标题...&amp;quot;)
layout.addWidget(self.title_input)

layout.addSpacing(6)

content_tip&amp;nbsp;=&amp;nbsp;QLabel(&amp;quot;内容&amp;quot;)
content_tip.setObjectName(&amp;quot;dialogFieldLabel&amp;quot;)
layout.addWidget(content_tip)

self.content_input&amp;nbsp;=&amp;nbsp;QTextEdit()
self.content_input.setObjectName(&amp;quot;dialogContentInput&amp;quot;)
self.content_input.setPlaceholderText(&amp;quot;请输入内容...&amp;quot;)
self.content_input.setPlainText(content)
self.content_input.setFixedHeight(220)
layout.addWidget(self.content_input)

layout.addSpacing(10)

button_box&amp;nbsp;=&amp;nbsp;QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok
|&amp;nbsp;QDialogButtonBox.StandardButton.Cancel
)
button_box.button(QDialogButtonBox.StandardButton.Ok).setText(&amp;quot;确定&amp;quot;)
button_box.button(QDialogButtonBox.StandardButton.Cancel).setText(&amp;quot;取消&amp;quot;)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)

self.title_input.setFocus()

def&amp;nbsp;get_values(self):
return&amp;nbsp;(
self.title_input.text().strip(),
self.content_input.toPlainText().strip(),
)


class&amp;nbsp;ConfirmDialog(QDialog):
&amp;quot;&amp;quot;&amp;quot;与应用风格一致的确认弹窗：纯文本，无图标。&amp;quot;&amp;quot;&amp;quot;

def&amp;nbsp;__init__(
self,
parent=None,
title=&amp;quot;确认&amp;quot;,
message=&amp;quot;&amp;quot;,
ok_text=&amp;quot;确定&amp;quot;,
cancel_text=&amp;quot;取消&amp;quot;,
):
super().__init__(parent)

self.setObjectName(&amp;quot;confirmDialog&amp;quot;)
self.setWindowTitle(title)
self.setMinimumWidth(320)
self.setStyleSheet(STYLE_SHEET)

layout&amp;nbsp;=&amp;nbsp;QVBoxLayout(self)
layout.setContentsMargins(24,&amp;nbsp;22,&amp;nbsp;24,&amp;nbsp;20)
layout.setSpacing(10)

title_label&amp;nbsp;=&amp;nbsp;QLabel(title)
title_label.setObjectName(&amp;quot;confirmTitle&amp;quot;)
layout.addWidget(title_label)

message_label&amp;nbsp;=&amp;nbsp;QLabel(message)
message_label.setObjectName(&amp;quot;confirmMessage&amp;quot;)
message_label.setWordWrap(True)
layout.addWidget(message_label)

layout.addSpacing(8)

btn_row&amp;nbsp;=&amp;nbsp;QHBoxLayout()
btn_row.setSpacing(10)
btn_row.addStretch()

cancel_btn&amp;nbsp;=&amp;nbsp;QPushButton(cancel_text)
cancel_btn.setObjectName(&amp;quot;confirmCancelBtn&amp;quot;)
cancel_btn.clicked.connect(self.reject)

ok_btn&amp;nbsp;=&amp;nbsp;QPushButton(ok_text)
ok_btn.setObjectName(&amp;quot;confirmDangerBtn&amp;quot;)
ok_btn.clicked.connect(self.accept)

btn_row.addWidget(cancel_btn)
btn_row.addWidget(ok_btn)
layout.addLayout(btn_row)

cancel_btn.setFocus()


#&amp;nbsp;全局快捷键：使用&amp;nbsp;macOS&amp;nbsp;Carbon&amp;nbsp;RegisterEventHotKey&amp;nbsp;仅监听&amp;nbsp;Cmd+Y，
#&amp;nbsp;避免&amp;nbsp;pynput&amp;nbsp;全局事件&amp;nbsp;tap&amp;nbsp;在切换输入法时崩溃，且无需辅助功能权限。

class&amp;nbsp;HotkeySignals(QObject):
trigger_show&amp;nbsp;=&amp;nbsp;pyqtSignal()


HOTKEY_SIGNALS&amp;nbsp;=&amp;nbsp;HotkeySignals()

try:
_CARBON&amp;nbsp;=&amp;nbsp;ctypes.CDLL(ctypes.util.find_library(&amp;quot;Carbon&amp;quot;))
except&amp;nbsp;Exception:
_CARBON&amp;nbsp;=&amp;nbsp;None


class&amp;nbsp;_EventHotKeyID(ctypes.Structure):
_fields_&amp;nbsp;=&amp;nbsp;[
(&amp;quot;signature&amp;quot;,&amp;nbsp;ctypes.c_uint32),
(&amp;quot;id&amp;quot;,&amp;nbsp;ctypes.c_uint32),
]


class&amp;nbsp;_EventTypeSpec(ctypes.Structure):
_fields_&amp;nbsp;=&amp;nbsp;[
(&amp;quot;eventClass&amp;quot;,&amp;nbsp;ctypes.c_uint32),
(&amp;quot;eventKind&amp;quot;,&amp;nbsp;ctypes.c_uint32),
]


_kEventClassKeyboard&amp;nbsp;=&amp;nbsp;0x6B657962
_kEventHotKeyPressed&amp;nbsp;=&amp;nbsp;5
_cmdKey&amp;nbsp;=&amp;nbsp;0x0100
_kVK_ANSI_Y&amp;nbsp;=&amp;nbsp;0x10
_HOTKEY_SIGNATURE&amp;nbsp;=&amp;nbsp;0x736E7079
_HOTKEY_ID&amp;nbsp;=&amp;nbsp;1

_HOTKEY_HANDLER_PROTO&amp;nbsp;=&amp;nbsp;ctypes.CFUNCTYPE(
ctypes.c_int32,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_void_p,
)

#&amp;nbsp;模块级全局引用，防止被&amp;nbsp;GC&amp;nbsp;回收导致回调悬挂/崩溃
_HOTKEY_REF&amp;nbsp;=&amp;nbsp;None
_HOTKEY_HANDLER_REF&amp;nbsp;=&amp;nbsp;None
_HOTKEY_CALLBACK&amp;nbsp;=&amp;nbsp;None
_HOTKEY_INSTALLED&amp;nbsp;=&amp;nbsp;False


def&amp;nbsp;_configure_carbon_signatures():
&amp;quot;&amp;quot;&amp;quot;显式设置&amp;nbsp;Carbon&amp;nbsp;函数签名，避免&amp;nbsp;64&amp;nbsp;位指针被截断。&amp;quot;&amp;quot;&amp;quot;
_CARBON.GetApplicationEventTarget.restype&amp;nbsp;=&amp;nbsp;ctypes.c_void_p
_CARBON.GetApplicationEventTarget.argtypes&amp;nbsp;=&amp;nbsp;[]

_CARBON.InstallEventHandler.restype&amp;nbsp;=&amp;nbsp;ctypes.c_int32
_CARBON.InstallEventHandler.argtypes&amp;nbsp;=&amp;nbsp;[
ctypes.c_void_p,
_HOTKEY_HANDLER_PROTO,
ctypes.c_uint32,
ctypes.POINTER(_EventTypeSpec),
ctypes.c_void_p,
ctypes.POINTER(ctypes.c_void_p),
]

_CARBON.RegisterEventHotKey.restype&amp;nbsp;=&amp;nbsp;ctypes.c_int32
_CARBON.RegisterEventHotKey.argtypes&amp;nbsp;=&amp;nbsp;[
ctypes.c_uint32,
ctypes.c_uint32,
_EventHotKeyID,
ctypes.c_void_p,
ctypes.c_uint32,
ctypes.POINTER(ctypes.c_void_p),
]

_CARBON.UnregisterEventHotKey.restype&amp;nbsp;=&amp;nbsp;ctypes.c_int32
_CARBON.UnregisterEventHotKey.argtypes&amp;nbsp;=&amp;nbsp;[ctypes.c_void_p]

_CARBON.RemoveEventHandler.restype&amp;nbsp;=&amp;nbsp;ctypes.c_int32
_CARBON.RemoveEventHandler.argtypes&amp;nbsp;=&amp;nbsp;[ctypes.c_void_p]


def&amp;nbsp;_on_hotkey(_next_handler,&amp;nbsp;_event,&amp;nbsp;_user_data):
try:
HOTKEY_SIGNALS.trigger_show.emit()
except&amp;nbsp;Exception:
pass
return&amp;nbsp;0


def&amp;nbsp;start_hotkey():
&amp;quot;&amp;quot;&amp;quot;注册全局热键（Cmd+Y），幂等。成功返回&amp;nbsp;True。&amp;quot;&amp;quot;&amp;quot;
global&amp;nbsp;_HOTKEY_REF,&amp;nbsp;_HOTKEY_HANDLER_REF,&amp;nbsp;_HOTKEY_CALLBACK
global&amp;nbsp;_HOTKEY_INSTALLED

if&amp;nbsp;_HOTKEY_INSTALLED:
return&amp;nbsp;True
if&amp;nbsp;_CARBON&amp;nbsp;is&amp;nbsp;None:
return&amp;nbsp;False

try:
_configure_carbon_signatures()

target&amp;nbsp;=&amp;nbsp;_CARBON.GetApplicationEventTarget()
if&amp;nbsp;not&amp;nbsp;target:
return&amp;nbsp;False

_HOTKEY_CALLBACK&amp;nbsp;=&amp;nbsp;_HOTKEY_HANDLER_PROTO(_on_hotkey)
event_spec&amp;nbsp;=&amp;nbsp;_EventTypeSpec(_kEventClassKeyboard,&amp;nbsp;_kEventHotKeyPressed)
handler_ref&amp;nbsp;=&amp;nbsp;ctypes.c_void_p()
status&amp;nbsp;=&amp;nbsp;_CARBON.InstallEventHandler(
target,
_HOTKEY_CALLBACK,
1,
ctypes.byref(event_spec),
None,
ctypes.byref(handler_ref),
)
if&amp;nbsp;status&amp;nbsp;!=&amp;nbsp;0:
_HOTKEY_CALLBACK&amp;nbsp;=&amp;nbsp;None
return&amp;nbsp;False
_HOTKEY_HANDLER_REF&amp;nbsp;=&amp;nbsp;handler_ref

hotkey_id&amp;nbsp;=&amp;nbsp;_EventHotKeyID(_HOTKEY_SIGNATURE,&amp;nbsp;_HOTKEY_ID)
hotkey_ref&amp;nbsp;=&amp;nbsp;ctypes.c_void_p()
status&amp;nbsp;=&amp;nbsp;_CARBON.RegisterEventHotKey(
_kVK_ANSI_Y,
_cmdKey,
hotkey_id,
target,
0,
ctypes.byref(hotkey_ref),
)
if&amp;nbsp;status&amp;nbsp;!=&amp;nbsp;0:
try:
_CARBON.RemoveEventHandler(_HOTKEY_HANDLER_REF)
except&amp;nbsp;Exception:
pass
_HOTKEY_HANDLER_REF&amp;nbsp;=&amp;nbsp;None
_HOTKEY_CALLBACK&amp;nbsp;=&amp;nbsp;None
return&amp;nbsp;False
_HOTKEY_REF&amp;nbsp;=&amp;nbsp;hotkey_ref

_HOTKEY_INSTALLED&amp;nbsp;=&amp;nbsp;True
return&amp;nbsp;True
except&amp;nbsp;Exception:
return&amp;nbsp;False


def&amp;nbsp;stop_hotkey():
&amp;quot;&amp;quot;&amp;quot;注销全局热键并移除事件处理器。&amp;quot;&amp;quot;&amp;quot;
global&amp;nbsp;_HOTKEY_REF,&amp;nbsp;_HOTKEY_HANDLER_REF,&amp;nbsp;_HOTKEY_CALLBACK
global&amp;nbsp;_HOTKEY_INSTALLED

if&amp;nbsp;_CARBON&amp;nbsp;is&amp;nbsp;None:
return

try:
if&amp;nbsp;_HOTKEY_REF&amp;nbsp;is&amp;nbsp;not&amp;nbsp;None:
_CARBON.UnregisterEventHotKey(_HOTKEY_REF)
except&amp;nbsp;Exception:
pass

try:
if&amp;nbsp;_HOTKEY_HANDLER_REF&amp;nbsp;is&amp;nbsp;not&amp;nbsp;None:
_CARBON.RemoveEventHandler(_HOTKEY_HANDLER_REF)
except&amp;nbsp;Exception:
pass

_HOTKEY_REF&amp;nbsp;=&amp;nbsp;None
_HOTKEY_HANDLER_REF&amp;nbsp;=&amp;nbsp;None
_HOTKEY_CALLBACK&amp;nbsp;=&amp;nbsp;None
_HOTKEY_INSTALLED&amp;nbsp;=&amp;nbsp;False


class&amp;nbsp;SnippetWindow(QWidget):

def&amp;nbsp;__init__(self):
super().__init__()

self.data&amp;nbsp;=&amp;nbsp;load_data()
self._drag_pos&amp;nbsp;=&amp;nbsp;None

self.setWindowTitle(&amp;quot;Snippet&amp;nbsp;Panel&amp;quot;)
self.setWindowFlags(
Qt.WindowType.Tool
|&amp;nbsp;Qt.WindowType.FramelessWindowHint
|&amp;nbsp;Qt.WindowType.WindowStaysOnTopHint
)
self.resize(420,&amp;nbsp;560)
self.init_ui()

HOTKEY_SIGNALS.trigger_show.connect(self.force_show)

def&amp;nbsp;init_ui(self):
layout&amp;nbsp;=&amp;nbsp;QVBoxLayout()
layout.setContentsMargins(16,&amp;nbsp;16,&amp;nbsp;16,&amp;nbsp;16)
layout.setSpacing(12)

self.title_bar&amp;nbsp;=&amp;nbsp;QWidget()
self.title_bar.setObjectName(&amp;quot;titleBar&amp;quot;)

top_bar&amp;nbsp;=&amp;nbsp;QHBoxLayout(self.title_bar)
top_bar.setContentsMargins(0,&amp;nbsp;0,&amp;nbsp;0,&amp;nbsp;0)

title_label&amp;nbsp;=&amp;nbsp;QLabel(&amp;quot;短语板&amp;quot;)
title_label.setObjectName(&amp;quot;titleLabel&amp;quot;)

self.copy_tip&amp;nbsp;=&amp;nbsp;QLabel(&amp;quot;&amp;quot;)
self.copy_tip.setObjectName(&amp;quot;copyTip&amp;quot;)
self.copy_tip.setMaximumWidth(180)
self.copy_tip.hide()

self._copy_tip_timer&amp;nbsp;=&amp;nbsp;QTimer(self)
self._copy_tip_timer.setSingleShot(True)
self._copy_tip_timer.timeout.connect(self.copy_tip.hide)

self.add_btn&amp;nbsp;=&amp;nbsp;QPushButton(&amp;quot;+&amp;quot;)
self.add_btn.setObjectName(&amp;quot;titleBarBtn&amp;quot;)
self.add_btn.setCursor(Qt.CursorShape.PointingHandCursor)
self.add_btn.clicked.connect(self.add_item)

self.menu_btn&amp;nbsp;=&amp;nbsp;QPushButton(&amp;quot;···&amp;quot;)
self.menu_btn.setObjectName(&amp;quot;titleBarBtn&amp;quot;)
self.menu_btn.setCursor(Qt.CursorShape.PointingHandCursor)
self.menu_btn.clicked.connect(self._show_menu)

top_bar.addWidget(title_label)
top_bar.addStretch()
top_bar.addWidget(self.copy_tip)
top_bar.addStretch()
top_bar.addWidget(self.add_btn)
top_bar.addWidget(self.menu_btn)

layout.addWidget(self.title_bar)

self.search&amp;nbsp;=&amp;nbsp;QLineEdit()
self.search.setObjectName(&amp;quot;searchBox&amp;quot;)
self.search.setPlaceholderText(&amp;quot;搜索...&amp;quot;)
self.search.textChanged.connect(self.refresh_list)
layout.addWidget(self.search)

self.list_widget&amp;nbsp;=&amp;nbsp;QListWidget()
self.list_widget.setObjectName(&amp;quot;snippetList&amp;quot;)
self.list_widget.setSpacing(8)
self.list_widget.itemDoubleClicked.connect(self._on_item_double_clicked)
layout.addWidget(self.list_widget)

self.setLayout(layout)
self.setStyleSheet(STYLE_SHEET)

self.refresh_list()

def&amp;nbsp;refresh_list(self):
keyword&amp;nbsp;=&amp;nbsp;self.search.text().lower()
self.list_widget.clear()

for&amp;nbsp;item&amp;nbsp;in&amp;nbsp;self.data:
if&amp;nbsp;(
keyword&amp;nbsp;in&amp;nbsp;item[&amp;quot;title&amp;quot;].lower()
or&amp;nbsp;keyword&amp;nbsp;in&amp;nbsp;item[&amp;quot;content&amp;quot;].lower()
):
self._add_card(item)

def&amp;nbsp;_add_card(self,&amp;nbsp;item):
title&amp;nbsp;=&amp;nbsp;item[&amp;quot;title&amp;quot;]
content&amp;nbsp;=&amp;nbsp;item[&amp;quot;content&amp;quot;]

list_item&amp;nbsp;=&amp;nbsp;QListWidgetItem(self.list_widget)
list_item.setSizeHint(QSize(0,&amp;nbsp;124))

card&amp;nbsp;=&amp;nbsp;QWidget()
card.setObjectName(&amp;quot;cardWidget&amp;quot;)

card_layout&amp;nbsp;=&amp;nbsp;QVBoxLayout(card)
card_layout.setContentsMargins(16,&amp;nbsp;12,&amp;nbsp;16,&amp;nbsp;8)
card_layout.setSpacing(6)

title_label&amp;nbsp;=&amp;nbsp;QLabel(title)
title_label.setObjectName(&amp;quot;cardTitle&amp;quot;)

content_label&amp;nbsp;=&amp;nbsp;ElidedLabel(content)
content_label.setObjectName(&amp;quot;cardLabel&amp;quot;)

card_layout.addWidget(title_label)
card_layout.addWidget(content_label)

action_row&amp;nbsp;=&amp;nbsp;QHBoxLayout()
action_row.setContentsMargins(0,&amp;nbsp;0,&amp;nbsp;0,&amp;nbsp;0)
action_row.setSpacing(4)
action_row.addStretch()

copy_btn&amp;nbsp;=&amp;nbsp;QPushButton(&amp;quot;复制&amp;quot;)
copy_btn.setObjectName(&amp;quot;cardActionBtn&amp;quot;)
copy_btn.setCursor(Qt.CursorShape.PointingHandCursor)
copy_btn.clicked.connect(lambda&amp;nbsp;_=False,&amp;nbsp;it=item:&amp;nbsp;self._copy_item(it))

del_btn&amp;nbsp;=&amp;nbsp;QPushButton(&amp;quot;删除&amp;quot;)
del_btn.setObjectName(&amp;quot;cardActionDangerBtn&amp;quot;)
del_btn.setCursor(Qt.CursorShape.PointingHandCursor)
del_btn.clicked.connect(lambda&amp;nbsp;_=False,&amp;nbsp;it=item:&amp;nbsp;self._delete_item(it))

action_row.addWidget(copy_btn)
action_row.addWidget(del_btn)
card_layout.addLayout(action_row)

self.list_widget.addItem(list_item)
self.list_widget.setItemWidget(list_item,&amp;nbsp;card)

def&amp;nbsp;get_selected_index(self):
row&amp;nbsp;=&amp;nbsp;self.list_widget.currentRow()
if&amp;nbsp;row&amp;nbsp;&amp;lt;&amp;nbsp;0:
return&amp;nbsp;None

keyword&amp;nbsp;=&amp;nbsp;self.search.text().lower()
filtered&amp;nbsp;=&amp;nbsp;[
item&amp;nbsp;for&amp;nbsp;item&amp;nbsp;in&amp;nbsp;self.data
if&amp;nbsp;keyword&amp;nbsp;in&amp;nbsp;item[&amp;quot;title&amp;quot;].lower()
or&amp;nbsp;keyword&amp;nbsp;in&amp;nbsp;item[&amp;quot;content&amp;quot;].lower()
]
return&amp;nbsp;self.data.index(filtered[row])

def&amp;nbsp;add_item(self):
dialog&amp;nbsp;=&amp;nbsp;SnippetDialog(self,&amp;nbsp;window_title=&amp;quot;新增&amp;quot;)
if&amp;nbsp;dialog.exec()&amp;nbsp;!=&amp;nbsp;QDialog.DialogCode.Accepted:
return

title,&amp;nbsp;content&amp;nbsp;=&amp;nbsp;dialog.get_values()
if&amp;nbsp;not&amp;nbsp;title:
return

self.data.append({&amp;quot;title&amp;quot;:&amp;nbsp;title,&amp;nbsp;&amp;quot;content&amp;quot;:&amp;nbsp;content})
save_data(self.data)
self.refresh_list()

def&amp;nbsp;edit_item(self):
idx&amp;nbsp;=&amp;nbsp;self.get_selected_index()
if&amp;nbsp;idx&amp;nbsp;is&amp;nbsp;None:
return

item&amp;nbsp;=&amp;nbsp;self.data[idx]
dialog&amp;nbsp;=&amp;nbsp;SnippetDialog(
self,
window_title=&amp;quot;编辑&amp;quot;,
title=item[&amp;quot;title&amp;quot;],
content=item[&amp;quot;content&amp;quot;],
)
if&amp;nbsp;dialog.exec()&amp;nbsp;!=&amp;nbsp;QDialog.DialogCode.Accepted:
return

title,&amp;nbsp;content&amp;nbsp;=&amp;nbsp;dialog.get_values()
if&amp;nbsp;not&amp;nbsp;title:
return

self.data[idx]&amp;nbsp;=&amp;nbsp;{&amp;quot;title&amp;quot;:&amp;nbsp;title,&amp;nbsp;&amp;quot;content&amp;quot;:&amp;nbsp;content}
save_data(self.data)
self.refresh_list()

def&amp;nbsp;_delete_item(self,&amp;nbsp;item):
try:
idx&amp;nbsp;=&amp;nbsp;self.data.index(item)
except&amp;nbsp;ValueError:
return
self._delete_by_index(idx)

def&amp;nbsp;_delete_by_index(self,&amp;nbsp;idx):
dialog&amp;nbsp;=&amp;nbsp;ConfirmDialog(
self,
title=&amp;quot;删除&amp;quot;,
message=&amp;quot;确定要删除这条短语吗？&amp;quot;,
ok_text=&amp;quot;删除&amp;quot;,
cancel_text=&amp;quot;取消&amp;quot;,
)
if&amp;nbsp;dialog.exec()&amp;nbsp;!=&amp;nbsp;QDialog.DialogCode.Accepted:
return

del&amp;nbsp;self.data[idx]
save_data(self.data)
self.refresh_list()

def&amp;nbsp;_on_item_double_clicked(self,&amp;nbsp;list_item):
self.list_widget.setCurrentItem(list_item)
self.edit_item()

def&amp;nbsp;_copy_item(self,&amp;nbsp;item):
try:
idx&amp;nbsp;=&amp;nbsp;self.data.index(item)
except&amp;nbsp;ValueError:
return
self._copy_by_index(idx)

def&amp;nbsp;_copy_by_index(self,&amp;nbsp;idx):
pyperclip.copy(self.data[idx][&amp;quot;content&amp;quot;])
self._show_copy_tip(self.data[idx][&amp;quot;title&amp;quot;])

def&amp;nbsp;_show_copy_tip(self,&amp;nbsp;title):
text&amp;nbsp;=&amp;nbsp;f&amp;quot;复制成功：{title}&amp;quot;
metrics&amp;nbsp;=&amp;nbsp;QFontMetrics(self.copy_tip.font())
elided&amp;nbsp;=&amp;nbsp;metrics.elidedText(
text,
Qt.TextElideMode.ElideRight,
self.copy_tip.maximumWidth(),
)
self.copy_tip.setText(elided)
self.copy_tip.show()
self._copy_tip_timer.start(1000)

def&amp;nbsp;_show_menu(self):
menu&amp;nbsp;=&amp;nbsp;QMenu(self)
menu.setObjectName(&amp;quot;titleMenu&amp;quot;)

quit_action&amp;nbsp;=&amp;nbsp;QAction(&amp;quot;结束进程&amp;quot;,&amp;nbsp;self)
quit_action.triggered.connect(self.force_quit)
menu.addAction(quit_action)

pos&amp;nbsp;=&amp;nbsp;self.menu_btn.mapToGlobal(self.menu_btn.rect().bottomRight())
menu.exec(pos&amp;nbsp;-&amp;nbsp;QPoint(menu.sizeHint().width(),&amp;nbsp;0))

def&amp;nbsp;force_quit(self):
dialog&amp;nbsp;=&amp;nbsp;ConfirmDialog(
self,
title=&amp;quot;强制结束&amp;quot;,
message=&amp;quot;确定要强制结束进程吗？应用将完全退出。&amp;quot;,
ok_text=&amp;quot;强制结束&amp;quot;,
cancel_text=&amp;quot;取消&amp;quot;,
)
if&amp;nbsp;dialog.exec()&amp;nbsp;!=&amp;nbsp;QDialog.DialogCode.Accepted:
return

stop_hotkey()
os._exit(0)

def&amp;nbsp;_close_child_dialogs(self):
&amp;quot;&amp;quot;&amp;quot;关闭所有由主窗口打开的弹窗。&amp;quot;&amp;quot;&amp;quot;
for&amp;nbsp;dlg&amp;nbsp;in&amp;nbsp;self.findChildren(QDialog):
dlg.reject()
dlg.close()

def&amp;nbsp;force_show(self):
self._close_child_dialogs()
self.show()
self.raise_()
self.activateWindow()
self.search.setFocus()

def&amp;nbsp;mousePressEvent(self,&amp;nbsp;event):
if&amp;nbsp;event.button()&amp;nbsp;==&amp;nbsp;Qt.MouseButton.LeftButton:
pos&amp;nbsp;=&amp;nbsp;event.position().toPoint()
if&amp;nbsp;pos.y()&amp;nbsp;&amp;lt;&amp;nbsp;self.title_bar.geometry().top():
self._drag_pos&amp;nbsp;=&amp;nbsp;(
event.globalPosition().toPoint()
-&amp;nbsp;self.frameGeometry().topLeft()
)
event.accept()
return
super().mousePressEvent(event)

def&amp;nbsp;mouseMoveEvent(self,&amp;nbsp;event):
if&amp;nbsp;(
self._drag_pos&amp;nbsp;is&amp;nbsp;not&amp;nbsp;None
and&amp;nbsp;event.buttons()&amp;nbsp;&amp;amp;&amp;nbsp;Qt.MouseButton.LeftButton
):
self.move(event.globalPosition().toPoint()&amp;nbsp;-&amp;nbsp;self._drag_pos)
event.accept()
return
super().mouseMoveEvent(event)

def&amp;nbsp;mouseReleaseEvent(self,&amp;nbsp;event):
self._drag_pos&amp;nbsp;=&amp;nbsp;None
super().mouseReleaseEvent(event)

def&amp;nbsp;changeEvent(self,&amp;nbsp;event):
if&amp;nbsp;event.type()&amp;nbsp;==&amp;nbsp;QEvent.Type.ActivationChange:
if&amp;nbsp;not&amp;nbsp;self.isActiveWindow():
self.hide()
super().changeEvent(event)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
app&amp;nbsp;=&amp;nbsp;QApplication(sys.argv)

window&amp;nbsp;=&amp;nbsp;SnippetWindow()
window.hide()

if&amp;nbsp;not&amp;nbsp;start_hotkey():
ConfirmDialog(
window,
title=&amp;quot;快捷键注册失败&amp;quot;,
message=(
&amp;quot;全局快捷键&amp;nbsp;Cmd+Y&amp;nbsp;注册失败，可能已被其他应用占用。\n\n&amp;quot;
&amp;quot;请关闭占用该组合键的应用后重启本程序。&amp;quot;
),
ok_text=&amp;quot;我知道了&amp;quot;,
cancel_text=&amp;quot;关闭&amp;quot;,
).exec()

sys.exit(app.exec())&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606221782130513905656.png&quot; alt=&quot;截屏2026-06-22 20.15.00.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Mon, 22 Jun 2026 20:14:40 +0800</pubDate></item><item><title>Spring Security + Redis 的无状态分布式认证架构设计</title><link>https://pan.anyuer.club/?id=294</link><description>&lt;h1&gt;传统Session和JWT的弊端&lt;/h1&gt;&lt;h2&gt;Session&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;在传统Web开发中，服务器通过Tomcat的 HttpSession（基于Cookie中的 JSESSIONID）维持状态。但在现代微服务与前后端分离架构下，这种方案存在天然弊端：&lt;/p&gt;&lt;p&gt;1.集群横向扩展困难：Session常驻单机内存，多台服务器间同步成本极高。&lt;/p&gt;&lt;p&gt;2.安全缺陷：基于Cookie的认证天然难以防范CSRF（跨站请求伪造）攻击。&lt;/p&gt;&lt;p&gt;3.为了解决有状态（Stateful）的痛点，业界普遍转向无状态（Stateless）架构。&lt;/p&gt;&lt;h2&gt;JWT&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606051780672062316915.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;JWT仅适用于单向、短期、无状态的边缘授权（如文件下载凭证、邮件激活码）；而对于需要强管控、高并发、支持滑动续期和踢人下线的企业级核心用户系统，Redis Token 才是生产环境的终极解法。&lt;/p&gt;&lt;h1&gt;实现思路&lt;/h1&gt;&lt;p&gt;1.请求进入网关或Tomcat后，自定义过滤器 RedisTokenFilter 拦截请求，解析 HTTP Header 中的 Authorization 字段。&lt;/p&gt;&lt;p&gt;2.过滤器拿着Token去Redis集群检索。若命中，则说明该用户处于合法登录状态。随后将其包装为框架可识别的令牌，注入 SecurityContextHolder（底层基于 ThreadLocal，确保多线程并发下的线程隔离与安全）。&lt;/p&gt;&lt;p&gt;3.请求流向Spring Security的核心鉴权层（AuthorizationFilter）。鉴权层根据 SecurityConfig 中配置的黑白名单规则，比对当前线程上下文中是否存在令牌。核验通过则放行至 Controller，否则直接响应 403。&lt;/p&gt;&lt;h1&gt;代码实现&lt;/h1&gt;&lt;h2&gt;SQL数据表&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;如果你还想做权限管理，我建议把数据库的用户表，增加一个字段用来区分权限。&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;--&amp;nbsp;phpMyAdmin&amp;nbsp;SQL&amp;nbsp;Dump
--&amp;nbsp;version&amp;nbsp;5.2.3
--&amp;nbsp;主机：&amp;nbsp;localhost
--&amp;nbsp;服务器版本：&amp;nbsp;5.7.44-log
--&amp;nbsp;PHP&amp;nbsp;版本：&amp;nbsp;8.2.28

SET&amp;nbsp;SQL_MODE&amp;nbsp;=&amp;nbsp;&amp;quot;NO_AUTO_VALUE_ON_ZERO&amp;quot;;
START&amp;nbsp;TRANSACTION;
SET&amp;nbsp;time_zone&amp;nbsp;=&amp;nbsp;&amp;quot;+00:00&amp;quot;;


/*!40101&amp;nbsp;SET&amp;nbsp;@OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT&amp;nbsp;*/;
/*!40101&amp;nbsp;SET&amp;nbsp;@OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS&amp;nbsp;*/;
/*!40101&amp;nbsp;SET&amp;nbsp;@OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION&amp;nbsp;*/;
/*!40101&amp;nbsp;SET&amp;nbsp;NAMES&amp;nbsp;utf8mb4&amp;nbsp;*/;

--
--&amp;nbsp;数据库：&amp;nbsp;`auth_study`
--

--&amp;nbsp;--------------------------------------------------------

--
--&amp;nbsp;表的结构&amp;nbsp;`s_users`
--

CREATE&amp;nbsp;TABLE&amp;nbsp;`s_users`&amp;nbsp;(
&amp;nbsp;&amp;nbsp;`uid`&amp;nbsp;int(11)&amp;nbsp;NOT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`role`&amp;nbsp;varchar(11)&amp;nbsp;NOT&amp;nbsp;NULL
)&amp;nbsp;ENGINE=InnoDB&amp;nbsp;DEFAULT&amp;nbsp;CHARSET=utf8mb4;

--
--&amp;nbsp;转存表中的数据&amp;nbsp;`s_users`
--

INSERT&amp;nbsp;INTO&amp;nbsp;`s_users`&amp;nbsp;(`uid`,&amp;nbsp;`role`)&amp;nbsp;VALUES
(1,&amp;nbsp;&amp;#39;user&amp;#39;),
(2,&amp;nbsp;&amp;#39;admin&amp;#39;);

--
--&amp;nbsp;转储表的索引
--

--
--&amp;nbsp;表的索引&amp;nbsp;`s_users`
--
ALTER&amp;nbsp;TABLE&amp;nbsp;`s_users`
&amp;nbsp;&amp;nbsp;ADD&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;(`uid`);
COMMIT;

/*!40101&amp;nbsp;SET&amp;nbsp;CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT&amp;nbsp;*/;
/*!40101&amp;nbsp;SET&amp;nbsp;CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS&amp;nbsp;*/;
/*!40101&amp;nbsp;SET&amp;nbsp;COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION&amp;nbsp;*/;&lt;/pre&gt;&lt;h2&gt;SecurityConfig&lt;/h2&gt;&lt;p&gt;可以看到，login接口是所有人哪怕未登录的游客也可以访问，但是admin接口是必须要权限为admin的用户才可以访问，hello接口的话登录了都可以访问&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.redisauth.config;

import&amp;nbsp;com.redisauth.util.RedisUtils;
import&amp;nbsp;org.springframework.context.annotation.Bean;
import&amp;nbsp;org.springframework.context.annotation.Configuration;
import&amp;nbsp;org.springframework.security.config.annotation.web.builders.HttpSecurity;
import&amp;nbsp;org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import&amp;nbsp;org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import&amp;nbsp;org.springframework.security.config.http.SessionCreationPolicy;
import&amp;nbsp;org.springframework.security.web.SecurityFilterChain;
import&amp;nbsp;org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public&amp;nbsp;class&amp;nbsp;SecurityConfig&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;final&amp;nbsp;RedisUtils&amp;nbsp;redisUtils;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;SecurityConfig(RedisUtils&amp;nbsp;redisUtils)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.redisUtils&amp;nbsp;=&amp;nbsp;redisUtils;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Bean
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;SecurityFilterChain&amp;nbsp;securityFilterChain(HttpSecurity&amp;nbsp;http)&amp;nbsp;throws&amp;nbsp;Exception&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;http
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.csrf(AbstractHttpConfigurer::disable)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.formLogin(AbstractHttpConfigurer::disable)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.httpBasic(AbstractHttpConfigurer::disable)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.addFilterBefore(new&amp;nbsp;AuthorityFilter(redisUtils),&amp;nbsp;UsernamePasswordAuthenticationFilter.class)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.sessionManagement(session&amp;nbsp;-&amp;gt;&amp;nbsp;session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.authorizeHttpRequests(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auth&amp;nbsp;-&amp;gt;&amp;nbsp;auth
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.requestMatchers(&amp;quot;/login&amp;quot;).permitAll()&amp;nbsp;//&amp;nbsp;允许未登录时访问登录接口
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.requestMatchers(&amp;quot;/admin&amp;quot;).hasRole(&amp;quot;admin&amp;quot;)&amp;nbsp;//&amp;nbsp;允许用户权限为admin的用户登录
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.anyRequest().authenticated()&amp;nbsp;//&amp;nbsp;其它所有剩余接口默认都需要登录
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;http.build();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;AuthorityFilter&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;当客户端携带该Token请求受保护的业务接口时，请求首先被AuthorityFilter安全过滤器拦截，过滤器从请求头提取出Token并拼接成对应的TokenKey去Redis中检索JSON字符串，若未命中则直接放行，由后续的Spring Security鉴权拦截器因上下文为空而统一响应403拒绝访问。
如果成功获取到JSON字符串，过滤器会将其反序列化解析出用户ID和角色，随后为了防御僵尸Token偷跑，过滤器利用解析出的用户ID实时去Redis中读取最新的UidKey，并将当前请求携带的Token与Redis中记录的最新活跃Token进行字符串一致性比对。
若比对完全一致，证明当前请求来自最后登录的合法设备，过滤器便为该角色组装框架认账的权限列表并注入当前请求的线程上下文中恢复合法身份，同时同步为当前的TokenKey和UidKey执行expire操作完成生命周期对齐的滑动续期；反之若比对不一致，说明该Token已被新设备顶替，过滤器则直接拒绝恢复其身份并顺手将该残余的TokenKey从Redis中连根拔除以释放内存，最后调用过滤链放行方法将请求安全传递给后续的路由鉴权关卡。&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.redisauth.config;

import&amp;nbsp;com.fasterxml.jackson.databind.JsonNode;
import&amp;nbsp;com.fasterxml.jackson.databind.ObjectMapper;
import&amp;nbsp;com.redisauth.util.RedisUtils;
import&amp;nbsp;jakarta.servlet.FilterChain;
import&amp;nbsp;jakarta.servlet.ServletException;
import&amp;nbsp;jakarta.servlet.http.HttpServletRequest;
import&amp;nbsp;jakarta.servlet.http.HttpServletResponse;
import&amp;nbsp;org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import&amp;nbsp;org.springframework.security.core.GrantedAuthority;
import&amp;nbsp;org.springframework.security.core.authority.SimpleGrantedAuthority;
import&amp;nbsp;org.springframework.security.core.context.SecurityContextHolder;
import&amp;nbsp;org.springframework.util.StringUtils;
import&amp;nbsp;org.springframework.web.filter.OncePerRequestFilter;

import&amp;nbsp;java.io.IOException;
import&amp;nbsp;java.util.ArrayList;
import&amp;nbsp;java.util.List;
import&amp;nbsp;java.util.concurrent.TimeUnit;

public&amp;nbsp;class&amp;nbsp;AuthorityFilter&amp;nbsp;extends&amp;nbsp;OncePerRequestFilter&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;final&amp;nbsp;RedisUtils&amp;nbsp;redisUtils;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;final&amp;nbsp;ObjectMapper&amp;nbsp;objectMapper&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ObjectMapper();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;AuthorityFilter(RedisUtils&amp;nbsp;redisUtils)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.redisUtils&amp;nbsp;=&amp;nbsp;redisUtils;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;void&amp;nbsp;doFilterInternal(HttpServletRequest&amp;nbsp;request,&amp;nbsp;HttpServletResponse&amp;nbsp;response,&amp;nbsp;FilterChain&amp;nbsp;filterChain)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throws&amp;nbsp;ServletException,&amp;nbsp;IOException&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;token&amp;nbsp;=&amp;nbsp;request.getHeader(redisUtils.HEADER);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(StringUtils.hasText(token))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;redisKey&amp;nbsp;=&amp;nbsp;redisUtils.tokenPreKey&amp;nbsp;+&amp;nbsp;token;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;jsonStr&amp;nbsp;=&amp;nbsp;redisUtils.get(redisKey);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(StringUtils.hasText(jsonStr))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;1.&amp;nbsp;解析&amp;nbsp;JSON&amp;nbsp;数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JsonNode&amp;nbsp;jsonNode&amp;nbsp;=&amp;nbsp;objectMapper.readTree(jsonStr);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;uid&amp;nbsp;=&amp;nbsp;jsonNode.get(&amp;quot;uid&amp;quot;).asText();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;role&amp;nbsp;=&amp;nbsp;jsonNode.get(&amp;quot;role&amp;quot;).asText();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;=====&amp;nbsp;漏掉的单端核心校验开始&amp;nbsp;=====
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;uidKey&amp;nbsp;=&amp;nbsp;redisUtils.uidPreKey&amp;nbsp;+&amp;nbsp;uid;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;currentActiveToken&amp;nbsp;=&amp;nbsp;redisUtils.get(uidKey);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(token.equals(currentActiveToken))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;只有携带的&amp;nbsp;Token&amp;nbsp;确实是当前&amp;nbsp;Redis&amp;nbsp;记录的最活跃&amp;nbsp;Token&amp;nbsp;时，才放行并续期
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;GrantedAuthority&amp;gt;&amp;nbsp;authorities&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ArrayList&amp;lt;&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;authorities.add(new&amp;nbsp;SimpleGrantedAuthority(&amp;quot;ROLE_&amp;quot;&amp;nbsp;+&amp;nbsp;role));

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UsernamePasswordAuthenticationToken&amp;nbsp;authentication&amp;nbsp;=
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new&amp;nbsp;UsernamePasswordAuthenticationToken(uid,&amp;nbsp;null,&amp;nbsp;authorities);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SecurityContextHolder.getContext().setAuthentication(authentication);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;同步双向续期
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.expire(redisKey,&amp;nbsp;30,&amp;nbsp;TimeUnit.MINUTES);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.expire(uidKey,&amp;nbsp;30,&amp;nbsp;TimeUnit.MINUTES);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;旧设备，直接从&amp;nbsp;Redis&amp;nbsp;删除
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.delete(redisKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;=====&amp;nbsp;单端核心校验结束&amp;nbsp;=====
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.logger.error(&amp;quot;解析用户安全上下文失败&amp;quot;,&amp;nbsp;e);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filterChain.doFilter(request,&amp;nbsp;response);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;TestController&lt;/h2&gt;&lt;p&gt;当用户调用登录接口并传入用户ID时，系统首先通过Mapper映射器校验该用户在数据库中是否存在，若不存在则直接返回错误提示阻断流程。
校验通过后，系统利用定义在工具类中的统一前缀配合用户ID拼接出唯一的UidKey，并立即去Redis中检索该键对应的值，若能获取到说明该账号此前已在其他设备登录。
此时系统会严格遵循先清道后建新凭证的顺序，立刻调用删除命令将该旧Token作为键的TokenKey从Redis内存中彻底抹除，以此确保旧设备的身份凭证死透，从根本上解决高并发下的数据覆盖Bug。
清除完旧通道后，系统利用UUID生成一个全新的Token串并组装出对应的新TokenKey，同时将最新的用户ID和角色信息格式化为JSON字符串，随后执行双向绑定写入，即把JSON字符串存入新TokenKey，把新Token串反向覆盖写入UidKey，并为这两个键设置完全一致的三十分钟逻辑过期时间，最后将新Token返回给前端。&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.redisauth.controller;


import&amp;nbsp;com.redisauth.entity.SUsers;
import&amp;nbsp;com.redisauth.mapper.SUsersMapper;
import&amp;nbsp;com.redisauth.util.RedisUtils;
import&amp;nbsp;org.springframework.beans.factory.annotation.Autowired;
import&amp;nbsp;org.springframework.util.StringUtils;
import&amp;nbsp;org.springframework.web.bind.annotation.GetMapping;
import&amp;nbsp;org.springframework.web.bind.annotation.PostMapping;
import&amp;nbsp;org.springframework.web.bind.annotation.RequestParam;
import&amp;nbsp;org.springframework.web.bind.annotation.RestController;

import&amp;nbsp;java.util.UUID;
import&amp;nbsp;java.util.concurrent.TimeUnit;

@RestController
public&amp;nbsp;class&amp;nbsp;TestController&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;SUsersMapper&amp;nbsp;sUsersMapper;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;RedisUtils&amp;nbsp;redisUtils;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@PostMapping(&amp;quot;/login&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;login(@RequestParam(&amp;quot;uid&amp;quot;)&amp;nbsp;int&amp;nbsp;uid)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SUsers&amp;nbsp;sUsers&amp;nbsp;=&amp;nbsp;sUsersMapper.selectByPrimaryKey(uid);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(sUsers&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;login&amp;nbsp;failed&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;uidKey&amp;nbsp;=&amp;nbsp;redisUtils.uidPreKey&amp;nbsp;+&amp;nbsp;sUsers.getUid();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;1.&amp;nbsp;严格先行：先检查并干掉旧设备，确保当前&amp;nbsp;Uid&amp;nbsp;相关的旧通道被完全清理
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;oldToken&amp;nbsp;=&amp;nbsp;redisUtils.get(uidKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(StringUtils.hasText(oldToken))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.delete(redisUtils.tokenPreKey&amp;nbsp;+&amp;nbsp;oldToken);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;2.&amp;nbsp;后行：旧设备死透了，再开始为新设备张罗新凭证
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;newToken&amp;nbsp;=&amp;nbsp;UUID.randomUUID().toString();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;tokenKey&amp;nbsp;=&amp;nbsp;redisUtils.tokenPreKey&amp;nbsp;+&amp;nbsp;newToken;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;jsonValue&amp;nbsp;=&amp;nbsp;String.format(&amp;quot;{\&amp;quot;uid\&amp;quot;:%d,\&amp;quot;role\&amp;quot;:\&amp;quot;%s\&amp;quot;}&amp;quot;,&amp;nbsp;sUsers.getUid(),&amp;nbsp;sUsers.getRole());

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;3.&amp;nbsp;双向绑定写入&amp;nbsp;Redis
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.set(tokenKey,&amp;nbsp;jsonValue,&amp;nbsp;30,&amp;nbsp;TimeUnit.MINUTES);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisUtils.set(uidKey,&amp;nbsp;newToken,&amp;nbsp;30,&amp;nbsp;TimeUnit.MINUTES);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;newToken;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&amp;quot;/hello&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;hello()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;hello&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&amp;quot;admin&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;admin()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;admin&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}


}&lt;/pre&gt;&lt;h2&gt;RedisUtils&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;redis相关做了封装，方便一点&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.redisauth.util;

import&amp;nbsp;org.springframework.data.redis.core.StringRedisTemplate;
import&amp;nbsp;org.springframework.stereotype.Component;

import&amp;nbsp;java.util.concurrent.TimeUnit;

/**
&amp;nbsp;*&amp;nbsp;Redis&amp;nbsp;基础操作通用工具类（基于&amp;nbsp;StringRedisTemplate）
&amp;nbsp;*/
@Component
public&amp;nbsp;class&amp;nbsp;RedisUtils&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;final&amp;nbsp;StringRedisTemplate&amp;nbsp;redisTemplate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;HEADER&amp;nbsp;=&amp;nbsp;&amp;quot;Authorization&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;uidPreKey&amp;nbsp;=&amp;nbsp;&amp;quot;RedisAuth:uid:&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;tokenPreKey&amp;nbsp;=&amp;nbsp;&amp;quot;RedisAuth:token:&amp;quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RedisUtils(StringRedisTemplate&amp;nbsp;redisTemplate)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.redisTemplate&amp;nbsp;=&amp;nbsp;redisTemplate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;写入缓存
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;set(String&amp;nbsp;key,&amp;nbsp;String&amp;nbsp;value)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.opsForValue().set(key,&amp;nbsp;value);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;写入缓存并设置过期时间
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;set(String&amp;nbsp;key,&amp;nbsp;String&amp;nbsp;value,&amp;nbsp;long&amp;nbsp;timeout,&amp;nbsp;TimeUnit&amp;nbsp;unit)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.opsForValue().set(key,&amp;nbsp;value,&amp;nbsp;timeout,&amp;nbsp;unit);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;读取缓存
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;get(String&amp;nbsp;key)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;redisTemplate.opsForValue().get(key);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;读取&amp;nbsp;Hash&amp;nbsp;指定字段值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;hGet(String&amp;nbsp;key,&amp;nbsp;String&amp;nbsp;field)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Object&amp;nbsp;value&amp;nbsp;=&amp;nbsp;redisTemplate.opsForHash().get(key,&amp;nbsp;field);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;value&amp;nbsp;!=&amp;nbsp;null&amp;nbsp;?&amp;nbsp;value.toString()&amp;nbsp;:&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;删除&amp;nbsp;Key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;Boolean&amp;nbsp;delete(String&amp;nbsp;key)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;redisTemplate.delete(key);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;设置/更新&amp;nbsp;Key&amp;nbsp;过期时间
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;Boolean&amp;nbsp;expire(String&amp;nbsp;key,&amp;nbsp;long&amp;nbsp;timeout,&amp;nbsp;TimeUnit&amp;nbsp;unit)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;redisTemplate.expire(key,&amp;nbsp;timeout,&amp;nbsp;unit);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;检查&amp;nbsp;Key&amp;nbsp;是否存在
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;Boolean&amp;nbsp;hasKey(String&amp;nbsp;key)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;redisTemplate.hasKey(key);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;主程序入口&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;目的是关闭spring security自带的登录，以及mapper扫描&lt;br/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.redisauth;

import&amp;nbsp;org.mybatis.spring.annotation.MapperScan;
import&amp;nbsp;org.springframework.boot.SpringApplication;
import&amp;nbsp;org.springframework.boot.autoconfigure.SpringBootApplication;
import&amp;nbsp;org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;

@SpringBootApplication(exclude&amp;nbsp;=&amp;nbsp;{UserDetailsServiceAutoConfiguration.class})
@MapperScan(&amp;quot;com.redisauth.mapper&amp;quot;)
public&amp;nbsp;class&amp;nbsp;RedisAuthApplication&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;void&amp;nbsp;main(String[]&amp;nbsp;args)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SpringApplication.run(RedisAuthApplication.class,&amp;nbsp;args);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

}&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Fri, 05 Jun 2026 22:59:33 +0800</pubDate></item><item><title>LSPosed和LSPatch插件开发教程</title><link>https://pan.anyuer.club/?id=293</link><description>&lt;h1&gt;简介&lt;/h1&gt;&lt;p&gt;其实他们都是基于xposed出现的框架，只是扩展了两个而已，所以他们的编程体系相同，但是LSPatch相比于LSPosed可以不需要root，但是其功能肯定不如LSPosed全面，同时呢，两个框架的运行模式也有不同&lt;/p&gt;&lt;h1&gt;LSPosed教程&lt;/h1&gt;&lt;h2&gt;创建项目&lt;br/&gt;&lt;/h2&gt;&lt;p&gt;打开Android studio&lt;/p&gt;&lt;p&gt;点New Project&amp;nbsp;&lt;/p&gt;&lt;p&gt;点Empty Views Activity&lt;/p&gt;&lt;p&gt;编程语言选java&amp;nbsp;&lt;/p&gt;&lt;p&gt;构建选build.gradle&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780499993725179.png&quot; alt=&quot;image.png&quot; width=&quot;561&quot; height=&quot;403&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;image.png&quot; style=&quot;width: 561px; height: 403px;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;插件模块配置&lt;/h2&gt;&lt;p&gt;打开AndroidManifest.xml添加如下配置&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--&amp;nbsp;是否为Xposed模块&amp;nbsp;--&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;meta-data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:name=&amp;quot;xposedmodule&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:value=&amp;quot;true&amp;quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--&amp;nbsp;模块的简介（在框架中显示）&amp;nbsp;--&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;meta-data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:name=&amp;quot;xposeddescription&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:value=&amp;quot;我是Xposed模块简介&amp;quot;&amp;nbsp;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--&amp;nbsp;模块最低支持的Api版本&amp;nbsp;一般填54即可&amp;nbsp;--&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;meta-data&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:name=&amp;quot;xposedminversion&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:value=&amp;quot;54&amp;quot;/&amp;gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780500429511652.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;另外提一嘴没有用的&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:icon=&amp;quot;@drawable/icon&amp;quot;&amp;nbsp;//换图标
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:label=&amp;quot;@string/app_name&amp;quot;&amp;nbsp;//换名字
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;android:roundIcon=&amp;quot;@drawable/icon&amp;quot;&amp;nbsp;//换图标&lt;/pre&gt;&lt;h2&gt;添加仓库配置&lt;/h2&gt;&lt;p&gt;打开setting.gradle添加&lt;br/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;maven&amp;nbsp;{&amp;nbsp;url=uri(&amp;quot;https://api.xposed.info&amp;quot;)&amp;nbsp;}&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780500549769578.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;h2&gt;导入jar包&lt;/h2&gt;&lt;p&gt;打开app module的build.gradle 添加&lt;br/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;compileOnly&amp;nbsp;&amp;#39;de.robv.android.xposed:api:82&amp;#39;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780500812187513.png&quot; alt=&quot;image.png&quot; width=&quot;729&quot; height=&quot;411&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;image.png&quot; style=&quot;width: 729px; height: 411px;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;创建xposed_init&lt;/h2&gt;&lt;p&gt;将左上角选择为project，右键main，选择新建一个Directory，然后选择assets&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780501207654525.png&quot; alt=&quot;image.png&quot; width=&quot;618&quot; height=&quot;316&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;image.png&quot; style=&quot;width: 618px; height: 316px;&quot;/&gt;&lt;/p&gt;&lt;p&gt;再在该目录下新建一个file，记住一定是file，没有后缀名文件名叫xposed_init&lt;/p&gt;&lt;p&gt;其内为如下内容，其中com.xxx.xxx是你的包名，如果你不知道，你可以打开MainActivity.java中第一行，package com.xxx.xxx，那个就是你的包名，MainHook是我们的xposed类名，稍等再创建这个类&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;com.xxx.xxx.MainHook&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780501440114456.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;h2&gt;Xposed Hook代码&lt;/h2&gt;&lt;p&gt;在和MainActivity相同的目录下新建MainHook.java，代码如图，第一行是你的包名，记住不要全部复制嗷&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780501794914604.png&quot; alt=&quot;image.png&quot; width=&quot;840&quot; height=&quot;331&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;image.png&quot; style=&quot;width: 840px; height: 331px;&quot;/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;package&amp;nbsp;com.yuer.study;//包名记得替换

import&amp;nbsp;de.robv.android.xposed.IXposedHookLoadPackage;
import&amp;nbsp;de.robv.android.xposed.XposedBridge;
import&amp;nbsp;de.robv.android.xposed.callbacks.XC_LoadPackage;

public&amp;nbsp;class&amp;nbsp;MainHook&amp;nbsp;implements&amp;nbsp;IXposedHookLoadPackage&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;handleLoadPackage(XC_LoadPackage.LoadPackageParam&amp;nbsp;lpparam)&amp;nbsp;throws&amp;nbsp;Throwable&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XposedBridge.log(&amp;quot;yuerlv&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;&amp;quot;启动&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;lpparam.packageName&amp;nbsp;+&amp;nbsp;&amp;quot;&amp;nbsp;成功咯&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

}&lt;/pre&gt;&lt;h2&gt;运行&lt;/h2&gt;&lt;p&gt;然后你打开LSPosed，勾选需要的App，再把该模块打开，看日志即可&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/06/202606031780502160116683.png&quot; alt=&quot;image.png&quot; width=&quot;714&quot; height=&quot;162&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;image.png&quot; style=&quot;width: 714px; height: 162px;&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Wed, 03 Jun 2026 23:12:21 +0800</pubDate></item><item><title>MySQL+Redis+RabbitMQ解决并发问题</title><link>https://pan.anyuer.club/?id=292</link><description>&lt;h1&gt;引言&lt;/h1&gt;&lt;p&gt;由于业务不方便展示，此处特定构造了一个新的业务场景。&lt;/p&gt;&lt;p&gt;假设每个用户有一个uid字段和hot字段，游客可以为uid为1的用户点赞，点赞一次数值加1，一个游客还好，那万一有很多很多呢&lt;/p&gt;&lt;p&gt;我写了一个demo，未进行任何并发处理的源码已经分享，方便大家参考&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;https://github.com/Anyuer9837/ConcurrencyStudy&lt;/pre&gt;&lt;p&gt;其中数据库建议云的，家用的笔记本电脑或者台式电脑，一般来说性能都不错，很难压力测试，或者说效果不明显， 所以我留下了可以使用的MySQL服务器进行在src/main/resources/application.properties 里面&lt;/p&gt;&lt;p&gt;同时希望大佬可以手下留情，我这为爱发电，希望大家别搞破坏，和平交流学习！共同进步！&lt;/p&gt;&lt;h1&gt;问题分析&lt;/h1&gt;&lt;p&gt;其实问题的原因很简单，当有大规模用户同时对MySQL的某一行数据的每一个字段进行写入的时候，会出现异常问题，&lt;br/&gt;&lt;/p&gt;&lt;h1&gt;解决思路&lt;/h1&gt;&lt;p&gt;针对这种高并发单点读写的瓶颈，整体的应对策略是利用分布式锁控制并发、缓存承载高频读写、消息队列异步削峰落库以及异常反向补偿的组合方案。&lt;/p&gt;&lt;p&gt;程序开始先获取十五秒的分布式锁。这样做是为了让并发请求针对该用户串行排队，防止高并发在缓存失效时同时击穿到数据库，也避免了多线程交错计算导致的数据覆盖。&lt;/p&gt;&lt;p&gt;拿到锁后优先读取缓存，有数据则直接使用，无数据则去数据库查询基数并回填。优先读缓存利用了内存的高性能，缓存缺失时去数据库查询并在锁的保护下回填，确保了冷启动时只有一个线程访问数据库，绝对安全。随后程序在内存中将热度加一，并立刻写回缓存，让前端能毫秒级感知最新状态。&lt;/p&gt;&lt;p&gt;缓存更新后，程序将数据打包发送给消息队列，发送成功即对前端返回成功。这种设计不等待慢速的数据库事务提交，利用队列作为蓄水池平滑落库，切断了高并发流量对底层数据库的直接冲击，实现了流量削峰。&lt;/p&gt;&lt;p&gt;如果发送队列失败，程序会立刻捕获异常并执行缓存的原子递减，把刚才多加的数值原路减回去。这种反向补偿机制是为了在分布式系统遭遇局部网络故障时，及时撤销内存变更，保证缓存和数据库的数据最终一致性。&lt;/p&gt;&lt;p&gt;最后无论流程顺利还是抛出异常，程序都会在清理块中判断锁是否仍由当前线程持有，确认无误后再执行释放。这样可以防止当前线程因为自身卡顿导致锁超时后，去错误地释放正在保护其他线程的锁，完成了安全的生命周期闭环。&lt;/p&gt;&lt;h1&gt;核心代码&lt;/h1&gt;&lt;p&gt;代码已经贴到上方仓库的1.0分支，这里展示的是核心代码&lt;br/&gt;&lt;/p&gt;&lt;h2&gt;查询逻辑+业务逻辑+发送消息到队列&lt;/h2&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;boolean&amp;nbsp;updateHot(UserHot&amp;nbsp;userHot)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;cacheKey&amp;nbsp;=&amp;nbsp;&amp;quot;user:hot:&amp;quot;&amp;nbsp;+&amp;nbsp;userHot.getUid();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;lockKey&amp;nbsp;=&amp;nbsp;&amp;quot;lock:user-hot:&amp;quot;&amp;nbsp;+&amp;nbsp;userHot.getUid();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RLock&amp;nbsp;lock&amp;nbsp;=&amp;nbsp;redissonClient.getLock(lockKey);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;1.&amp;nbsp;尝试获取分布式锁，限时&amp;nbsp;5&amp;nbsp;秒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(lock.tryLock(5,&amp;nbsp;TimeUnit.SECONDS))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;newHot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;2.&amp;nbsp;隔离并发下的&amp;nbsp;Redis&amp;nbsp;操作
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;cachedHot&amp;nbsp;=&amp;nbsp;redisTemplate.opsForValue().get(cacheKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;currentHot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(cachedHot&amp;nbsp;!=&amp;nbsp;null)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentHot&amp;nbsp;=&amp;nbsp;Integer.parseInt(cachedHot);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UserHot&amp;nbsp;dbData&amp;nbsp;=&amp;nbsp;userHotMapper.selectByPrimaryKey(userHot.getUid());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(dbData&amp;nbsp;==&amp;nbsp;null)&amp;nbsp;return&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentHot&amp;nbsp;=&amp;nbsp;dbData.getHot();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.opsForValue().set(cacheKey,&amp;nbsp;String.valueOf(currentHot));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;内存计算并回写&amp;nbsp;Redis
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;newHot&amp;nbsp;=&amp;nbsp;currentHot&amp;nbsp;+&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.opsForValue().set(cacheKey,&amp;nbsp;String.valueOf(newHot));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.err.println(&amp;quot;【❌&amp;nbsp;Redis故障】操作Redis失败，原因:&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;e.getMessage());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;3.&amp;nbsp;隔离&amp;nbsp;MQ&amp;nbsp;消息发送
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;message&amp;nbsp;=&amp;nbsp;userHot.getUid()&amp;nbsp;+&amp;nbsp;&amp;quot;,&amp;quot;&amp;nbsp;+&amp;nbsp;newHot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rabbitTemplate.convertAndSend(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RabbitConfig.HOT_EXCHANGE,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RabbitConfig.HOT_ROUTING_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;message
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.err.println(&amp;quot;【MQ故障】消息发送到RabbitMQ失败！原因:&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;e.getMessage());

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;💡&amp;nbsp;开始执行回滚逻辑
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&amp;quot;【触发回滚】正在将&amp;nbsp;Redis&amp;nbsp;数据恢复原状...&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;使用&amp;nbsp;decrement&amp;nbsp;原子操作，把刚才加的&amp;nbsp;1&amp;nbsp;减回去
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.opsForValue().decrement(cacheKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&amp;quot;【回滚成功】Redis&amp;nbsp;数据已撤销，保证了数据一致性。&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;rollbackEx)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;🚨&amp;nbsp;极端边缘场景：刚才写&amp;nbsp;Redis&amp;nbsp;还好好的，回滚的时候&amp;nbsp;Redis&amp;nbsp;突然宕机或断网了！
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.err.println(&amp;quot;【致命灾难】MQ发送失败，且&amp;nbsp;Redis&amp;nbsp;回滚也失败！出现严重数据不一致！&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.err.println(&amp;quot;急需人工介入！出问题的&amp;nbsp;UID:&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;userHot.getUid()&amp;nbsp;+&amp;nbsp;&amp;quot;，报错:&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;rollbackEx.getMessage());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;真实生产环境中，这里通常会打印&amp;nbsp;Error&amp;nbsp;级别日志触发钉钉/邮件报警，
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;或者把这条失败记录写到一个本地磁盘日志文件里，等重启后人工修数据。
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&amp;quot;【锁竞争激烈】获取分布式锁超时，其他线程正占用锁，本次更新放弃。&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(InterruptedException&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&amp;quot;【线程中断】等待锁的过程中线程被中断:&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;e.getMessage());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Thread.currentThread().interrupt();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;finally&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;严格释放锁
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(lock.isHeldByCurrentThread())&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lock.unlock();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/pre&gt;&lt;h2&gt;消费者&lt;/h2&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@RabbitListener(queues&amp;nbsp;=&amp;nbsp;RabbitConfig.HOT_QUEUE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;receiveUserHotMessage(String&amp;nbsp;message)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;1.&amp;nbsp;解析消息（把&amp;nbsp;&amp;quot;1,11&amp;quot;&amp;nbsp;拆成&amp;nbsp;uid=1,&amp;nbsp;hot=11）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String[]&amp;nbsp;parts&amp;nbsp;=&amp;nbsp;message.split(&amp;quot;,&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Integer&amp;nbsp;uid&amp;nbsp;=&amp;nbsp;Integer.parseInt(parts[0]);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Integer&amp;nbsp;newHot&amp;nbsp;=&amp;nbsp;Integer.parseInt(parts[1]);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UserHot&amp;nbsp;updateData&amp;nbsp;=&amp;nbsp;new&amp;nbsp;UserHot();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;updateData.setUid(uid);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;updateData.setHot(newHot);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userHotMapper.updateByPrimaryKeySelective(updateData);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;System.out.println(&amp;quot;【MQ成功落盘】用户&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;uid&amp;nbsp;+&amp;nbsp;&amp;quot;&amp;nbsp;的热度已同步为&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;newHot);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.err.println(&amp;quot;【MQ消费失败】&amp;quot;&amp;nbsp;+&amp;nbsp;e.getMessage());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;注意：这里如果抛出异常，RabbitMQ&amp;nbsp;会自动把这条消息重新放回队列头部，实现自动重试！
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;e;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Mon, 01 Jun 2026 13:56:22 +0800</pubDate></item><item><title>安卓设备翻墙后将代理分享给局域网其他设备</title><link>https://pan.anyuer.club/?id=291</link><description>&lt;p&gt;找到你的翻墙软件，打开设置，允许来自局域网的连接打开。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780203607697748.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后，其它设备找到wifi，代理改为手动，输入翻墙的局域网设备ip和端口&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780203509283365.jpg&quot; alt=&quot;Screenshot_2026-05-31-12-57-47-943_com.android.settings.jpg&quot; width=&quot;720&quot; height=&quot;1600&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;Screenshot_2026-05-31-12-57-47-943_com.android.settings.jpg&quot; style=&quot;width: 720px; height: 1600px;&quot;/&gt;&lt;/p&gt;</description><pubDate>Sun, 31 May 2026 12:56:16 +0800</pubDate></item><item><title>Cloudflare Tunnel 局域网服务变到公网</title><link>https://pan.anyuer.club/?id=290</link><description>&lt;p&gt;这一招真的是太屌了，之前还要用各种内网穿透的软件，什么花生壳啊之类的，等等，就算公开了，域名也不能自行绑定，但是Cloudflare Tunnel 打通了这个通道，而且过程非常方便，现在教大家跑通这个流程。&lt;/p&gt;&lt;h1&gt;Windows&lt;br/&gt;&lt;/h1&gt;&lt;p&gt;打开cloudflare官网&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;https://dash.cloudflare.com/&lt;/pre&gt;&lt;p&gt;创建隧道&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780204684838747.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;输入一个隧道名称&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780204785829748.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后，下载对应的配置工具&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780204831162907.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;下载完成后，双击运行。记得以管理员权限打开&lt;/p&gt;&lt;p&gt;然后输入命令，命令必须复制cloudflare发给你的&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780204971739830.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780205336114862.png&quot; alt=&quot;屏幕截图 2026-05-31 132839.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后你就会发现成功了，点击创建就可以了&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780205360501243.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后就是创建路由了，点击刚才的隧道，进去后点击添加路由&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780205412126740.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后选择本地已经开启的服务&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780205432391466.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后选择你在cloudflare绑定的域名，再加一个二级域名，再输入本地的服务，最后点击添加路由，就完事了&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605311780205487392559.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后就可以通过域名进行访问了。&lt;/p&gt;&lt;p&gt;但是现在目前来看，一个设备只能一个cloudflare服务，如果你要把当前的设备进行更换隧道，那么就必须卸载掉原有隧道才行&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;cloudflared&amp;nbsp;service&amp;nbsp;uninstall&lt;/pre&gt;&lt;h1&gt;Android&lt;/h1&gt;&lt;p&gt;我的发，太伟大了，这种服务也可以让termux部署，先输入如下命令&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;apt&amp;nbsp;install&amp;nbsp;cloudflared&lt;/pre&gt;&lt;p&gt;然后其他流程一模一样。。。。。。。&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;ALTER&amp;nbsp;USER&amp;nbsp;&amp;#39;root&amp;#39;@&amp;#39;localhost&amp;#39;&amp;nbsp;IDENTIFIED&amp;nbsp;BY&amp;nbsp;&amp;#39;你的新密码&amp;#39;;
FLUSH&amp;nbsp;PRIVILEGES;&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Sat, 30 May 2026 22:55:15 +0800</pubDate></item><item><title>MyBatisCodeHelper免费版</title><link>https://pan.anyuer.club/?id=289</link><description>&lt;p&gt;MyBatisCodeHelper好用是好用，就是不免费这太难受了，整理了一下大佬破解的免费的版本&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-basic&quot;&gt;通过网盘分享的文件：MyBatisCodeHelper-Pro-3.4.4_2321-2023.2-2025.1obfuscated.zip
链接:&amp;nbsp;https://pan.baidu.com/s/1Jccn2qWRoryGgBSHxGzLeQ?pwd=yuer&amp;nbsp;提取码:&amp;nbsp;yuer&amp;nbsp;
--来自百度网盘超级会员v4的分享&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Fri, 29 May 2026 16:01:57 +0800</pubDate></item><item><title>去掉Ai生成的ppt里面的水印</title><link>https://pan.anyuer.club/?id=288</link><description>&lt;h1&gt;Ai网址是&lt;/h1&gt;&lt;pre class=&quot;prism-highlight prism-language-bash&quot;&gt;https://gamma.app/&lt;/pre&gt;&lt;h1&gt;原理介绍&lt;/h1&gt;&lt;p&gt;ppt、apk等文件其实都是能通过特定方式进行解压的，原因很简单，那么多图片文字等等资源都在一个文件格式里面，肯定是要把文件塞进去才行的，而我门的ppt或者pptx，你可以直接修改为zip，然后进行解压就好了。大原理是这样，而Ai的生成的ppt大部分水印其实是一张图片进行叠加的，解压了之后，进去替换掉对应的水印文件就好了&lt;/p&gt;&lt;h1&gt;具体教程&lt;/h1&gt;&lt;p&gt;先将文件后缀名修改为zip，再进行解压，然后打开如下目录&lt;br/&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-bash&quot;&gt;\ppt\media&lt;/pre&gt;&lt;p&gt;查看每张图片，找到水印的图片，比如gamma的水印图片就是image2.png&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605211779349458384722.png&quot; alt=&quot;image2.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;然后随便利用一个空白图片进行替换，就比如我下面这个&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://pan.anyuer.club/zb_users/upload/2026/05/202605211779349572737518.png&quot; alt=&quot;transparent.png&quot; width=&quot;100&quot; height=&quot;100&quot; border=&quot;0&quot; vspace=&quot;0&quot; title=&quot;transparent.png&quot; style=&quot;width: 100px; height: 100px;&quot;/&gt;&lt;/p&gt;&lt;p&gt;额，它真的有，但是由于是空白的，所以你看不到，如果你看得到的话，到时候替换过去的图片你也就能看到了，所以肯定是不好的，这里提供下载链接&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-bash&quot;&gt;https://pan.anyuer.club/zb_users/upload/2026/05/202605211779349572737518.png&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Thu, 21 May 2026 15:38:48 +0800</pubDate></item></channel></rss>