-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 162 KB
/
content.json
1
{"meta":{"title":"人生当苦, 良人当归","subtitle":"","description":"我不过像你像他像那野草野花。","author":"有李说不清","url":"https://fairyeye.github.io","root":"/"},"pages":[{"title":"","date":"2023-06-05T01:28:10.943Z","updated":"2023-06-05T01:28:10.943Z","comments":true,"path":"404.html","permalink":"https://fairyeye.github.io/404.html","excerpt":"","text":""},{"title":"","date":"2023-06-05T01:28:10.943Z","updated":"2023-06-05T01:28:10.943Z","comments":true,"path":"404/404.html","permalink":"https://fairyeye.github.io/404/404.html","excerpt":"","text":""},{"title":"category","date":"2020-04-27T06:30:47.000Z","updated":"2023-06-05T01:28:10.959Z","comments":true,"path":"categories/index.html","permalink":"https://fairyeye.github.io/categories/index.html","excerpt":"","text":""},{"title":"","date":"2023-06-05T01:28:10.959Z","updated":"2023-06-05T01:28:10.959Z","comments":true,"path":"about/index.html","permalink":"https://fairyeye.github.io/about/index.html","excerpt":"","text":"This is About."},{"title":"all","date":"2020-04-27T06:26:32.000Z","updated":"2023-06-05T05:49:50.607Z","comments":true,"path":"tags/index.html","permalink":"https://fairyeye.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"say something","slug":"N N","date":"2024-07-09T03:09:01.791Z","updated":"2024-10-09T02:00:47.753Z","comments":true,"path":"2024/07/09/N N/","link":"","permalink":"https://fairyeye.github.io/2024/07/09/N%20N/","excerpt":"","text":"# A 延迟队列?15 天 并且可以重置 提醒续命 (记录) 通知 => +1 天 通知 + 短信 => +2 天 发给紧急联系人() 可以关闭掉 如果恢复,打上标记 或者删掉这条记录 --- 打上标记 1[{"type":"text","value":"222","timestamp":"2024-10-08 16:22:11.421","synced":true},{"type":"text","value":"1111","timestamp":"2024-10-08 16:21:50.566","synced":true}] # B","categories":[],"tags":[]},{"title":"Arch Linux安装","slug":"Arch Linux","date":"2024-07-03T13:04:21.000Z","updated":"2024-07-05T09:31:23.238Z","comments":true,"path":"2024/07/03/Arch Linux/","link":"","permalink":"https://fairyeye.github.io/2024/07/03/Arch%20Linux/","excerpt":"","text":"很详细的链接 # 制作 U 盘 使用 Rufus 下载链接 v4.5 # 使用 archInstall 安装 12345archinstall# 设置一下即可# 必选:设置硬盘、root密码 网络!! 选第二个# 可选:时区 # 启用 SSH 如果你安装完系统后无法通过 SSH 远程登录,可能是由于 SSH 服务未安装、未启动或防火墙配置问题。以下是一些可能的解决方法: # 1. 检查并安装 OpenSSH 确保 OpenSSH 已安装: 安装 sshd sudo pacman -S openssh # 2. 启动并启用 SSH 服务 启动 SSH 服务: sudo systemctl start sshd 启用 SSH 服务,使其在系统启动时自动运行: sudo systemctl enable sshd # 3. 检查防火墙设置 如果你启用了防火墙,需要确保允许 SSH 端口(默认端口 22)的连接。例如,如果你使用 ufw 作为防火墙,可以使用以下命令: sudo ufw allow ssh 如果使用 iptables ,可以使用以下命令: sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 4. 检查 SSH 配置文件 检查 SSH 配置文件 /etc/ssh/sshd_config 是否正确配置。例如,确保以下行没有被注释掉(去掉前面的 # ): Port 22 PermitRootLogin yes # 如果你需要以 root 登录 PasswordAuthentication yes # 如果你使用密码登录 编辑完配置文件后,重新启动 SSH 服务以应用更改: sudo systemctl restart sshd # 5. 检查网络连接 确保你的计算机在网络中是可访问的,可以通过以下命令查看 IP 地址: ip addr show 确保你使用的是正确的 IP 地址和端口连接。 # 6. 检查 SSH 客户端输出 如果仍然无法连接,使用 SSH 客户端连接时查看详细输出,以获取更多调试信息。例如,在 Linux 或 macOS 上,可以使用以下命令: ssh -v user@hostname 这个命令会显示详细的连接过程,有助于找出问题所在。 # 7. 确保 SSH 服务在运行 使用以下命令检查 SSH 服务状态: sudo systemctl status sshd 确保显示的状态为 active (running) 。如果不是,请检查日志文件以获取更多信息: sudo journalctl -u sshd # 8. 确保主机名解析正常 确保你连接的主机名可以正确解析。如果你使用主机名连接,尝试改用 IP 地址连接,以排除 DNS 解析问题。 # 9. 确保网络没有阻塞 SSH 端口 某些网络环境(如企业网络或公共 WiFi)可能会阻止 SSH 端口的流量。尝试在不同的网络环境中进行连接测试。 # 10. 检查用户权限 确保你使用的用户在目标机器上存在,并且具有适当的权限。你可以使用以下命令查看当前用户列表: cat /etc/passwd 确保用户存在并且可以登录。 # 启用并启动显示管理器 假设你选择了 GDM 作为显示管理器: 12sudo systemctl enable gdmsudo systemctl start gdm 如果选择了 SDDM: 12sudo systemctl enable sddmsudo systemctl start sddm 安装缺失的软件包: 1234sudo pacman -S gnome # 安装 GNOMEsudo pacman -S plasma # 安装 KDE Plasmasudo pacman -S gdm # 安装 GDMsudo pacman -S sddm # 安装 SDDM 到这里 系统就算安装完成了 # 安装 Hyprland 报错中 需要创建一个非 root 用户 给 sudo 权限 需要安装编辑器 vim 或者 nano","categories":[{"name":"杂项","slug":"杂项","permalink":"https://fairyeye.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[]},{"title":"开发日志","slug":"开发日志","date":"2024-06-13T08:42:20.913Z","updated":"2024-06-13T08:54:42.462Z","comments":true,"path":"2024/06/13/开发日志/","link":"","permalink":"https://fairyeye.github.io/2024/06/13/%E5%BC%80%E5%8F%91%E6%97%A5%E5%BF%97/","excerpt":"","text":"# 备份部分代码 MainActivity.java 实用源生安卓来处理模糊,但是还是慢 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869package com.li.ying; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import android.renderscript.*; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "image_processing"; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { super.configureFlutterEngine(flutterEngine); new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler( (call, result) -> { if (call.method.equals("blurImage")) { String imagePath = call.argument("imagePath"); int radius = call.argument("radius"); try { result.success(blurImage(imagePath, radius)); } catch (IOException e) { throw new RuntimeException(e); } } else { result.notImplemented(); } } ); } private String blurImage(String imagePath, int radius) throws IOException { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); RenderScript rs = RenderScript.create(this); Allocation input = Allocation.createFromBitmap(rs, bitmap); Allocation output = Allocation.createTyped(rs, input.getType()); ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 多次应用高斯模糊 for (int i = 0; i < 5; i++) { script.setRadius(radius); script.setInput(input); script.forEach(output); output.copyTo(bitmap); input.copyFrom(bitmap); } rs.destroy(); File outputDir = getCacheDir(); File outputFile = File.createTempFile("blurred_", ".png", outputDir); FileOutputStream out = new FileOutputStream(outputFile); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); out.close(); return outputFile.getAbsolutePath(); } } main.dart 调用源生安卓 12345678910111213141516171819// static const platform = MethodChannel('image_processing'); // // 这种方法 调用了安卓原生代码 速度更快 但是快不过缩小图片模糊 // 并且radius最大25 模糊效果不尽人意 // File? _blurredImage; // // Future<void> _blurImage(String imagePath) async { // try { // final String blurredImagePath = await platform.invokeMethod( // 'blurImage', // {'imagePath': imagePath, 'radius': 25}, // ); // setState(() { // _blurredImage = File(blurredImagePath); // }); // } on PlatformException catch (e) { // print("Failed to blur image: '${e.message}'."); // } // }","categories":[],"tags":[]},{"title":"苏州一日游","slug":"苏州一日游","date":"2024-06-08T13:52:56.000Z","updated":"2024-06-08T16:00:11.512Z","comments":true,"path":"2024/06/08/苏州一日游/","link":"","permalink":"https://fairyeye.github.io/2024/06/08/%E8%8B%8F%E5%B7%9E%E4%B8%80%E6%97%A5%E6%B8%B8/","excerpt":"","text":"# 洞泾 - 上海松江站 - 苏州站 (7.20-8.20 02 车) 21 分钟(5.1 公里)- 8.50 左右 # 平江路 吃早点,逛一逛 (1.5h - 9.30 左右) 从南 - > 北 # 苏香记 # 梅月轩 # 苏州打卡墙 # 桃花源记 # 通利桥 # 胡厢使桥 # 猫咖狗咖 # 西园寺 交通:最好打车 素面 # 虎丘 # 东方之门 # 七里山塘景区 夜景 # 蟹皇捞・江南蟹黄面 # 苏州站 - 上海站 - 地铁 (21.15-22.22)","categories":[],"tags":[{"name":"旅游","slug":"旅游","permalink":"https://fairyeye.github.io/tags/%E6%97%85%E6%B8%B8/"}]},{"title":"hy2.0","slug":"HY2.0","date":"2024-05-28T12:12:46.003Z","updated":"2024-11-12T10:15:03.794Z","comments":true,"path":"2024/05/28/HY2.0/","link":"","permalink":"https://fairyeye.github.io/2024/05/28/HY2.0/","excerpt":"","text":"# Flask Python 服务器部署 # 1. 准备服务器 首先,你需要一个可以运行 Python 的服务器。你可以使用以下服务: 云服务提供商:如 AWS、阿里云、DigitalOcean 等。 VPS 提供商:如 Linode、Vultr 等。 确保服务器上安装了 Python 和 pip。 # 2. 连接到服务器 使用 SSH 连接到你的服务器: ssh username@your_server_ip # 3. 安装依赖 在服务器上,确保你已经安装了 Python 和 pip。你可以使用以下命令检查: python3 --version pip3 --version 如果没有安装,可以使用以下命令安装: sudo apt update sudo apt install python3 python3-pip # 4. 上传代码 将你的 Flask 应用代码上传到服务器。可以使用 SCP、FTP 或 Git 来上传文件。下面是使用 SCP 的示例: scp -r /path/to/your/project username@your_server_ip:/path/to/target/directory # 5. 创建虚拟环境 在你的项目目录中创建一个虚拟环境: cd /path/to/target/directory python3 -m venv venv source venv/bin/activate # 6. 安装项目依赖 在虚拟环境中安装你的项目依赖: pip install -r requirements.txt # 7. 运行 Flask 应用 在开发环境中,你可以使用以下命令运行 Flask 应用: python app.py 但是在生产环境中,最好使用 WSGI 服务器(如 Gunicorn)来运行 Flask 应用: pip install gunicorn gunicorn -w 4 app:app --bind 0.0.0.0:5000 这里 -w 4 表示使用 4 个工作进程,你可以根据需要调整。 # 8. 配置反向代理(可选) 为了在 80 或 443 端口上提供服务,建议使用 Nginx 作为反向代理: # 安装 Nginx sudo apt install nginx # 配置 Nginx 创建一个新的 Nginx 配置文件,例如 /etc/nginx/sites-available/my_flask_app : nginx 复制代码 server { listen 80; server_name your_server_ip; # 或者你的域名 location / { proxy_pass http://127.0.0.1:5000; # Flask 应用的地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 创建符号链接到 sites-enabled : sudo ln -s /etc/nginx/sites-available/my_flask_app /etc/nginx/sites-enabled 测试 Nginx 配置并重启服务: sudo nginx -t sudo systemctl restart nginx # 9. 配置防火墙(可选) 确保服务器的防火墙允许 HTTP 和 HTTPS 流量: sudo ufw allow 'Nginx Full' # 10. 访问你的应用 现在,你应该能够通过浏览器访问你的 Flask 应用,使用服务器的 IP 地址或域名。 # 额外建议 使用 HTTPS:在生产环境中,强烈建议使用 HTTPS。可以使用 Let’s Encrypt 来免费申请 SSL 证书。 监控和日志:考虑使用工具来监控应用的性能和日志,以便及时发现问题。 # python 好的,这里是一个详细的 Python 方案,包括登录页面、选择文件夹、处理 PDF 文件并将数据存储到 SQLite 数据库,以及在线更新功能。 # 1. 环境配置和库安装 1pip install PyInstaller PyUpdater PyPDF2 pdfminer.six pymupdf tkinter requests # 2. 登录页面和主程序逻辑 # 2.1. 创建登录界面 创建一个文件 login.py 用于实现登录界面: 123456789101112131415161718192021222324252627282930313233343536373839404142import tkinter as tkfrom tkinter import messageboximport requestsclass LoginApp: def __init__(self, root): self.root = root self.root.title("登录") self.root.geometry("300x150") self.username_label = tk.Label(root, text="用户名") self.username_label.pack() self.username_entry = tk.Entry(root) self.username_entry.pack() self.password_label = tk.Label(root, text="密码") self.password_label.pack() self.password_entry = tk.Entry(root, show="*") self.password_entry.pack() self.login_button = tk.Button(root, text="登录", command=self.login) self.login_button.pack() def login(self): username = self.username_entry.get() password = self.password_entry.get() # 假设存在一个登录接口 login_url = "http://example.com/api/login" response = requests.post(login_url, json={"username": username, "password": password}) if response.status_code == 200 and response.json().get("status") == "success": messagebox.showinfo("成功", "登录成功") self.root.destroy() import main main.run_app() else: messagebox.showerror("错误", "用户名或密码错误")def run_login_app(): root = tk.Tk() app = LoginApp(root) root.mainloop() # 2.2. 主程序逻辑 创建 main.py 实现主程序逻辑: 123456789101112131415161718192021222324252627282930313233343536373839404142434445import osimport fitz # PyMuPDFimport sqlite3import tkinter as tkfrom tkinter import filedialog, messageboxdef select_folder(): folder_path = filedialog.askdirectory(title="选择包含PDF文件的文件夹") return folder_pathdef extract_text_from_pdf(pdf_path): doc = fitz.open(pdf_path) text = "" for page in doc: text += page.get_text() return textdef store_data_to_db(data): conn = sqlite3.connect('extracted_data.db') cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS pdf_data (id INTEGER PRIMARY KEY, file_name TEXT, content TEXT)''') cursor.execute('INSERT INTO pdf_data (file_name, content) VALUES (?, ?)', data) conn.commit() conn.close()def process_pdfs(folder_path): for file_name in os.listdir(folder_path): if file_name.endswith('.pdf'): pdf_path = os.path.join(folder_path, file_name) text = extract_text_from_pdf(pdf_path) store_data_to_db((file_name, text)) messagebox.showinfo("处理完成", f"文件 {file_name} 处理完成")def run_app(): root = tk.Tk() root.withdraw() folder_path = select_folder() if folder_path: process_pdfs(folder_path) else: messagebox.showinfo("提示", "没有选择文件夹")if __name__ == "__main__": run_login_app() # 2.3. 在线更新功能 创建 client_config.py 配置 PyUpdater: 12345678910111213141516171819202122import osfrom pyupdater.client import ClientAPP_NAME = "PDFExtractorApp"APP_VERSION = "0.1.0"UPDATE_URLS = ['http://your-update-server.com/']class ClientConfig: PUBLIC_KEY = "your-public-key" APP_NAME = APP_NAME COMPANY_NAME = "YourCompany" UPDATE_URLS = UPDATE_URLS MAX_DOWNLOAD_RETRIES = 3 USE_PATCHES = Truedef check_for_updates(): client = Client(ClientConfig(), refresh=True) app_update = client.update_check(ClientConfig.APP_NAME, ClientConfig.APP_VERSION) if app_update is not None: app_update.download() if app_update.is_downloaded(): app_update.extract_restart() 在 main.py 的 run_app 函数中加入更新检查: 1234567891011def run_app(): from client_config import check_for_updates check_for_updates() root = tk.Tk() root.withdraw() folder_path = select_folder() if folder_path: process_pdfs(folder_path) else: messagebox.showinfo("提示", "没有选择文件夹") # 2.4. 打包应用程序 创建一个 spec 文件来配置打包设置( your_script.spec ): 123456789101112131415161718192021222324252627282930313233343536373839404142# your_script.spec# -*- mode: python ; coding: utf-8 -*-block_cipher = Nonea = Analysis(['main.py'], pathex=[], binaries=[], datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False)pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)exe = EXE(pyz, a.scripts, [], exclude_binaries=True, name='PDFExtractorApp', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=False)coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='PDFExtractorApp') 然后使用 pyinstaller 打包: 1pyinstaller your_script.spec # 3. 配置和初始化 PyUpdater 123456pyupdater initpyupdater pkg --processpyupdater pkg --signpyupdater pkg --uploadpyupdater keys --createpyupdater settings # 4. 部署和测试 将生成的可执行文件和更新配置上传到你的服务器。然后运行打包好的应用程序,确保登录功能、PDF 处理功能和在线更新功能都正常工作。 这样,你就有了一个带有登录功能的 Windows 应用程序,可以选择文件夹中的 PDF 文件进行处理,并支持在线更新。 # 要读取多个 Excel 文件并两两判断它们之间是否存在高度重复的数据,可以按以下步骤进行 : 读取所有 Excel 文件。 提取并整理数据。 比较每对 Excel 文件的数据。 计算重复率。 输出比较结果。 可以使用 pandas 库来处理 Excel 文件,利用 pandas 的 DataFrame 结构进行数据比较和重复率计算。 # 1. 安装必要的库 首先,确保你已经安装了 pandas 和 openpyxl 库: bash 复制代码 pip install pandas openpyxl # 2. 读取所有 Excel 文件 使用 pandas 读取 Excel 文件,并提取数据。 python 复制代码 import os import pandas as pd def read_excel_files(folder_path): excel_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith('.xlsx')] dataframes = {f: pd.read_excel(f) for f in excel_files} return dataframes folder_path = 'your_folder_with_excel_files' dataframes = read_excel_files(folder_path) # 3. 比较每对 Excel 文件的数据 定义一个函数来比较两个 DataFrame,计算重复率。 python 复制代码 def calculate_similarity(df1, df2): # 确保两者都有相同的列 common_columns = df1.columns.intersection(df2.columns) if len(common_columns) == 0: return 0.0 # 选择共同的列进行比较 df1_common = df1[common_columns] df2_common = df2[common_columns] # 合并两个数据集,保留重复项 combined = pd.concat([df1_common, df2_common]) duplicates = combined.duplicated(keep=False) # 计算重复率 similarity = duplicates.sum() / len(combined) return similarity def compare_excel_files(dataframes): results = [] files = list(dataframes.keys()) for i in range(len(files)): for j in range(i + 1, len(files)): df1 = dataframes[files[i]] df2 = dataframes[files[j]] similarity = calculate_similarity(df1, df2) results.append((files[i], files[j], similarity)) return results similarity_results = compare_excel_files(dataframes) # 4. 输出比较结果 将比较结果输出,显示每对 Excel 文件的相似度。 python 复制代码 for file1, file2, similarity in similarity_results: print(f"文件 {file1} 和文件 {file2} 的相似度为: {similarity:.2%}") # 完整代码 python 复制代码 import os import pandas as pd def read_excel_files(folder_path): excel_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith('.xlsx')] dataframes = {f: pd.read_excel(f) for f in excel_files} return dataframes def calculate_similarity(df1, df2): common_columns = df1.columns.intersection(df2.columns) if len(common_columns) == 0: return 0.0 df1_common = df1[common_columns] df2_common = df2[common_columns] combined = pd.concat([df1_common, df2_common]) duplicates = combined.duplicated(keep=False) similarity = duplicates.sum() / len(combined) return similarity def compare_excel_files(dataframes): results = [] files = list(dataframes.keys()) for i in range(len(files)): for j in range(i + 1, len(files)): df1 = dataframes[files[i]] df2 = dataframes[files[j]] similarity = calculate_similarity(df1, df2) results.append((files[i], files[j], similarity)) return results folder_path = 'your_folder_with_excel_files' dataframes = read_excel_files(folder_path) similarity_results = compare_excel_files(dataframes) for file1, file2, similarity in similarity_results: print(f"文件 {file1} 和文件 {file2} 的相似度为: {similarity:.2%}") # 注意事项 列对齐:确保所有比较的数据具有相同的列。 数据清理:在实际使用中,可能需要对数据进行清理和预处理,以确保比较的准确性。 优化:对于大型数据集,计算重复率可能会很耗时。可以考虑优化算法或使用更高效的数据结构。 通过这些步骤,你可以实现读取多个 Excel 文件并两两判断是否有高度重复的数据,并计算和输出相似度。","categories":[],"tags":[]},{"title":"拉勾训练营5期","slug":"AA","date":"2024-03-20T08:15:40.000Z","updated":"2024-04-10T09:14:21.647Z","comments":true,"path":"2024/03/20/AA/","link":"","permalink":"https://fairyeye.github.io/2024/03/20/AA/","excerpt":"","text":"# JDBC 需要手动引入 Mysql 的 jar 包 123456789101112131415161718192021public class JDBCDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 建立连接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "12345678"); // 定义SQL语句 String sql = "select * from db_account where id = 4"; // 获取预处理prepareStatement PreparedStatement preparedStatement = connection.prepareStatement(sql); // 设置参数 // preparedStatement.setInt(1, 4); // 执行查询 得到结果 ResultSet resultSet = preparedStatement.executeQuery(sql); // 处理结果 while (resultSet.next()) { System.out.println("id:" + resultSet.getInt("id")); System.out.println("email:" + resultSet.getString("email")); } } } 为什么要有 ORM 框架 驱动 uri、数据库地址、账号密码,硬编码,不灵活 重复的建立连接 处理结果集麻烦 # 自定义 # 创建两个工程 IPersistence、IPersistence_Test # IPersistence_Test 使用端 # IPersistence 自定义框架 # 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中 1Resources.getResourceAsStream(String path) 获得 sqlSession 对象 sqlSession 通过 sqlSessionFatory.open 获得 sqlSessionFatory 通过 sqlSessionFatoryBuilder.build (configuration) 获得 build 需要获取数据库信息 创建 SqlSessionFactoryBuilder 通过 SqlSessionFatoryBuilder.build () 获得 SqlSessionFatory 通过 DefaultSqlSessionFactory.open () 获得 SqlSession 创建 DefaultSqlSession 实现基础方法 selectAll,selectList 执行 JDBC 逻辑 创建 Executor、Executor 实现类,执行 CURD 处理返回结果 通过反射或内省 + SQLID 上的 resultType 全路径,处理返参 问题 1:数据库类型与实体类型不一致 1Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) 问题 2:数据库版本与驱动版本不一致 无法获取数据库连接,报错信息和获取连接方法有关 使用 C3P0 连接池是报错: 1java.sql.SQLException: Connections could not be acquired from the underlying database! 使用 DriverManager 直接连接时: 12Client does not support authentication protocol requested by server; consider upgrading MySQL client 持久层实现 通过 mapper 接口,数据库的交互 SqlSession 中创建一个 getMapper 方法,获取 mapper 的代理类,执行被代理类的方法 # Mybatis # 概念 基于 ORM 的 半自动 轻量级持久层框架。 # 缓存 底层数据结构: 就是一个 HashMap。 先去缓存中查,然后到数据库中,如果缓存中有,就直接返回,不再去数据库查询。 # 一级缓存 - SqlSession 级别 是否启用: 默认开启 cacheKey: org.apache.ibatis.executor.BaseExecutor#createCacheKey 增删改操作时,会刷新缓存(全部缓存) # 二级缓存 - NameSpace 级别 是否启用: 默认关闭,需要手动开启 [I] 二级缓存是在 SqlSession 事务提交时写入的 [!] 二级缓存在分布式的情况下,可能有问题。 1234567891011121314151617181920@Test public void secondLevelCacheTest() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class); IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class); User user1 = mapper1.selectByPrimaryKey(1); // 这样是不会查到二级缓存的,需要事务提交或者关闭后才可以 // sqlSession1.commit(); // sqlSession1.close(); User user2 = mapper2.selectByPrimaryKey(1); System.out.println(user1==user2); } 结论: 节省了数据库的交互 Q: 1234567891011121314151617181920212223242526@Test public void firstLevelCacheTest3() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true); SqlSession sqlSession2 = sqlSessionFactory.openSession(true); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class); User user = mapper.selectByPrimaryKey(1); System.out.println(user); mapper2.updateUserByPrimaryKey(new User(1, "gaga")); User user1 = mapper.selectByPrimaryKey(1); System.out.println(user1); System.out.println("user == user1: " + (user == user1)); sqlSession.close(); sqlSession2.close(); }// User{id=1, name='haha'}// User{id=1, name='haha'}// user == user1: true # 插件 [I] 需要在 SqlMapConfig.xml 中启用 123<plugins> <plugin interceptor="com.li.plugin.MyPlugin"></plugin> </plugins> # 分页插件 拦截器实现 [*] com.github.pagehelper.PageHelper [*] 入口: com.github.pagehelper.SqlUtil#_processPage [*] 增加 COUNTSQL: com.github.pagehelper.MSUtils#processCountMappedStatement(MappedStatement ms, SqlSource sqlSource, Object[] args) countSql 返回结果大于 0 时,执行分页,将总数设置到 page 对象中 替换参数 com.github.pagehelper.MSUtils#processPageMappedStatement(MappedStatement ms, SqlSource sqlSource, Page page, Object[] args) 创建新的 mapperStatement,执行分页 SQL 设置分页参数: com.github.pagehelper.MSUtils#setPageParameter # 通用 Mapper # 架构原理 # 架构设计 # 接口 通过 sqlSession.method (statementId) 或者 Mapper 代理类调用方法,执行主句的增删改查。 调用接口修改配置信息等。 # 数据处理 请求参数处理 (@Param):ParameterHandler SQL 解析 (处理占位符、Mapper 标签):SqlSource SQL 执行 (JDBC):Executor 返回结果处理 (类型转换等):ResultSetHandler # 框架支撑 事务管理 连接池管理 缓存机制 # 主要构件 SqlSession:session 表示与数据库的连接 Executor:执行器 StatementHandler: ParameterHandler: BoundSql: ResultSetHander: TypeHandler:数据库类型与 JavaBean 类型的转换 MappedStatement: SqlSource: # 总体流程 SqlSessionFactoryBuilder 获取 SqlSessionFactory SqlSessionFactory.openSession 获取 SqlSession 对象 通过 getMapper 获取 Mapper 代理对象 执行代理 Mapper 的方法 => Executor Mybatis 的执行器 => StatementHandler 与 JDBC Statement 的交互 => ParameterHandler 处理方法中携带的参数,拼接到 Sql 中 => 执行 JDBC 流程(加载驱动、建立连接、定义 Sql、获取预处理对象、处理参数、执行、处理返回结果) => 处理 Java 类型和数据库类型映射 # 源码分析 # getMapper 扫描 @Mapper 注解、从 sqlMapConfigXml 中读取 Mapper 包名,或者 Mapper 接口,将其存到 MapperRegistry.knownMappers 中 1Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); value 值存储的是一个工厂类,有个 Class<T> 的变量,和 newInstance(SqlSession sqlSession) 方法,用于给 Mapper 创建代理对象 [*] Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy); 123456789// JDK动态代理 生成代理对象/** * loader 类加载器 * interfaces 代理对象类型 * h InvocationHandler接口的实现类,需要实现invoke方法 */newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) # 二级缓存 [*] org.apache.ibatis.executor.CachingExecutor#flushCacheIfRequired [*] org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement(java.lang.String, org.apache.ibatis.mapping.SqlSource, org.apache.ibatis.mapping.StatementType, org.apache.ibatis.mapping.SqlCommandType, java.lang.Integer, java.lang.Integer, java.lang.String, java.lang.Class<?>, java.lang.String, java.lang.Class<?>, org.apache.ibatis.mapping.ResultSetType, boolean, boolean, boolean, org.apache.ibatis.executor.keygen.KeyGenerator, java.lang.String, java.lang.String, java.lang.String, org.apache.ibatis.scripting.LanguageDriver, java.lang.String) [?] 二级缓存需要再事务提交后或者关闭后生效 org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql) => 使用 CachingExecutor.query() => 清空缓存 => 1234567891011121314151617181920212223242526// 从二级缓存中,获取结果List<E> list = (List<E>) tcm.getObject(cache, key);getObject => TransactionalCacheManager.transactionalCaches.delegate中获取缓存// 如果没有取到 去一级缓存中取// 缓存查询结果 tcm.putObject(cache, key, list);=> 实际是存到了entriesToAddOnCommit中transactionalCaches中有一个:private final Map<Object, Object> entriesToAddOnCommit;public void putObject(Cache cache, CacheKey key, Object value) { // 存入TransactionalCache的缓存中 getTransactionalCache(cache).putObject(key, value); }=> entriesToAddOnCommit.put(key, object);transactionalCaches中有一个flushPendingEntries方法,该方法会在事务提交、关闭时会调用,这也是二级缓存需要在事务提交或者关闭后才能查到的原因// 将 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate(cache) 中 flushPendingEntries(); [?] 二级缓存为什么使用的是 CachingExecutor sqlSessionFactory.openSession () 时会 new Executor org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)","categories":[],"tags":[{"name":"学习","slug":"学习","permalink":"https://fairyeye.github.io/tags/%E5%AD%A6%E4%B9%A0/"}]},{"title":"Spring Boot Admin","slug":"Spring Boot Admin","date":"2024-03-13T09:11:02.629Z","updated":"2024-03-13T09:20:00.211Z","comments":true,"path":"2024/03/13/Spring Boot Admin/","link":"","permalink":"https://fairyeye.github.io/2024/03/13/Spring%20Boot%20Admin/","excerpt":"","text":"# 服务端配置 新建一个 SpringBoot 项目 pom.xml 12345678910111213<!--如果不需要鉴权 可以不加Security依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> application.properties 123# 端口server.port=20000 server.servlet.context-path=/admin 启动类加上 @EnableAdminServer 注解 如果不需要鉴权 到这里就结束了,运行项目,然后访问 localhost:20000/admin 就可以看到 SBA 的 UI # 非必须项 引入 Security,开启认证登录,下面是一个简单的样例 12345678910111213141516171819202122232425262728293031323334353637383940@Configuration(proxyBeanMethods = false) public class SecuritySecureConfig extends WebSecurityConfigurerAdapter { private final AdminServerProperties adminServer; public SecuritySecureConfig(AdminServerProperties adminServer) { this.adminServer = adminServer; } @Override protected void configure(HttpSecurity http) throws Exception { SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successHandler.setTargetUrlParameter("redirectTo"); successHandler.setDefaultTargetUrl(this.adminServer.path("/")); http.authorizeRequests() .antMatchers(this.adminServer.path("/assets/**")).permitAll() .antMatchers(this.adminServer.path("/login")).permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage(this.adminServer.path("/login")).successHandler(successHandler).and() .logout().logoutUrl(this.adminServer.path("/logout")).and() .httpBasic().and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers( new AntPathRequestMatcher(this.adminServer.path("/instances"), HttpMethod.POST.toString()), new AntPathRequestMatcher(this.adminServer.path("/instances/*"), HttpMethod.DELETE.toString()), new AntPathRequestMatcher(this.adminServer.path("/actuator/**")) ) .and() .rememberMe().key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("{noop}passwd").roles("USER"); } } 运行项目,然后访问 localhost:20000/admin 需要登录","categories":[{"name":"java","slug":"java","permalink":"https://fairyeye.github.io/categories/java/"}],"tags":[{"name":"日常记录","slug":"日常记录","permalink":"https://fairyeye.github.io/tags/%E6%97%A5%E5%B8%B8%E8%AE%B0%E5%BD%95/"}]},{"title":"security","slug":"security","date":"2024-02-22T11:26:20.691Z","updated":"2024-02-23T08:08:37.222Z","comments":true,"path":"2024/02/22/security/","link":"","permalink":"https://fairyeye.github.io/2024/02/22/security/","excerpt":"","text":"# 1. 引入依赖: 12345678<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 一些必要的配置: 新建一个配置类 WebSecurityConfig 继承 WebSecurityConfigurerAdapter 重写 configure 方法。(重要) 是 SpringSecurity 的核心 12345678@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 开启登录 http.formLogin(); } } 新建一个 controller 用来测试登录 1234567891011121314/** * @author zhanghuapeng * @date 2024/1/28 */@RestController public class UserController { /** * 获取当前登录用户信息 */ @GetMapping("/user-info") public Authentication getUserInfo(Authentication authentication) { return authentication; } } 启动项目:终端会有这么一段日志 1Using generated security password: f429b724-db54-4a56-ae82-7ebb63f22d69 表示:没有设置用户信息,给出了一个默认用户及密码,默认用户 user 登录之后,默认会跳转到 Index 页面,但是目前没有这个页面,所以会报错。 暂不处理。 访问: http://localhost:8080/user-info 返回结果: 1{"credentials":null,"details":null,"authenticated":false,"authorities":null,"principal":null,"name":"not login!"} 可以通过 http://localhost:8080/logount 退出登录 之后在访问 user-info 接口,发现不在包含用户信息 # 2. 实际使用中 没有登录的用户是不能访问接口的 修改 WebSecurityConfig 123456789@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 开启登录 http.formLogin(); // 设置访问权限 任何请求均需要认证(登录成功)才能访问 http.authorizeRequests().anyRequest().authenticated(); } } 重启项目 此时,访问: http://localhost:8080/user-info 发现会直接跳转到登录页面 # 增加一些细节 # 依赖 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950<dependencies> <!-- Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- Undertow --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <!-- Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <!--mybatis--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <!-- 添加jwt的依赖 --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.11.0</version> </dependency> <!--Redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency></dependencies> # 配置 12345678910111213141516171819202122232425262728293031server: port: 8080 spring: output: ansi: enabled: always # 强制启用 ansi 输出 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/security_study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 12345678 redis: host: 127.0.0.1 port: 6379 database: 1 jwt: secretKey: a3e4cd2d191a017bf49dbdf49a4c62b1fb292c5b112d6a51bdc4e2ea5052e816 expiration: 3600 logging: pattern: # 控制台日志格式 console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(%-40.40logger{39}){cyan} : %msg%n" mybatis: type-aliases-package: com.li.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath:mapper/*.xml 在 com.li 新建 utils 包,新建 JwtUtils 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596@Component @Slf4j public class JwtUtils { //算法密钥 @Value("${jwt.secretKey}") private String jwtSecretKey; // 过期时间 @Value("${jwt.expiration}") private long expiration; /** * 创建jwt * @param userInfo 用户信息 * @param authList 用户权限列表 * @return 返回jwt(JSON WEB TOKEN) */ public String createToken(String userInfo, List<String> authList) { //创建时间 Date currentTime = new Date(); //过期时间,5分钟后过期 Date expireTime = new Date(currentTime.getTime() + expiration); //jwt 的header信息 Map<String, Object> headerClaims = new HashMap<>(); headerClaims.put("type", "JWT"); headerClaims.put("alg", "HS256"); //创建jwt return JWT.create() .withHeader(headerClaims) // 头部 .withIssuedAt(currentTime) //已注册声明:签发日期,发行日期 .withExpiresAt(expireTime) //已注册声明 过期时间 .withIssuer("thomas") //已注册声明,签发人 .withClaim("userInfo", userInfo) //私有声明,可以自己定义 .withClaim("authList", authList) //私有声明,可以自定义 .sign(Algorithm.HMAC256(jwtSecretKey)); // 签名,使用HS256算法签名,并使用密钥 // HS256是一种对称算法,这意味着只有一个密钥,在双方之间共享。 使用相同的密钥生成签名并对其进行验证。 应特别注意钥匙是否保密。 } /** * 验证jwt的签名,简称验签 * * @param token 需要验签的jwt * @return 验签结果 */ public boolean verifyToken(String token) { //获取验签类对象 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build(); try { //验签,如果不报错,则说明jwt是合法的,而且也没有过期 DecodedJWT decodedJWT = jwtVerifier.verify(token); return true; } catch (JWTVerificationException e) { //如果报错说明jwt 为非法的,或者已过期(已过期也属于非法的) log.error("验签失败:{}", token); } return false; } /** * 获取用户id * * @param token jwt * @return 用户id */ public String getUserInfo(String token) { //创建jwt验签对象 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build(); try { //验签 DecodedJWT decodedJWT = jwtVerifier.verify(token); //获取payload中userInfo的值,并返回 return decodedJWT.getClaim("userInfo").asString(); } catch (JWTVerificationException e) { log.error("getUserInfo error", e); } return null; } /** * 获取用户权限 * * @param token * @return */ public List<String> getUserAuth(String token) { //创建jwt验签对象 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build(); try { //验签 DecodedJWT decodedJWT = jwtVerifier.verify(token); //获取payload中的自定义数据authList(权限列表),并返回 return decodedJWT.getClaim("authList").asList(String.class); } catch (JWTVerificationException e) { log.error("getUserAuth error", e); } return null; } } 在 com.li 新建 filter 包,新建 SaySomethingJWTFilter 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475/** * @author zhanghuapeng * @date 2024/2/22 * @desc 一次性请求过滤器 */ @Component public class SaySomethingJWTFilter extends OncePerRequestFilter { @Resource private ObjectMapper objectMapper; @Resource private StringRedisTemplate stringRedisTemplate; @Resource private JwtUtils jwtUtils; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //获取请求uri String requestURI = request.getRequestURI(); // 如果是登录页面,放行 if (requestURI.equals("/login")) { filterChain.doFilter(request, response); return; } //获取请求头中的Authorization String authorization = request.getHeader("Authorization"); //如果Authorization为空,那么不允许用户访问,直接返回 if (!StringUtils.hasText(authorization)) { printFront(response, "没有登录!"); return; } //Authorization 去掉头部的Bearer 信息,获取token值 String jwtToken = authorization.replace("Bearer ", ""); //验签 boolean verifyTokenResult = jwtUtils.verifyToken(jwtToken); //验签不成功 if (!verifyTokenResult) { printFront(response, "jwtToken 已过期"); return; } //从payload中获取userInfo String userInfo = jwtUtils.getUserInfo(jwtToken); //从payload中获取授权列表 List<String> userAuth = jwtUtils.getUserAuth(jwtToken); //创建登录用户 SysUser sysUser = objectMapper.readValue(userInfo, SysUser.class); SecurityUser securityUser = new SecurityUser(sysUser); //设置权限 List<SimpleGrantedAuthority> authList = userAuth.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); securityUser.setAuthorities(authList); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToke = new UsernamePasswordAuthenticationToken(securityUser , null, authList); //通过安全上下文设置认证信息 SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToke); //继续访问相应的rul等 filterChain.doFilter(request, response); } private void printFront(HttpServletResponse response, String message) throws IOException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); HttpResult httpResult = new HttpResult(); httpResult.setCode(-1); httpResult.setMsg(message); writer.print(objectMapper.writeValueAsString(httpResult)); writer.flush(); } } 调整 SecurityConfig , 将过滤器添加到配置中 123456789@Resource private SaySTokenFilter saySTokenFilter;@Override protected void configure(HttpSecurity http) throws Exception { // 增加配置 http.addFilterBefore(saySomethingJWTFilter, UsernamePasswordAuthenticationFilter.class); // ...原来的配置} # 调试 1234# 不携带token访问http://localhost:8080/user-info返回:{"code":-1,"msg":"没有登录!","data":null}# 携带错误token访问http://localhost:8080/user-info返回:{"code":-1,"msg":"jwtToken 已过期","data":null} 在 com.li.config , 新建 SaySAuthenticationSuccessHandler # 设置权限 在 loadUserByUsername 中获取权限,并设置到 SecurityUser 中 1234567891011// com.li.service.impl.UserServiceImplSecurityUser securityUser = new SecurityUser(sysUser); // 获取权限信息 List<String> authList = sysMenuDao.queryPermissionByUserId(sysUser.getUserId()); if (!CollectionUtils.isEmpty(authList)) { List<SimpleGrantedAuthority> authorities = authList.stream().map(SimpleGrantedAuthority::new).collect(toList()); // 设置权限 securityUser.setAuthorities(authorities); } return securityUser; 在 SaySAuthenticationSuccessHandler.onAuthenticationSuccess 中,生成 Token 时,可以将权限信息一起放入 Token 中。 12345678910List<String> authList = new ArrayList<>(); // 获取权限 List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) securityUser.getAuthorities(); if (!CollectionUtils.isEmpty(authorities)) { // 转成String 用于生成Token authList = authorities.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.toList()); }// 创建Token 增加authList参数String token = saySJwtUtils.createToken(userInfo, authList); # 注销处理 Jwt 本质上是一个字符串,无法手动将其过期,也就是说,即使手动退出登录,对于 Token 来说,还是一个有效的 Token,可以通过接入 Redis 来解决这一问题 登录成功时,将 Token 写入 Redis 123456789101112131415161718// SaySAuthenticationSuccessHandler// 设置过期时间 @Value("${jwt.expiration}") private long expiration;// 引入StringRedisTemplate@Resource private StringRedisTemplate stringRedisTemplate;// 在创建Token之后,将Token存到Redis中onAuthenticationSuccess(){// 创建Token String token = saySJwtUtils.createToken(userInfo, authList);// 写入Redis stringRedisTemplate.opsForValue().set("login_token:" + token, objectMapper.writeValueAsString(authentication), expiration, TimeUnit.MILLISECONDS);} 校验 Token 时,先验签,再去 Redis 中判断 Token 是否还存在 如果验签成功,但是 Redis 中不存在,说明 Token 被手动过期了 12345678910doFilterInternal(){...// 从Redis获取token并校验 String tokenInRedis = stringRedisTemplate.opsForValue().get("login_token:" + jwtToken); if (!StringUtils.hasText(tokenInRedis)) { printFront(response, "用户已退出,请重新登录"); return; }...} 在 com.li.config ,新建 SaysLogoutSuccessHandler 12345678910111213141516171819202122232425262728293031323334353637383940 /** * 退出成功处理器,用户退出成功后,执行此处理器 */ @Component public class SaysLogoutSuccessHandler implements LogoutSuccessHandler { //使用此工具类的对象进行序列化操作 @Resource private ObjectMapper objectMapper; @Resource private StringRedisTemplate stringRedisTemplate; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //从请求头中获取Authorization信息 String authorization = request.getHeader("Authorization"); //如果授权信息为空,返回前端 if (null == authorization) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf-8"); HttpResult httpResult = HttpResult.builder().code(-1).msg("token不能为空").build(); PrintWriter writer = response.getWriter(); writer.write(objectMapper.writeValueAsString(httpResult)); writer.flush(); return; } //如果Authorization信息不为空,去掉头部的Bearer字符串 String token = authorization.replace("Bearer ", ""); //redis中删除token,这是关键点 stringRedisTemplate.delete("login_token:" + token); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf-8"); HttpResult httpResult = HttpResult.builder().code(200).msg("退出成功").build(); PrintWriter writer = response.getWriter(); writer.write(objectMapper.writeValueAsString(httpResult)); writer.flush(); } } 调整 SecurityConfig 123456789@Resource private SaysLogoutSuccessHandler saysLogoutSuccessHandler;configure(){http.logout().logoutSuccessHandler(saysLogoutSuccessHandler);// 禁用跨域请求保护 要不然logout不能访问(目前体现是弹出了确认退出登录的确认框 http.csrf().disable();} org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate org.springframework.security.authentication.dao.DaoAuthenticationProvider#retrieveUser org.springframework.security.authentication.dao.DaoAuthenticationProvider#additionalAuthenticationChecks","categories":[],"tags":[]},{"title":"","slug":"Leetcode","date":"2024-01-19T01:20:19.000Z","updated":"2024-01-19T09:07:52.000Z","comments":true,"path":"2024/01/19/Leetcode/","link":"","permalink":"https://fairyeye.github.io/2024/01/19/Leetcode/","excerpt":"","text":"# 2171 拿出最少得魔法豆","categories":[],"tags":[]},{"title":"","slug":"数组与链表","date":"2024-01-03T02:14:03.735Z","updated":"2024-01-03T02:14:03.735Z","comments":true,"path":"2024/01/03/数组与链表/","link":"","permalink":"https://fairyeye.github.io/2024/01/03/%E6%95%B0%E7%BB%84%E4%B8%8E%E9%93%BE%E8%A1%A8/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"Hello Algo","slug":"Algo","date":"2023-12-25T08:04:09.000Z","updated":"2024-01-03T02:03:31.741Z","comments":true,"path":"2023/12/25/Algo/","link":"","permalink":"https://fairyeye.github.io/2023/12/25/Algo/","excerpt":"","text":"二分搜索、插入排序、贪心 迭代、递归 1234# n >= 1 时T(n) = 3+2n <= 3n+2n = 5nT(n) <= c * f(n)T(n) = O(f(n)) # KMP # 求 next[] 12next[]:找出一个以0下标(必须0下标)开始,以j-1下标结束的两个相同子串=>next[j-1] => k-1 123456789101112131415161718192021222324252627282930313233343536373839404142哈哈 k x j 下标k 0 1 2 3 4 5 6 7 8 9 10 11 12 13数组p a b a b c a b c d a b c d enext数组 -1 0 0 1 2 0 1 2 0 0 1 2 0 0j++下标0 = a当 j = 3: 下标j-1=2 -> a 可以找到 a、aba、a 但是aba不满足条件 => 1当 j = 4: 下标j-1=3 -> b 可以找到 ab、abab 但是abab不满足条件 => 2当 j = 5: 下标j-1=4 -> c 可以找到 ababc 不满足条件 => 0哈哈 k x j 下标k 0 1 2 3 4 5 6 7 8 9 10 11 12 13数组p a b a b c a b a d a b c d enext数组 -1 0 0 1 2 0 1 2 3 0 1 2 0 0已知条件:以0下标(必须0下标)开始,以j-1下标结束的两个相同子串p[0]..p[k-1] = p[x]..p[j-1]得出:=> k-1-0 = j-1-x => k = j-x=> x = j-k==> p[0]..p[k-1] = p[j-k]..p[j-1]假设:p[k] = p[j]=> p[0]..p[k] = p[j-k]..p[j]所以 next[j] = k k-1 = next[j-1]p[0]..p[k-1] = p[x]..p[j-1]假设:p[k] = p[j]p[0]..p[k-1]p[k] = p[x]..p[j-1]p[j]p[0]..p[k] = p[j-k]..p[j]k = next[j] # 数组 优点: 空间效率高 支持随机访问 缓存局部性? 缺点: 插入与删除效率低 长度不可变 空间浪费 典型应用: 随机访问 排序、搜索 查找表 机器学习 数据结构实现 # 链表 # 数组 vs 链表 存储方式 容量扩展 内存效率 访问元素 添加元素 删除元素","categories":[],"tags":[{"name":"学习","slug":"学习","permalink":"https://fairyeye.github.io/tags/%E5%AD%A6%E4%B9%A0/"}]},{"title":"测试","slug":"文章名称","date":"2023-10-27T03:08:31.251Z","updated":"2023-10-27T03:08:31.521Z","comments":true,"path":"2023/10/27/文章名称/","link":"","permalink":"https://fairyeye.github.io/2023/10/27/%E6%96%87%E7%AB%A0%E5%90%8D%E7%A7%B0/","excerpt":"","text":"123if(Objects.nonNull(prLine)&&Objects.nonNull(prLine.getPurchaseAgentId())){ poHeaderDetailDTO.setAgentId(prLine.getPurchaseAgentId());}","categories":[],"tags":[]},{"title":"Mac使用记录","slug":"Mac使用记录","date":"2023-10-25T03:31:59.000Z","updated":"2024-06-05T12:19:12.080Z","comments":true,"path":"2023/10/25/Mac使用记录/","link":"","permalink":"https://fairyeye.github.io/2023/10/25/Mac%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/","excerpt":"","text":"# 软件 # ttygif 终端录制工具 # 安装教程 1brew install ttygif # 使用 命令行提示符 command:(\"[root@localhost] $\":1,9-10||\"[admin@remotehost] #\":4-6)1ttyrec myrecording # sshx 终端共享 # 使用 123456zhanghuapengdeMacBook-Pro :: Downloads/work_space/AA % sshx sshx v0.2.1 ➜ Link: https://sshx.io/s/ZtVval8VO2#e9o4sruIiflVdh ➜ Shell: /bin/zsh # Flutter # 环境安装: 官方说明 # 环境 python 环境 虚拟环境:~/ DP:DrissionPage # LUA lua+redis 限流 # Cargo install cargo # 破解软件打不开 123456# 这个好像没生效Mac :: ~ % sudo spctl --global-disablePassword:# 将软件拖进来Mac :: ~ % sudo xattr -r -c /Applications/Navicat\\ Premium.app # Jrebel 激活 12345docker pull qierkang/golang-reverseproxydocker run -d -p 8888:8888 qierkang/golang-reverseproxy#licenehttp://127.0.0.1:8888/7a14c9f7-8a27-46d6-bb50-2b30c19e766c # 一行命令下载全网视频 1$ pip3 install you-get 如何下载 1. 可通过如下命令查看该视频的详细信息。 1you-get -i '视频url' 2. 下载方式更简单,只需一行命令即可下载了: 1you-get '视频url' # Jan - 将人工智能带入您的桌面 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355```## Sonoma系统退回到Catalina![](https://s3.bmp.ovh/imgs/2024/03/18/26d4edbd95ba29b0.png)20款MacBook Pro,使用Sonoma系统感觉有点卡顿,晚上说是新系统对旧Mac兼容不是很好,决定退回Catalina版本,最后一个Inter电脑发布的系统准备:U盘(32G)(没有也行时间机器:(没有也行 主打一个凑合电脑硬盘:这个得有Catalina系统安装器(去App Store下载好0. 先分区 - 有U盘的情况,直接新建一个系统分区就好(`APFS`格式) - 无U盘的情况,先建一个系统分区,再用至少20G空间做一个引导系统分区,格式选(Mac OS日志) - 分区的时候,该抹掉就抹掉,只要别把当前系统抹掉就行1. 制作引导系统 (这步记不太清楚了有U盘的情况下,重启电脑,按`option`键,显示小地球图标(没有图),大概就是下面的这种,![](https://s3.bmp.ovh/imgs/2024/03/18/5ad8cd52fe6b475c.png)2. 分区3. 安装到分区上4. 用Catalina系统制作时间机器,保证时间机器分区是Mac OS 扩展(日志式)5. 将Sonoma系统数据备份到时间机器6. 到Catalina系统,用迁移助理将数据迁移过来7. 后续看情况删除Sonoma系统分区## Docker### QL#### dailycheckin```python"""获取i茅台账号cookie"""import hashlibimport jsonimport timeimport requests"""获取地点信息,这里用的高德 api,需要自己去高德开发者平台申请自己的 key"""AMAP_KEY = "d13d06ac58fd360776f58583254c0079"SALT = "2af72f100c356273d46284f6fd1dfc08"CURRENT_TIME = str(int(time.time() * 1000))headers = {}mt_version = json.loads(requests.get("https://itunes.apple.com/cn/lookup?id=1600482450").text)["results"][0]["version"]header_context = """MT-Lat: 28.499562MT-K: 1675213490331MT-Lng: 102.182324Host: app.moutai519.com.cnMT-User-Tag: 0Accept: */*MT-Network-Type: WIFIMT-Token: 1MT-Team-ID: 1MT-Info: 028e7f96f6369cafe1d105579c5b9377MT-Device-ID: 2F2075D0-B66C-4287-A903-DBFF6358342AMT-Bundle-ID: com.moutai.mallAccept-Language: en-CN;q=1, zh-Hans-CN;q=0.9MT-Request-ID: 167560018873318465MT-APP-Version: 1.3.7User-Agent: iOS;16.3;Apple;?unrecognized?MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==Content-Length: 93Accept-Encoding: gzip, deflate, brConnection: keep-aliveContent-Type: application/jsonuserId: 2"""# 初始化请求头def init_headers(user_id: str = "1", token: str = "2", lat: str = "29.83826", lng: str = "119.74375"):for k in header_context.strip().split("\\n"):temp_l = k.split(": ")dict.update(headers, {temp_l[0]: temp_l[1]})dict.update(headers, {"userId": user_id})dict.update(headers, {"MT-Token": token})dict.update(headers, {"MT-Lat": lat})dict.update(headers, {"MT-Lng": lng})dict.update(headers, {"MT-APP-Version": mt_version})# 用高德api获取地图信息def select_geo(i: str):# 校验高德api是否配置if AMAP_KEY is None:print("!!!!请配置 AMAP_KEY (高德地图的MapKey)")raise ValueErrorresp = requests.get(f"https://restapi.amap.com/v3/geocode/geo?key={AMAP_KEY}&output=json&address={i}")geocodes: list = resp.json()["geocodes"]return geocodesdef signature(data: dict):keys = sorted(data.keys())temp_v = ""for item in keys:temp_v += data[item]text = SALT + temp_v + CURRENT_TIMEhl = hashlib.md5()hl.update(text.encode(encoding="utf8"))md5 = hl.hexdigest()return md5# 获取登录手机验证码def get_vcode(mobile: str):params = {"mobile": mobile}md5 = signature(params)dict.update(params, {"md5": md5, "timestamp": CURRENT_TIME, "MT-APP-Version": mt_version})responses = requests.post("https://app.moutai519.com.cn/xhr/front/user/register/vcode",json=params,headers=headers,)if responses.status_code != 200:print(f"get v_code : params : {params}, response code : {responses.status_code}, response body : {responses.text}")# 执行登录操作def login(mobile: str, v_code: str):params = {"mobile": mobile, "vCode": v_code, "ydToken": "", "ydLogId": ""}md5 = signature(params)dict.update(params, {"md5": md5, "timestamp": CURRENT_TIME, "MT-APP-Version": mt_version})responses = requests.post("https://app.moutai519.com.cn/xhr/front/user/register/login",json=params,headers=headers,)if responses.status_code != 200:print(f"login : params : {params}, response code : {responses.status_code}, response body : {responses.text}")dict.update(headers, {"MT-Token": responses.json()["data"]["token"]})dict.update(headers, {"userId": responses.json()["data"]["userId"]})return responses.json()["data"]["token"], responses.json()["data"]["userId"]def get_location():while 1:location = input("请输入精确小区位置,例如[小区名称],为你自动预约附近的门店:").strip()selects = select_geo(location)a = 0for item in selects:formatted_address = item["formatted_address"]province = item["province"]print(f"{a} : [地区:{province},位置:{formatted_address}]")a += 1user_select = input("请选择位置序号,重新输入请输入[-]:").strip()if user_select == "-":continueselect = selects[int(user_select)]formatted_address = select["formatted_address"]province = select["province"]print(f"已选择 地区:{province},[{formatted_address}]附近的门店")return selectif __name__ == "__main__":items = []while 1:init_headers()location_select: dict = get_location()province = location_select["province"]city = location_select["city"]location: str = location_select["location"]mobile = input("输入手机号[18888888888]:").strip()get_vcode(mobile)code = input(f"输入 [{mobile}] 验证码[8888]:").strip()token, userId = login(mobile, code)item = {"city": str(city),"lat": location.split(",")[1],"lng": location.split(",")[0],"mobile": str(mobile),"province": province,"token": str(token),"userid": str(userId),"reserve_rule": 0,"item_codes": ["10941", "10942"],}items.append(item)condition = input("是否继续添加账号[y/n]:").strip()with open("account.json", "w") as f:f.write(json.dumps(items, ensure_ascii=False, indent=4))if condition.lower() == "n":break # Scrcpy 12# 手机息屏启动scrcpy --turn-screen-off # 服务器 FRP # path: /home/li/frpc","categories":[],"tags":[{"name":"日常记录","slug":"日常记录","permalink":"https://fairyeye.github.io/tags/%E6%97%A5%E5%B8%B8%E8%AE%B0%E5%BD%95/"}]},{"title":"Centos","slug":"Centos","date":"2023-10-12T08:12:32.000Z","updated":"2024-06-13T03:18:18.506Z","comments":true,"path":"2023/10/12/Centos/","link":"","permalink":"https://fairyeye.github.io/2023/10/12/Centos/","excerpt":"","text":"# 1.frpc 内网穿透 # 2. 青龙 # 3.xdd-plus https://www.qqmate.cn/652.html 1. 进入 xdd 目录找到 device.json 文件 2. 双击打开,修改: “protocol”:0, 改为 "protocol":2, # FRPC 123456789101. 在`frpc.exe`所在的文件夹中,右键点击空白处,选择“新建” -> “快捷方式”。2. 在创建快捷方式向导中,浏览并选择`frpc.exe`文件,然后点击“下一步”。3. 给快捷方式命名,然后点击“完成”。4. 找到刚刚创建的快捷方式,右键点击它,选择“属性”。5. 在“快捷方式”标签页下,找到“目标”字段。默认情况下,它应该只包含`"C:\\Path\\To\\frpc.exe"`(假设`frpc.exe`在`C:\\Path\\To`目录下)。6. 在“目标”字段的末尾,添加一个空格,然后输入`-c frpc.toml`,确保整个命令看起来像这样:`"C:\\Path\\To\\frpc.exe" -c frpc.toml`。7. 点击“应用”和“确定”保存更改。8. 将这个修改过的快捷方式拖放到“启动”文件夹中。这样,每次您登录Windows时,`frpc.exe`都会以`frpc.toml`作为配置文件运行。请注意,这种方法不会在后台静默运行`frpc.exe`,它会在用户登录时打开一个命令行窗口。如果您想要`frpc.exe`在后台运行而不显示命令行窗口,您应该考虑使用任务计划程序或将其安装为服务。 12345678910111213141516171. **使用任务计划程序**: - 打开“任务计划程序”(可以在开始菜单中搜索“任务计划程序”来找到它)。 - 创建一个新的基本任务,设置触发器按照您的需要启动任务(例如,计算机启动时)。 - 在操作步骤中,选择“启动程序”,然后浏览并选择`frpc.exe`,并在“添加参数(可选)”中输入`-c frpc.toml`。2. **使用Windows服务**: - 使用第三方工具如[nssm](https://nssm.cc/)(Non-Sucking Service Manager)将`frpc.exe`安装为一个服务。 - 下载并解压`nssm.exe`。 - 打开命令提示符或PowerShell,导航到`nssm.exe`所在的文件夹。 - 运行命令`nssm install <ServiceName>`来创建新的服务,然后`nssm set <ServiceName> AppPath <PathTofrpc.exe>`设置应用路径,接着`nssm set <ServiceName> AppParameters -c frpc.toml`设置参数。 - 最后,启动服务使用`nssm start <ServiceName>`。3. **使用批处理文件**: - 创建一个批处理文件(`.bat`),在其中写入`frpc.exe -c frpc.toml`。 - 将批处理文件放置在`frpc.exe`相同的文件夹中。 - 您可以双击运行此批处理文件,或者将其添加到启动文件夹以在用户登录时自动运行。 # 端口 # 3001 账号:li gz123456 # 开机启动 frpc 全部服务 123456789101112[Unit]Description=Frp Multiple Client ServicesAfter=network.target[Service]Type=simpleExecStart=/root/frpc/frpc/start_all_frpc.shRestart=on-failureRestartSec=5s[Install]WantedBy=multi-user.target start_all_frpc.sh 1234567#!/bin/bash/root/frpc/frpc -c /root/frpc/frpc/frpc.toml &/root/frpc/frpc -c /root/frpc/frpc/frpc-29252.toml &/root/frpc/frpc -c /root/frpc/frpc/frpc-3001.toml &wait # Nginx # 安装 12345678910111213141516sudo yum install nginx# 如果报错:没有可用软件包 nginxsudo vi /etc/yum.repos.d/nginx.repo[nginx-stable]name=nginx stable repobaseurl=http://nginx.org/packages/centos/$releasever/$basearch/gpgcheck=1enabled=1gpgkey=https://nginx.org/keys/nginx_signing.keymodule_hotfixes=true # 使用 123456curl localhost可以curl ip 不可以修改配置文件/etc/nginx/config.d/default.conf # GitHub Page # NGINX 配置 123456789listen 80;server_name 10.213.42.79;#access_log /var/log/nginx/host.access.log main;location / { root /usr/share/nginx/fairyeye.github.io; index index.html index.htm;} 分之: master 位置: /usr/share/nginx/fairyeye.github.io [/] 未实现部分 自动拉代码 本地每天部署一遍 更新图片地址 # Windows 开机启动虚拟机 在虚拟机安装目录新建 start_vm.bat , ps:如果 vmx 文件包含中文,保存编码选择 GB 开头的格式 123456@echo off.\\vmrun.exe start "D:\\Centos\\CentOS 7 64位.vmx"exit","categories":[],"tags":[]},{"title":"231104","slug":"231104","date":"2023-09-25T01:36:59.000Z","updated":"2024-01-24T08:27:40.840Z","comments":true,"path":"2023/09/25/231104/","link":"","permalink":"https://fairyeye.github.io/2023/09/25/231104/","excerpt":"","text":"# pur-24166 返回消息: scux.spuc.lotus_nyo_no_enabled_master_enable_bank scux.spuc.lotus_nyo_no_erp_supplier 埋点: SPUC_SUBMIT_PO # pur-26089 组合业务对象: SRM_C_SRM_SODR_PO_HEADER 个性化单元: SODR.ORDER_TRACKING_LIST.EXPORT # pur-25937 平台供应商多语言待确定 页面个性化: SODR.WORKSPACE_DETAILALL.SEARCH # pur-26467 独立脚本 + API 发布: SCUX_LDJT_QUERY_LAST_PURCHASE_PRICE # pur-26322 独立脚本 + API: SCUX_WATSONS_UPDATE_ATTACHMENT_WFL 适配器: SCUX_MTC_PO_HEADER_INFO SPUC_ORDER_QUERY_PROCESS_ACTION # pur-26427 配置表: scux_srm_carlsberg_contract_amount_change_record 适配器: SPUC_ORDER_SYNC_IMP_INSERT_PROCESS SPUC_BEFORE_ERP_CLOSE_OR_CANCEL_PO 更新时:判断取消状态和表里的取消状态 # pur-26689 独立脚本: SCUX_VN_SCM_PO_RETURN_OA 适配器: 重新同步:SPUC_RE_SYNC_ERP 值集: SPUC.SYNC_EXP_TYPE SPUC.SYNC_INTERFACE_TYPE # pur-26080 适配器: SPUC_SUBMIT_PO SPUC_ORDER_APPROVED_EVENT # pur-26150 配置表: scux_srm_sanning_fee_info scux_srm_sanning_fee_calculate_type API: 查询(返回配置表数据,且翻译税率、关联协议:SCUX_SANNING_FEE_INFO_QUERY /marmot-api/v49ECXUYP60iaTD6VDYCeIrqbnlzgazaL5HYWyv9ScGg 查询2 及币种精度、CNY币种ID、CNY、费用计算类型对应关系 SCUX_SANNING_FEE_OTHER_QUERY 保存 有ID是更新,没有ID是新增 SCUX_SANNING_FEE_INFO_SAVE_OR_UPDATE /marmot-api/v49ECXUYP60iaTD6VDYCeIpQpEumLeekdKWnTERvKJjibX4rd3PkiakwNOzdMU5JBn8 删除 根据ID删除,支持批量删除 SCUX_SANNING_FEE_INFO_DEL /marmot/v1/20990/marmot-api/v49ECXUYP60iaTD6VDYCeIiab6r6sOKdPfrdSqia2ePicuU 更新预付标识:SCUX_SANNING_FEE_INFO_UPDATE_PAY_FLAG 查询: 给结算用 SCUX_SANNING_FEE_INFO_QUERY_FOR_SETTLE QB:SCUX_SANNING_FEE_INFO_QUERY_FOR_SETTLE 独立脚本: SCUX_SANNING_FEE_INFO_QUERY SCUX_SANNING_FEE_OTHER_QUERY SCUX_SANNING_FEE_INFO_SAVE_OR_UPDATE SCUX_SANNING_FEE_INFO_DEL 埋点: 保存、提交计算头个性化字段金额:SPUC_ORDER_PRICE_SOURCE_TYPE 整单取消:SPUC_ALL_CANCEL_PO_HEADER 按行取消:SPUC_LINE_CANCEL_PO_LINE 复制: SPUC_ORDER_SAVE_DATA_CONVERSION_AFTER(写入数据 SPUC_ORDER_SAVE_DATA_CONVERSION_NEWPRICE_AFTER SCUX_ORDER_PO_COPY_LINE(记录订单行ID # pur-27164 适配器: SPUC_ORDER_SYNC_IMP_INSERT_PROCESS SPUC_ORDER_SYNC_IMP_UPDATE_PROCESS 配置表: scux_srm_daqo_po_drawing_info API+独立脚本 查询:SCUX_DAQO_PO_DRAWING_INFO_QUERY 导出:SCUX_DAQO_PO_DRAWING_INFO_EXPORT 下载: # BUG pur-26901 SPUC_ORDER_BATCH_SUBMIT_HANDLE Ctrl + C","categories":[],"tags":[{"name":"work","slug":"work","permalink":"https://fairyeye.github.io/tags/work/"},{"name":"迭代","slug":"迭代","permalink":"https://fairyeye.github.io/tags/%E8%BF%AD%E4%BB%A3/"}]},{"title":"Git","slug":"GIT","date":"2023-06-17T14:33:35.000Z","updated":"2024-11-05T02:01:02.659Z","comments":true,"path":"2023/06/17/GIT/","link":"","permalink":"https://fairyeye.github.io/2023/06/17/GIT/","excerpt":"","text":"# Gitee Go # 流水线 12# 官方文档https://blog.gitee.com/2022/11/23/pipeline/ # 前端 CI 手动创建流水线,选择部署分之,需要手动增加部署阶段 # 添加部署阶段 点击发布后面的+ 添加新阶段 点击部署 点击主机部署 选择执行主机组(如果没有就先去添加主机,选择 填写部署脚本,前端项目把上游构建的包,解压到服务器指定路径即可 12345678910# 功能:部署脚本会在部署主机组的每台机器上执行# 使用场景:先将制品包解压缩到指定目录中,再执行启动脚本deploy.sh,脚本示例地址:https://gitee.com/gitee-go/spring-boot-maven-deploy-case/blob/master/deploy.sh# mkdir -p /home/admin/app# tar zxvf ~/gitee_go/deploy/output.tar.gz -C /home/admin/app# sh /home/admin/app/deploy.sh restart# 如果你是php之类的无需制品包的制品方式,可以使用 git clone 或者 git pull 将源代码更新到服务器,再执行其他命令# git clone ***@***.gittar zxvf ~/gitee_go/deploy/output.tar.gz -C /home/ubuntu # 添加主机 点击新建主机组 选择新建类型(以腾讯云为例),填写基本信息(以 Linux 为例),点击确认 添加主机 点击添加 Linux 主机,选择通过命令行逐台添加, 复制命令到目标腾讯云主机命令行 刷新页面即可见关联服务器信息 # 后端 CI # 部署脚本 12345678910111213cd ~/gitee_go/deploy/lstar -zxf API.tar.gzcd targetpid=`ps -ef|grep smart-admin-api-1.0.0|grep -v grep|awk '{print $2}'`if [ $pid ]thensudo kill -15 $pid fisudo nohup /usr/lib/jvm/jdk1.8.0_341/bin/java -jar smart-admin-api-1.0.0.jar >/home/ubuntu/log.log & 123456789101112131415161718cd ~/gitee_go/deploy/lstar -zxf API.tar.gzcd targetpid=`ps -ef|grep smart-admin-api-1.0.0|grep -v grep|awk '{print $2}'`if [ $pid ]thensudo kill -15 $pid fisudo nohup /usr/lib/jvm/jdk1.8.0_341/bin/java -jar smart-admin-api-1.0.0.jar> /home/ubuntu/log.log 2>&1 & # 开源项目 # 免费的 API 学习平台:apihub 主语言:JavaScript,Star:6.4k,周增长:1k 这是一个功能齐全的 API 学习平台,支持多种编程语言(Node.js、Python、Go 等)的 API 开发和学习。它免费提供丰富的 API 集合,涉及社交媒体集成、支付网关、物联网设备连接和机器学习等领域。你可以在该平台获取 API 开发的各类资源,包括详细教程、接口文档、代码示例和在线尝试。除了使用在线服务外,强烈推荐用户选择本地部署,以避免官网服务每两小时重置数据的限制。 GitHub 地址→github.com/hiteshchoudhary/apihub # 轻松启动本地 HTTPS 代理的工具:ophiuchi-desktop 主语言:TypeScript,Star:928 这是一个本地 HTTPS 代理服务器管理工具,无需复杂配置即可轻松设置本地 HTTPS 代理。它使用 Docker 作为后端,并采用 Tauri 编写 GUI 界面,极大地简化了本地 HTTPS 代理的配置流程。不过,使用前需确保本机已安装 Docker。 GitHub 地址→github.com/apilylabs/ophiuchi-desktop # 一个鼠标操作多个电脑 Deskflow 帮助用户在多台计算机(包括 Windows、macOS 和 Linux)之间共享键盘和鼠标,就像软件版的 KVM(但不包含视频功能)。 它支持 TLS 加密、Wayland 显示协议,并且具备剪贴板共享功能。作为社区驱动项目,Deskflow 鼓励用户参与开发和改进,同时与类似的开源项目合作,如 Synergy 和 Input Leap。用户可以通过安装包或源码编译来使用该软件。 开源地址:https://github.com/deskflow/deskflow # beszel:轻量级高颜值的 Docker 监控平台。 这是一个轻量级的服务器监控平台,包括 Docker 统计、历史数据和警报功能。它拥有友好的 Web 界面,配置简单、开箱即用,支持自动备份、多用户、OAuth 认证和 API 访问等功能。 地址:github.com/henrygd/beszel # mame:开源的街机模拟器。 这是一款支持海量街机游戏的模拟器。它通过模拟多种硬件平台,实现了在电脑上运行各种复古软件的功能。不仅支持街机,还有老式电脑和游戏机。 地址:github.com/mamedev/mame # shadPS4:开源的 PS4 模拟器。 这是用 C++ 编写的 PlayStation 4(PS4)模拟器,支持在 Windows、Linux 和 macOS 系统上玩 PS4 游戏。虽然项目仍处于早期开发阶段,能运行的游戏有限,但最新版已经能够成功运行《血源诅咒》和《黑暗之魂 II》等游戏。 地址:github.com/shadps4-emu/shadPS4 # 索尼 PS1 模拟器 “开源” 项目 Duckstation 是由 stenzek 开发的索尼 PS 模拟器,适用于 x86-64/AArch32/AArch64/RV64。 这开源项目专注于可玩性,速度,目标是尽可能让相对低端的设备也能玩 PS 游戏。默认设置即可运行所有支持的游戏,仅有部分兼容性问题。 “开源” 地址:https://github.com/stenzek/duckstation # omakub:精美的 Ubuntu 配置方案。 该项目可以将全新的 Ubuntu 24.04 系统配置成美观、功能齐全、适合 Web 开发的系统。只需简单的一条命令,即可拥有配置好的 GNOME 桌面环境、窗口管理工具、Alacritty 终端、Neovim 和 VSCode 编辑器等应用,还会将 Chrome 设置成默认浏览器。 地址:github.com/basecamp/omakub # Oshi 1获取操作系统和硬件信息的 Java 库。这是一个基于 JNA 实现的获取本机操作系统和硬件信息的库,支持操作系统版本、进程、内存、 CPU 使用率、磁盘和分区、设备、传感器等信息。 # PlayEdu 1一款 Java 写的内部培训系统。这是一款基于 SpringBoot+React 开发而成的视频培训系统,它界面清爽、交互流畅,支持上传资源、创建部门、添加学员、指派课程等功能,可用于企业和机构搭建内部培训平台。 # Holer 1234一个将局域网中的应用映射到公网访问的端口映射软件,支持转发基于 TCP 协议的报文。内网穿透工具,包含 Web 后台管理系统。用到的技术如下:- 服务端采用 SpringBoot 和 Netty 实现- 客户端采用 Java Netty 和 Go 语言实现 # SoloPo 1一个不需要连接电脑、非侵入式的 Android 自动化工具。公测版拥有录制回放、性能测试、一机多控三项主要功能,能为测试开发人员节省宝贵时间。安卓版本多、终端型号多,一个成熟安卓应用的上线需要进行大量测试,而很多测试都是属于重复操作,通过此工具可以极大简化测试人员的工作量 # Hitomi 本周 star 增长数:400+,主语言:Python Hitomi-Downloader 知名下载工具,只需要一个 url 就能下载对应的图片、视频、音频。部分特性: 简洁的用户界面 支持下载加速,也支持限速 支持单任务由 24 个线程 支持多种下载方式 # Python 搞定 UI:nicegui 本周 star 增长数:850+,主语言:Python、JavaScript New 用 Python 搞定 Web UI,有了它,你可以用 Python 创建按钮、对话框、Markdown 文件、3D 场景。 GitHub 地址→https://github.com/zauberzeug/nicegui # 炫酷的 Windows 终端软件:FluentTerminal 主语言:C# 基于 UWP 的 Windows 终端应用,拥有强大的自定义主题模块,能够轻松定制出风格各异的主题。提供了中文选项,支持多窗口、SSH 和搜索等功能。 HG 评价地址→https://hellogithub.com/repository/352150f3034742cbbf67d301a86973ca # AI 生图:ControlNet 主语言:Python New 上周线稿上色的 style2paints 在 ControlNet 面前可能只是个弟弟。ControlNet 是一种通过添加额外条件来控制扩散模型的神经网络结构。为什么说 style2paints 是个弟弟呢?这是 ControlNet 的社生成效果图,从线稿到成品,一句话搞点。 GitHub 地址→https://github.com/lllyasviel/ControlNet # 1Panel 12345678910111213141516[1Panel Log]: 1Panel 服务启动成功![1Panel Log]:[1Panel Log]: =================感谢您的耐心等待,安装已经完成==================[1Panel Log]:[1Panel Log]: 请用浏览器访问面板:[1Panel Log]: 面板地址: http://$LOCAL_IP:29252/821d637d70[1Panel Log]: 用户名称: e0ae9ef986[1Panel Log]: 用户密码: zhang...1997[1Panel Log]:[1Panel Log]: 项目官网: https://1panel.cn[1Panel Log]: 项目文档: https://1panel.cn/docs[1Panel Log]: 代码仓库: https://github.com/1Panel-dev/1Panel[1Panel Log]:[1Panel Log]: 如果使用的是云服务器,请至安全组开放 29252 端口[1Panel Log]:[1Panel Log]: ================================================================ # Nezha 国产的轻量级服务器监控工具。 这是一款名为 “哪吒” 的服务器监控面板,它安装简单、开箱即用,支持监控多个服务器的系统状态、SSL 证书状态、报警通知、流量监控、设置定时任务等功能,适用于 Linux、Windows、macOS、OpenWRT 等主流系统。 # Deskreen:将任何屏幕变成你的扩展显示器 我们经常需要在多个设备之间切换工作,但有时候,如果能将手机或平板变成电脑的第二屏幕,那将大大提高工作效率。 Deskreen,一个开源项目,正是为此而生!这是一个简单而强大的工具,它允许你将任何带有网络浏览器的设备变成电脑的第二屏幕。 无论是为了扩展你的工作空间,还是为了在大屏幕上展示你的演示文稿,Deskreen 都能轻松实现。 1开源地址:https://github.com/pavlobu/deskreen # Etcher 简单易用的 USB/SD 启动盘制作工具。 该项目可以将操作系统镜像烧录进 SD 卡或 USB 设备,可用于制作可启动、便携式的操作系统。它拥有友好的操作界面,仅需 3 步就能完成 USB 启动盘制作,适用于 Linux、macOS 和 Windows 10 及更高版本。 # MoneyPrinterTurbo:一键生成短视频的 AI 工具。 该项目是基于大模型服务的 AI 视频生成工具,只需要提供一个主题或关键字,就可以自动生成高清的短视频。它拥有简单易用的 Web 界面,支持批量生成、设置视频时长和横 / 竖屏尺寸等功能。来自 @jolahua 的分享 地址:github.com/harry0703/MoneyPrinterTurbo # Omost :极简提示词的文生图工具。 该项目基于 LLM 的编程能力帮用户自动完善文生图的提示词,可以根据用户输入的简短提示词生成高质量的图片,还支持图片局部修改等功能,比如将图片中的龙变成恐龙,极大地降低了编写文生图的门槛,无需复杂的提示词即可生成满意的图片。 地址:github.com/lllyasviel/Omost # Scrapegraph-ai :基于 AI 的 Python 爬虫。 这是一个由 AI 驱动的 Python 爬虫库,它借助 LLM 的能力,可以根据提示词自动抓取目标网站的数据。 smart_scraper_graph = SmartScraperGraph( prompt="List me all the projects with their descriptions", source="目标网站", config=graph_config ) result = smart_scraper_graph.run() print(result) 地址:github.com/VinciGit00/Scrapegraph-ai # 全平台通用的换源工具:chsrc 主语言:C 该项目能够为常见的 Linux 发行版、编程语言和软件切换至国内镜像源,操作简单仅需一条命令。它采用 C 语言编写,具有高效和轻量级的特点,支持测速、多平台以及项目级换源等功能,适用于优化下载速度或解决源受限的场景。 项目详情→hellogithub.com/repository/7666ba91e01e4a59be5809b02d9e8ff6 # 免费的可视化 Web 页面构建平台:GrapesJS 主语言:TypeScript 该项目通过直观的可视化界面,让用户能够通过拖拽的方式,快速设计和构建网站的 HTML 模板。它所见即所得、移动端适配,适用于官网、新闻和 CMS 等类型的网站。 项目详情→hellogithub.com/repository/572e31f5fc7541efb19c16d331796edf # 轻量级的 AI 证件照制作工具:HivisionIDPhotos 主语言:Python,Star:1.9k,周增长:1.5k 这是一款简单易用的 AI 证件照制作工具,能够生成标准证件照和六寸排版照。它提供了简洁的 Web 界面和 API 服务,即使在没有 GPU 的电脑上也能够运行,支持抠图、尺寸调整和自定义底色等功能。 GitHub 地址→github.com/Zeyi-Lin/HivisionIDPhotos # Git 技巧 1https://hellogithub.com/article/9aed28d4d64b4649bb364685ef557ae4 # 2.2 Windows 激活:Microsoft-Activation-Scripts 本周 star 增长数:1,100+ 有了 Microsoft-Activation-Scripts,激活 Windows 和 Office 不再是问题。它注重开源、减少反病毒软件的检测,这个用到 HWID、Ohook、KMS38、在线 KMS 激活方法的工具,一定能帮你解决 Windows 的激活问题。 GitHub 地址→github.com/massgravel/Microsoft-Activation-Scripts 17、source-code-hunter:Spring 全家桶源码解读。该项目提供了一系列互联网主流框架和中间件的源码讲解,包括 Spring 全家桶、Mybatis、Netty、Dubbo 等框架。 地址:https://github.com/doocs/source-code-hunter # 2.1 免费的 AI 图像升级器:Upscaler 主语言:TypeScript,Star:25k,周增长:1k 这是一款通过 AI 算法提高图像分辨率(超级分辨率,简称超分)的桌面工具,它免费、开源、无需联网、开箱即用,安装包大概 200+MB,需要有 GPU 的机器才能运行,适用于 Windows、Linux 和 macOS 系统。 GitHub 地址→github.com/upscayl/upscayl # 2.1 在线的数据库设计工具:DrawDB 主语言:JavaScript,Star:4.7k,周增长:3.8k 这个开源项目是一个免费、简单、强大的数据库实体关系(DBER)在线编辑器,无需注册即可直接在浏览器中使用。它提供了直观、可视化的操作界面,用户通过点击即可构建数据库表和导出建表语句,还可以导入建表语句,实现可视化编辑、错误检查等。支持 MySQL、PostgreSQL、SQLite、MariaDB、SQL Server 共 5 种常用的关系数据库。 GitHub 地址→github.com/drawdb-io/drawdb 5、WingetUI:带界面的 Windows 包管理器。该项目是一个为 Windows 常用的命令行包管理工具设计的用户界面,如 Winget、Scoop、Pip、NPM、.NET Tool 等。它的界面友好、设计美观、支持中文,通过它你可以轻松下载、安装、更新和卸载包管理器上发布的任何软件以及其它日常应用,如浏览器、PDF 阅读器等。 地址:github.com/marticliment/WingetUI 9、freeze:生成代码图片的终端工具。该项目可以将代码片段和终端输出,转换成 PNG、SVG 和 WebP 格式的图片,它采用 Go 语言开发,特点是安装简单和易于使用,支持一条命令生成图片,也可以通过交互模式生成定制的图片。 # macOS or Linux brew install charmbracelet/tap/freeze # Arch Linux (btw) pacman -S freeze # Nix nix-env -iA nixpkgs.charm-freeze 地址:github.com/charmbracelet/freeze # Java 项目 12、CompreFace:免费、开源的人脸识别系统。该项目提供了用于人脸识别、检测、验证、头部姿势检测、性别和年龄识别的 REST API 服务,不用懂机器学习就能轻松集成到任何系统中。它后端采用 Java 编写,人脸识别是基于 FaceNet 和 InsightFace 实现,同时支持 Docker 部署。 地址:github.com/exadel-inc/CompreFace 13、fdroidclient:免费、开源的 Android 应用商店。该项目是 F-Droid 的 Android 客户端,专门收集各类开源安卓软件(FOSS)的应用商店。它里面大部分是免费且无广告的应用,如遇到资源加载慢的情况,可通过设置镜像源解决。 地址:github.com/f-droid/fdroidclient 18、tailwind-landing-page-template:免费、开源的落地页模板。该项目是基于 TailwindCSS、React 和 Next.js 构建的落地页模板,它界面美观、代码简单、设计在线,适用于快速制作公司主页、活动落地页等。 git clone 项目 yarn install yarn dev # http://localhost:3000 地址:github.com/cruip/tailwind-landing-page-template 22、python-miio:用于控制小米智能家电的 Python 库。该项目提供了一个 Python 库和命令行工具,可以用来控制使用小米的 miIO 和 MIoT 协议的设备。借助它用户可以轻松地与小米智能设备进行通信和远程控制,包括扫地机器人、灯泡、空气净化器等,非常适合喜欢 DIY 智能家居系统的开发者。 地址:github.com/rytilahti/python-miio 23、undetected-chromedriver:绕过反爬检测的 Python 库。这是一个经过优化的 Selenium WebDriver 补丁,专门用于防止浏览器自动化过程中,触发反机器人机制。它能够隐藏浏览器特征(指纹),使用起来十分方便,就像一个 Python 的第三方库一样。 import undetected_chromedriver as uc driver = uc.Chrome(headless=True,use_subprocess=False) driver.get('https://nowsecure.nl') driver.save_screenshot('nowsecure.png') 地址:github.com/ultrafunkamsterdam/undetected-chromedriver 29、reminders-menubar:极简的 macOS 菜单栏提醒工具。这是一款使用 SwiftUI 开发的小工具,能够在 macOS 菜单栏查看 / 提醒待办事项。它体积小、交互简单、界面清爽,支持开机启动、多语言(包括中文)、菜单栏显示计数、快捷键等功能。 地址:github.com/DamascenoRafael/reminders-menubar # 32、ServiceLogos:超可爱的 Logo 集合。这里是用来存放 Sawaratsuki 制作的各种 logo 的仓库,这些 logo 制作精美、画风可爱,包括编程语言、框架、工具和各大社交媒体的商标™️。 地址:github.com/SAWARATSUKI/ServiceLogos 35、how-to-learn-robotics:机器人学自学指南。这本指南专为非科班的小伙伴而设计,旨在指导他们如何学习机器人学。它包含了必备知识、入门教材推荐、实践项目以及进阶方法等内容,帮助读者逐步成长为一名优秀的机器人工程师。 地址:github.com/qqfly/how-to-learn-robotics # 2.2 免费、开源的落地页模板:tailwind-landing-page-template 主语言:TypeScript 该项目是基于 TailwindCSS、React 和 Next.js 构建的落地页模板,它界面美观、代码简单、设计在线,适用于快速制作公司主页、活动落地页等。 项目详情→hellogithub.com/repository/9f205fad64b241609ce3feec456ab818 # C# 项目 3、RunCat_for_windows:在 Windows 任务栏飞奔的 “小猫”。这是一个用 C# 写的小工具,它会在 Windows 任务栏显示一只奔跑的小猫动画,CPU 使用率越高它跑得越快。 地址:github.com/Kyome22/RunCat_for_windows 9、mactop:专为苹果芯片打造的 Mac 性能监控工具。该项目用不到 1k 行的 Go 代码,实现了一个类似 top 命令的工具。它可以实时显示 Apple M 系列芯片的性能指标,包括 CPU、GPU 使用率、内存、网络和硬盘等信息。 地址:github.com/context-labs/mactop 11、superfile:非常漂亮的终端文件管理器。这是一个现代终端文件管理器,为命令行文件操作提供了一个直观且漂亮的界面。它默认采用 Vim 风格的快捷键操作,还支持插件和主题自定义。 地址:github.com/yorukot/superfile 2、chsrc:全平台通用的换源工具。该项目能够为常见的 Linux 发行版、编程语言和软件切换至国内镜像源,操作简单仅需一条命令。它采用 C 语言编写,具有高效和轻量级的特点,支持测速、多平台以及项目级换源等功能,适用于优化下载速度或解决源受限的场景。来自 @ccmywish 的分享 #chsrc 地址:github.com/RubyMetric/chsrc 6、lnav:强大的终端日志文件查看工具。这是一款用于查看和分析日志文件的轻量级工具。它无需配置、开箱即用,可自动识别日志格式并解压文件,支持同时处理多个文件和目录、实时更新、文本高亮、正则与 SQL 过滤日志等功能,特别适合在服务器和开发环境中使用。来自 @DeShuiYu 的分享 地址:github.com/tstack/lnav # 登录页面 HTML https://mp.weixin.qq.com/s?__biz=MzkyOTY0MTc2Mw==&mid=2247484615&idx=1&sn=ea7f0a83d59cbecd8d35bcc8593df965&chksm=c379bfb1c9e47cd5f6cb9f2f1549b14c7e6ae9d5df148869070d42df10a94ff5fc731bc5ea2b&scene=132&exptype=timeline_recommend_article_extendread_extendread_interest&show_related_article=1&subscene=132&scene=132#wechat_redirect # 在 Master 分之发生变化的时候,pull 一下代码 要在 Git 仓库的 master 分支发生变化时自动执行 git pull 操作,可以设置一个 Git hook 来触发这个动作。具体步骤如下: 安装 Git: 确保系统上已经安装了 Git。如果没有安装,可以运行以下命令: sudo yum install git 克隆仓库: 如果您还没有克隆仓库,请先克隆它: git clone https://github.com/username/repository.git /path/to/your/local/repo cd /path/to/your/local/repo 设置 Git hook: Git hooks 是一些脚本,在 Git 仓库中的特定事件发生时执行。我们可以使用 post-merge 和 post-receive hooks 来实现这个功能。 创建一个钩子脚本: vi /path/to/your/local/repo/.git/hooks/post-merge 添加以下内容: 1234#!/bin/bash # 切换到仓库目录 cd /path/to/your/local/repo # 执行 git pull git pull origin master 保存并关闭文件。 注意: 确保替换 /path/to/your/local/repo 为您的实际仓库路径。 为钩子脚本添加执行权限: 1chmod +x /path/to/your/local/repo/.git/hooks/post-merge # Github Hooks GitHooks","categories":[{"name":"use","slug":"use","permalink":"https://fairyeye.github.io/categories/use/"}],"tags":[{"name":"git","slug":"git","permalink":"https://fairyeye.github.io/tags/git/"}]},{"title":"GitHub Page","slug":"GitHub Page","date":"2023-06-05T06:10:07.408Z","updated":"2023-06-05T06:11:05.499Z","comments":true,"path":"2023/06/05/GitHub Page/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/GitHub%20Page/","excerpt":"","text":"123## 拉取子模块git submodule init // 初始化子模块git submodule update // 更新子模块与主仓库中的子模块代码同步","categories":[],"tags":[{"name":"hexo","slug":"hexo","permalink":"https://fairyeye.github.io/tags/hexo/"}]},{"title":"单例模式","slug":"单例模式","date":"2023-06-05T05:49:50.605Z","updated":"2023-06-05T05:49:50.605Z","comments":true,"path":"2023/06/05/单例模式/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/","excerpt":"","text":"# 1. 最简单的单例模式: 123456789101112131415161718192021222324252627282930package com.example.demo.JUC.thread;/** * @author huapeng.zhang * @version 1.0 * @date 2020/9/17 18:28 */public class SingletomDemo { private static SingletomDemo singletomDemo = null; private SingletomDemo() { System.out.println(Thread.currentThread().getName() + "\\t 我是构造方法SingletomDemo()!"); } public static SingletomDemo getInstance() { if (singletomDemo == null) { singletomDemo = new SingletomDemo(); } return singletomDemo; } public static void main(String[] args) { // 单线程 System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance()); System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance()); System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance()); }} 在单线程的情况下,打印结果如下: 可以看到,虽然我们一共调用了六次 getInstance() , 但是只打印了一次构造方法输出内容,也就是只调用了一个构造函数,所获得的对象地址自然是一样的。 # 2. 多线程下的单例模式 我们对 main() 方法做一下改造,改造后的代码如下: 12345678public static void main(String[] args) { // 改为多线程后 可能多次调用构造函数 for (int i = 0; i < 10; i++) { new Thread(() -> { SingletomDemo.getInstance(); }, String.valueOf(i)).start(); }} 打印结果为: 多次执行下可以看到打印的次数是不同的。 可以对 getInstance() 方法添加 synchronized 加锁,保证只生成一个实例。 123456private static synchronized SingletomDemo getInstance() { if (singletomDemo == null) { singletomDemo = new SingletomDemo(); } return singletomDemo;} 再次执行程序发现只打印了一次构造方法输出函数,还有另外一种方法就是 DCL:double check locks双端检测模式 模式也可以达到同样的目的。 # 3.DCL + 单例模式 我们再次对 getInstance() 方法进行改造,代码如下: 1234567891011// DCL模式(double check locks双端检测模式)private static SingletomDemo getInstance() { if (singletomDemo == null) { synchronized (SingletomDemo.class) { if (singletomDemo == null) { singletomDemo = new SingletomDemo(); } } } return singletomDemo;} 当我们的单例模式写到这种程度的时候,基本可以应对 99% 的情况,但是由于 指令排序 的存在,还是有可能会出现问题。 123memory = allocate(); //1.分配对象内存空间instance(memory); //2.初始化对象instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=null 步骤 2、3 不存在数据依赖,所以由于指令重排的关系,可能会出现: 123memory = allocate(); //1.分配对象内存空间instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=nullinstance(memory); //2.初始化对象 # 4.Volatile + 单例模式 我们在声明 singletomDemo 时,加上 Volatile 关键字修饰,就可以达到完美的效果。 最终代码: 1234567891011121314151617181920212223242526272829303132333435363738394041424344package com.example.demo.JUC.thread;/** * @author huapeng.zhang * @version 1.0 * @date 2020/9/17 18:28 */public class SingletomDemo { private static volatile SingletomDemo singletomDemo = null; private SingletomDemo() { System.out.println(Thread.currentThread().getName() + "\\t 我是构造方法SingletomDemo()!"); } // DCL模式(double check locks双端检测模式) private static SingletomDemo getInstance() { if (singletomDemo == null) { synchronized (SingletomDemo.class) { if (singletomDemo == null) { singletomDemo = new SingletomDemo(); } } } return singletomDemo; } public static void main(String[] args) { // 单线程// System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance());// System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance());// System.out.println(SingletomDemo.getInstance() == SingletomDemo.getInstance());// 改为多线程后 可能多次调用构造函数// 可以在 getInstance上加SYNC解决问题 for (int i = 0; i < 10; i++) { new Thread(() -> { SingletomDemo.getInstance(); }, String.valueOf(i)).start(); } }}","categories":[{"name":"学习笔记","slug":"学习笔记","permalink":"https://fairyeye.github.io/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"}],"tags":[{"name":"demo","slug":"demo","permalink":"https://fairyeye.github.io/tags/demo/"}]},{"title":"MySQL查询","slug":"同步不同服务的MySQL数据表","date":"2023-06-05T05:49:50.605Z","updated":"2023-06-05T05:49:50.605Z","comments":true,"path":"2023/06/05/同步不同服务的MySQL数据表/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E5%90%8C%E6%AD%A5%E4%B8%8D%E5%90%8C%E6%9C%8D%E5%8A%A1%E7%9A%84MySQL%E6%95%B0%E6%8D%AE%E8%A1%A8/","excerpt":"","text":"# 1. 首先要保证本地的 MySQL 服务支持 FEDERATED 引擎。 12345输入:SHOW ENGINES;如下 FEDERATED 行的Support为YES则表示开启了FEDERATED。如果为 NO 则表示未开启。如果 FEDERATED 没有开启的话 要启用。 # 2. 启用 FEDERATED 123在MySQL的配置文件安装目录下的my.ini或my.cnf 加上 一行 `FEDERATED` # 3. 建表 建表一般应的是 InnoDB 引擎,这里需要修改一下 改为 ENGINE=FEDERATED 从原表导出表结构 SQL,然后修改 ENGINE = InnoDB -> ENGINE=FEDERATED CONNECTION='mysql://用户名:密码@IP:PORT/库名/表名' 然后打开这张表,可以看到远程表里的数据已经同步到了这张表。","categories":[{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"}],"tags":[{"name":"utils","slug":"utils","permalink":"https://fairyeye.github.io/tags/utils/"}]},{"title":"Transactional注解","slug":"事务问题","date":"2023-06-05T05:49:50.604Z","updated":"2023-06-05T05:49:50.604Z","comments":true,"path":"2023/06/05/事务问题/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E4%BA%8B%E5%8A%A1%E9%97%AE%E9%A2%98/","excerpt":"","text":"方法 A 中调用 B 后再调用 C, 三个方法中均有插入数据后再查询数据 B 方法的注解:@Transactional (propagation=Propagation.REQUIRES_NEW) -- 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 C 方法的注解:@Transactional (propagation=Propagation.REQUIRED) -- 如果有事务,那么加入事务,没有的话新建一个 1、当方法 B 或者 C 报错时,A、B、C 方法都回滚 2、当方法 A、B、C 异常被捕获时,A、B、C 事务不回滚(a、b、c 均入库) 3、当方法 BC 执行后,方法 A 报错时,A、C 事务回滚,B 事务不回滚(b 数据入库) A 方法向表中添加数据 a,在查询该表能得到数据 a B 方法向表中添加数据 b,再查询该表能得到数据 b C 方法向表中添加数据 c,再查询该表能得到数据 a,b,c 带有注解 @Transactional (propagation=Propagation.REQUIRES_NEW) 的方法走完之后,数据就会被提交入库 另外需要注意方法内部调用 @Transactional (propagation=Propagation.REQUIRES_NEW) 注解不生效,比如 A 和 B 都在同一个方法中,A 调用 B,B 方法是此注解,则不生效。同样,AOP 拦截也拦截不到 B 12345678910111213141516171819public enum Propagation { REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6); private final int value; private Propagation(int value) { this.value = value; } public int value() { return this.value; }} 七种事物传播类型 默认为: REQUIRED","categories":[{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"}],"tags":[]},{"title":"Sql小技巧","slug":"分页查询","date":"2023-06-05T05:49:50.604Z","updated":"2023-06-05T05:49:50.605Z","comments":true,"path":"2023/06/05/分页查询/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E5%88%86%E9%A1%B5%E6%9F%A5%E8%AF%A2/","excerpt":"","text":"MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行。 123456789# 反例(耗时129.570s)select * from task_result LIMIT 20000000, 10;# 正例(耗时5.114s)SELECT a.* FROM task_result a, (select id from task_result LIMIT 20000000, 10) b where a.id = b.id;# 说明task_result表为生产环境的一个表,总数据量为3400万,id为主键,偏移量达到2000万 # 获取一条数据时的 Limit 1 在很多情况下我们已知数据仅存在一条,此时我们应该告知数据库只用查一条,否则将会转化为全表扫描 123456789# 反例(耗时2424.612s)select * from task_result where unique_key = 'ebbf420b65d95573db7669f21fa3be3e_861414030800727_48';# 正例(耗时1.036s)select * from task_result where unique_key = 'ebbf420b65d95573db7669f21fa3be3e_861414030800727_48' LIMIT 1;# 说明task_result表为生产环境的一个表,总数据量为3400万,where条件非索引字段,数据所在行为第19486条记录 # 批量插入 1234567891011# 反例INSERT into person(name,age) values('A',24)INSERT into person(name,age) values('B',24)INSERT into person(name,age) values('C',24)# 正例INSERT into person(name,age) values('A',24),('B',24),('C',24);# 说明比较常规,就不多做说明了 https://juejin.im/post/5ea16dede51d45470b4ffc5b?utm_source=gold_browser_extension","categories":[{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"}],"tags":[{"name":"mysql","slug":"mysql","permalink":"https://fairyeye.github.io/tags/mysql/"}]},{"title":"Springboot","slug":"Springboot返回html","date":"2023-06-05T05:49:50.603Z","updated":"2023-06-05T06:02:36.940Z","comments":true,"path":"2023/06/05/Springboot返回html/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Springboot%E8%BF%94%E5%9B%9Ehtml/","excerpt":"","text":"# Springboot 返回 html 注:Springboot 的版本 2.1.3.RELEASE List-1 application.properties 文件 1234567server.port=8080#url中,项目的前缀server.servlet.context-path=/projectspring.mvc.view.prefix=/spring.mvc.view.suffix=.html 整体结构如下图 1 所示,html 要放在 static 下,不是 templates 下 图1 List-2 HelloController 的内容如下 1234567891011121314import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Slf4j@Controllerpublic class HelloController { @RequestMapping(value = "/hello") public String index() { log.info("收到请求"); return "html/hello"; }} List-3 启动 springboot,之后在浏览器中输入 12345#返回index.html的内容http://localhost:8080/project/#返回hello.html的内容http://localhost:8080/project/hello 网上很多关于模板的(Thymeleaf 、FreeMarker 等),但是我不需要,我只需要纯的 html。 index.html 是 springboot 的默认 welcome page。 # Reference https://www.jianshu.com/p/eb4c0fc2dfc4 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-welcome-page (adsbygoogle = window.adsbygoogle || []).push({});","categories":[],"tags":[{"name":"基础","slug":"基础","permalink":"https://fairyeye.github.io/tags/%E5%9F%BA%E7%A1%80/"}]},{"title":"StringToInteger","slug":"StringToInteger","date":"2023-06-05T05:49:50.603Z","updated":"2023-06-05T05:49:50.603Z","comments":true,"path":"2023/06/05/StringToInteger/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/StringToInteger/","excerpt":"","text":"# String 转 Integer # 1.Integer 的 parseInt 方法 12345String string = "123";int value = Integer.parseInt(string);System.out.println("stringToInt---------->"+value);结果:StringToInt---------->123 # 2.Integer 的 valueOf 方法 12345String string = "123";Integer value = Integer.valueOf(string);System.out.println("stringToInt1---------->"+value);结果:StringToInt1---------->123 以上两种方法都是可行的。 但是会有特殊的情况,比如: 1234567891011String string = "abc";int value = Integer.parseInt(string);System.out.println("stringToInt---------->"+value);结果:Exception in thread "main" java.lang.NumberFormatException: For input string: "abc" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at com.hand.todo.infra.util.StringToInt.stringToInt(StringToInt.java:16) at com.hand.todo.infra.util.StringToInt.main(StringToInt.java:10) 使用 stringToInt1 () 也会返回同样的错误。 123public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10));} valueOf () 调用了 parseInt (),所以返回同样的错误是正常的。 按照正常的逻辑来说,我们需要对这个错误进行处理,比如: 1234567891011121314151617String string = "abc";int value = 0;try { value = Integer.parseInt(string);} catch (NumberFormatException e) { e.printStackTrace();}System.out.println("StringToInt---------->"+value);结果:java.lang.NumberFormatException: For input string: "abc" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at com.hand.todo.infra.util.StringToInt.stringToInt(StringToInt.java:18) at com.hand.todo.infra.util.StringToInt.main(StringToInt.java:10)StringToInt---------->0 可以看到,虽然依然报错,但是程序还是继续执行了。 然后回到主题。 # 3.Guava 的 Ints 结合 Java8 的 Optional 1234567String string = "abc";Integer value = Optional.ofNullable(string) .map(Ints::tryParse) .orElse(0); System.out.println("StringToInt2---------->" + value); 结果:StringToInt2---------->0 可以在转换失败的时候为他设置默认值: 1234567String string = "abc";Integer value = Optional.ofNullable(string) .map(Ints::tryParse) .orElse(100); System.out.println("StringToInt2---------->" + value); 结果:StringToInt2---------->100 写在末尾:之所以写这么详细是因为最开始的时候我很菜,看别人写的文档都很简单,虽然可以解决问题,但是却不解其意,所以写的啰嗦一点,对新手比较友好。 </body> </html>","categories":[{"name":"java","slug":"java","permalink":"https://fairyeye.github.io/categories/java/"}],"tags":[]},{"title":"需要掌握以下知识","slug":"需要掌握以下知识","date":"2023-06-05T05:49:50.603Z","updated":"2023-09-18T08:58:30.140Z","comments":true,"path":"2023/06/05/需要掌握以下知识/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1%E4%BB%A5%E4%B8%8B%E7%9F%A5%E8%AF%86/","excerpt":"","text":"导入数据 匹配规则 3. 生成报告和数据 作为一名三年经验的 Java 开发人员,你需要掌握以下知识: Java 基础知识:掌握 Java 的基本语法、面向对象编程、常用数据结构和算法等,能够熟练使用 Java 语言进行开发。 Java Web 开发框架:熟悉 Spring、SpringMVC 和 MyBatis 等主流的 Java Web 开发框架,能够使用这些框架进行 Web 应用程序的开发。 数据库知识:熟悉关系型数据库和 NoSQL 数据库的使用,掌握 SQL 语言和数据库设计的基本知识。 Web 前端技术:熟悉 HTML、CSS、JavaScript、jQuery 等前端开发技术,能够与前端开发人员协同工作,完成 Web 应用程序的前后端分离开发。 网络编程知识:熟悉 TCP/IP 协议、HTTP 协议等网络编程相关的知识,能够使用 Java 进行网络编程开发。 分布式系统开发:了解分布式系统的基本概念和原理,熟悉 Spring Cloud、Dubbo 等分布式系统开发框架,能够进行分布式系统的设计和开发。 微服务架构:了解微服务架构的基本概念和原理,熟悉 Spring Boot、Spring Cloud 等微服务开发框架,能够进行微服务的设计和开发。 工具和技术:掌握 Maven、Git、Jenkins 等常用的工具和技术,能够进行代码管理、构建和部署。 技术栈扩展:根据自身的兴趣和职业发展需求,了解其他的 Java 技术栈,例如大数据处理、人工智能、区块链等,不断扩展自己的技术领域。 除了以上技术知识,你还需要具备良好的团队协作能力、学习能力和解决问题的能力,能够快速适应不同的项目需求和开发环境,不断提升自己的技术水平和职业素养。","categories":[],"tags":[{"name":"学习","slug":"学习","permalink":"https://fairyeye.github.io/tags/%E5%AD%A6%E4%B9%A0/"}]},{"title":"List stream","slug":"Java 8 Stream","date":"2023-06-05T05:49:50.602Z","updated":"2023-06-05T05:49:50.602Z","comments":true,"path":"2023/06/05/Java 8 Stream/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Java%208%20Stream/","excerpt":"","text":"通过 workOrederNum 字段排序 1List<ProduceOrderDTO> collect = list.stream().sorted(Comparator.comparing(ProduceOrderDTO::getWorkOrderNum)).collect(Collectors.toList()); 查出 InstructionPoDTO 中 InstructionDocNum 作为 list 12// 筛选出不重复的 instructionDocNumList<String> instructionDocNums = read.stream().map(InstructionPoDTO::getInstructionDocNum).distinct().collect(Collectors.toList()); 筛选所有 instructionDocNum = 123 的对象 12List<InstructionPoDTO> instructionPoDTOS = read.stream().filter(instructionPoDTO -> instructionPoDTO.getInstructionDocNum().equals("123")).collect(Collectors.toList()); 查询出第一个 1String instructionDocId = instructionDocs.stream().findFirst().get(); 设置 typeCode 为 key, list 的对象为 value 1Map<String, MtGenType> typesMap = types.stream().collect(Collectors.toMap(t -> t.getTypeCode(), t -> t)); // 通过字段去重 1instructionSapStockDTOS.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()-> new TreeSet<>(Comparator.comparing(o -> o.getMaterial()+";"+o.getPlant()+";"+o.getStorage()+";"+o.getBatch()+";"+o.getSpecialStock()))), ArrayList::new)); GROUP BY 123456789101112131415161718192021222324252627Map<String, List<WmsIqcRecord>> map = records.stream().collect(Collectors.groupingBy(iqcRecord -> { if (StringUtils.equals("TO_DO", iqcRecord.getStatus())) { switch (iqcRecord.getDealMethod()) { case "RELEASE": return "TO_RELEASE"; case "FREEZE": return "TO_FREEZE"; case "RETURN": return "TO_RETURN"; default: return "TO_DO"; } } else if (StringUtils.equals("DONE", iqcRecord.getStatus())) { switch (iqcRecord.getDealMethod()) { case "RELEASE": return "RELEASE_DONE"; case "FREEZE": return "FREEZE_DONE"; case "RETURN": return "RETURN_DONE"; default: return "DONE"; } } else { return "DEAL"; } })); 1234567// List<LocalDate> collect = localDates.stream().sorted((var1, var2) -> {// if (var1.isBefore(var2)) {// return -1;// }// return 1;// }).collect(Collectors.toList());// System.out.println(localDates.stream().sorted().collect(Collectors.toList()));","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://fairyeye.github.io/tags/Java/"}]},{"title":"MySQL","slug":"MySQL","date":"2023-06-05T05:49:50.602Z","updated":"2023-07-20T03:25:07.132Z","comments":true,"path":"2023/06/05/MySQL/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/MySQL/","excerpt":"","text":"123456789101112SELECT swid.OPERATION, swid.DELIVERY_METHOD, GROUP_CONCAT( DISTINCT( swid.WORK_ORDER_ID ) ) WORK_ORDER_ID FROM SSME_WO_ISSUE_DETAIL swid WHERE swid.TENANT_ID = 4 AND swid.WORK_ORDER_ID IN ( '42717.1','42719.1' ) GROUP BY swid.OPERATION, swid.DELIVERY_METHOD https://www.cnblogs.com/minqiliang/p/16577102.html https://blog.csdn.net/z15711187787/article/details/124986309 https://blog.csdn.net/weixin_45994575/article/details/123071909?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-123071909-blog-123821186.t0_edu_mix&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-123071909-blog-123821186.t0_edu_mix&utm_relevant_index=1 部署记录 # JDBCTemplete 批处理 123456789101112131415161718// sql语句String sql = "INSERT INTO ssme_iqc_change(TENANT_ID, KID) VALUE (?, ?);";List<String> kids = new ArrayList<>();// BatchPreparedStatementSetter 通过循环取出数据填充到SQL中jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setLong(1, 4L); ps.setString(2, kids.get(i)); } @Override public int getBatchSize() { return kids.size(); } }); # MySQL 数据删除恢复 123456# 数据库中运行# 判断binlog是否开启show variables like '%log_bin%';# 找到数据库data位置show variables like 'datadir'; data 目录下数据 找到当天的 binlog 12# 转换为SQL 只转换操作时间内的数据即可root@VM-4-10-ubuntu:/var/lib/mysql# mysqlbinlog --base64-output=decode-rows -v --database=smart-admin-dev --start-datetime="2022-11-28 10:50:00" --stop-datetime="2022-11-28 11:00:00" binlog.000018 > 000018.sql 000018.sql 12# 将SQL转换为insert语句root@VM-4-10-ubuntu:/var/lib/mysql# cat 000018.sql | sed -n '/###/p' | sed 's/### //g;s/\\/\\*.*/,/g;s/DELETE FROM/;INSERT INTO/g;s/WHERE/SELECT/g;' |sed -r 's/(@17.*),/\\1;/g' | sed 's/@1=//g'| sed 's/@[1-9]=/,/g' | sed 's/@[1-9][0-9]=/,/g' > 000018OK.sql 000018OK.sql 最后将数据导入数据库即可。 # Group 123-- 修改 group_concat 长度限制SET GLOBAL group_concat_max_len=102400;SET SESSION group_concat_max_len=102400; # Mapper 空指针 count sql resultType 为对象,导致报错 # Waiting for table metadata lock https://blog.csdn.net/jianlong727/article/details/111877226","categories":[{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"}],"tags":[]},{"title":"Nacos","slug":"Nacos","date":"2023-06-05T05:49:50.602Z","updated":"2023-06-05T05:49:50.602Z","comments":true,"path":"2023/06/05/Nacos/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Nacos/","excerpt":"","text":"# 配置中心 # 1. 添加依赖 12345<dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-config-spring-boot-starter</artifactId> <version>0.2.1</version> </dependency> # 2. 启动类注解 @NacosPropertySource 12345678910@SpringBootApplication // dataId 对应配置管理-配置列表-dataId@NacosPropertySource(dataId = "nacosDemo", autoRefreshed = true) public class NacosDemoApplication { public static void main(String[] args) { SpringApplication.run(NacosDemoApplication.class, args); } } # 3. 配置 nacos 地址 123spring: application: name: nacos-demo # 4. 使用配置 123456789101112131415@RestController public class CacheController { @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true) private boolean useLocalCache; private static final String template = "useLocalCache is %s!"; @GetMapping("/cache") public String cache() { // 默认返回false return String.format(template, useLocalCache); } }// 此时返回结果 :useLocalCache is false! 增加配置并发布 1// 返回结果 :useLocalCache is true!","categories":[{"name":"中间件","slug":"中间件","permalink":"https://fairyeye.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"}],"tags":[]},{"title":"Delete/Truncate/Drop","slug":"Delete & Truncate","date":"2023-06-05T05:49:50.601Z","updated":"2023-06-05T05:49:50.601Z","comments":true,"path":"2023/06/05/Delete & Truncate/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Delete%20&%20Truncate/","excerpt":"","text":"假如把一张表比作一间教室,数据比作学生。 drop 就是把这件教师炸掉了,教室都没了,更不用说数据了。 truncate 就是把学生开除了,离开了,再也没有了。 delete 就是学生出去了,虽然他们可能不会回来了,但是位置还是要留着。 # Drop 直接删掉表,这个没什么好说的。 包括表结构,表数据,全部删除,占用的空间也会释放。 # Truncate 截断表,会释放空间。 # Delete 删除数据","categories":[],"tags":[{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"}]},{"title":"","slug":"FileUtils","date":"2023-06-05T05:49:50.601Z","updated":"2023-06-05T05:49:50.601Z","comments":true,"path":"2023/06/05/FileUtils/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/FileUtils/","excerpt":"","text":"# title: 工具类 date: 2020-04-20 11:00:50 categories: "工具类" # 数组转文件 123456789101112131415161718192021222324252627282930313233343536373839/** * @param bfile * @param filePath * @param fileName * * 根据byte数组,生成文件 */public static void getFile(byte[] bfile, String filePath,String fileName) { BufferedOutputStream bos = null; FileOutputStream fos = null; File file = null; try { File dir = new File(filePath); if(!dir.exists()&&dir.isDirectory()){//判断文件目录是否存在 dir.mkdirs(); } file = new File(filePath+"\\\\"+fileName); fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos); bos.write(bfile); } catch (Exception e) { e.printStackTrace(); } finally { if (bos != null) { try { bos.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e1) { e1.printStackTrace(); } } }}","categories":[],"tags":[]},{"title":"WHEN YOU HAVE A NEW PC","slug":"WHEN YOU HAVE A NEW PC","date":"2023-06-05T05:49:50.000Z","updated":"2024-03-29T07:24:23.791Z","comments":true,"path":"2023/06/05/WHEN YOU HAVE A NEW PC/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/WHEN%20YOU%20HAVE%20A%20NEW%20PC/","excerpt":"","text":"# <center> WHEN YOU HAVE A NEW PC</center> # Java 环境配置 # 1. 安装 提前新建两个文件夹,jdk,jre,默认路径也可以 安装 jdk-8u111-windows-x64.exe 设置 jdk、jre 的路径 # 2. 配置环境变量 右键此电脑 -> 属性 -> 高级系统设置 -> 环境变量 -> 系统变量 (S) 只有系统变量才是全局的。 123456新建 CLASSPATH .;新建 JAVA_HOME jdk路径修改 path 添加 %JAVA_HOME%\\bin;测试 java -version javac -version # Maven 环境配置 解压 apache-maven-3.3.9.rar 到指定的目录。 修改配置文件(压缩包已修改过)。 # GIT 工具 # 1. 配置用户信息 12345678# 用户级git config --global user.name "fairy"git config --global user.email "[email protected]"# 仓库级 local可以省略git config --local user.name "张华朋26190"git config --local user.email "[email protected]"# 使配置不生效git config --global --unset user.name # 2. 生成 key 1ssh-keygen -t rsa -C 'email' // Email可选,会在key中生成你的邮箱信息 一直回车就行 生成的 key 文件 C:\\Users\\你的用户名\\.ssh\\id_rsa.pub 用文本编辑器打开,复制到 GIT 上。 # 3. 上传项目到 GIT 123456cd 项目文件加夹git initgit remote add origin [email protected]:fairyeye/StudyJava.gitgit add .git commit -m "Initial commit"git push -u origin master # MySQL 安装 安装 https://www.runoob.com/mysql/mysql-install.html 修改初始密码 MySQL 版本 5.7.6 版本以前用户可以使用如下命令: 1mysql> SET PASSWORD = PASSWORD('your pwd'); MySQL 版本 5.7.6 版本开始的用户可以使用如下命令: 1mysql> ALTER USER USER() IDENTIFIED BY 'your pwd'; 登录报错 https://www.cnblogs.com/lifan1998/p/9177731.html # 去图标 管理员运行 去图标.bat 文件。 # 谷歌访问助手 谷歌浏览器 -> 更多工具 -> 扩展程序 -> 开发者模式 (打开) 。 拖动 谷歌访问助手.crx 到谷歌浏览器中。 # 软件安装清单 IDEA: Chrome: uTools: Typora: Another Redis Desktop Manager:很好看的 Redis 客户端 XShell:好看 好用 tabby-terminal:https://github.com/Eugeny/tabby/releases/tag/v1.0.164 Shadowsocks:小飞机 Windows Termial:Windows 终端 nvm: Windows 切换 node 版本 n:Mac 切换 node 版本 https://github.com/tj/n npm install -g n pyenv:切换 python 版本 Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1" Redis 图形化页面: Tiny RDM 以下是如何在 Linux 或 macOS 上创建和使用虚拟环境的步骤: 打开终端。 使用 python3 -m venv path/to/venv 命令创建一个新的虚拟环境,其中 path/to/venv 是你想要创建虚拟环境的目录。例如,你可以在家目录下创建一个名为 myenv 的虚拟环境,使用命令 python3 -m venv ~/myenv 。 激活虚拟环境。在 Linux 或 macOS 上,你可以使用以下命令激活虚拟环境: 复制 1source ~/myenv/bin/activate 激活后,你的命令行提示符会改变,通常前面会加上虚拟环境的名字,比如 (myenv) 。 在虚拟环境中,你可以使用 pip 安装、升级或删除包,而不会影响到系统级别的 Python 环境。例如,你可以使用以下命令安装一个包: 复制 1pip install package-name 当你完成工作后,你可以通过运行 deactivate 命令来停用虚拟环境。 请注意,如果你的系统上没有安装 python3 ,或者你的 Python 版本不同,你可能需要根据你的实际情况调整上述命令。如果你使用的是 Windows 系统,步骤会有所不同,通常你会使用 path\\to\\venv\\Scripts\\activate 来激活虚拟环境 # Mac # 终端: # oh my zsh 12https://zhuanlan.zhihu.com/p/550022490https://blog.csdn.net/weixin_42326144/article/details/121957795 # IDEA 1234567891011121314151617在 IntelliJ IDEA 中,要确保项目中的中文字符以 UTF-8 编码保存,可以按照以下步骤操作:1. **打开项目设置**:在 IntelliJ IDEA 中,点击菜单栏中的 "File" -> "Settings"(或者直接使用快捷键 `Ctrl + Alt + S`)来打开项目设置。 2. **设置文件编码**:在设置窗口左侧的搜索框中输入 "File Encoding",然后选择 "File Encodings" 选项。 3. **更改默认编码**:在右侧的界面中,找到 "Global Encoding" 下的 "IDE Encoding",将其设置为 "UTF-8"。同样,也可以设置 "Project Encoding" 和 "Default encoding for properties files" 为 "UTF-8"。 4. **应用更改并重新加载项目**:点击 "OK" 按钮以应用更改,并可能需要重新加载项目以确保更改生效。 5. **保存文件为 UTF-8 编码**:对于现有的文件,可以通过以下方式将其保存为 UTF-8 编码: - 打开文件。 - 点击菜单栏中的 "File" -> "Save with Encoding..."。 - 在弹出的对话框中,选择 "UTF-8" 编码,然后点击 "OK" 保存文件。通过以上步骤,您可以确保在 IntelliJ IDEA 中创建和编辑的所有文件都以 UTF-8 编码保存,包括中文字符。","categories":[{"name":"杂谈","slug":"杂谈","permalink":"https://fairyeye.github.io/categories/%E6%9D%82%E8%B0%88/"}],"tags":[]},{"title":"反射","slug":"反射获取父类字段","date":"2023-06-05T01:28:10.956Z","updated":"2023-06-05T01:28:10.956Z","comments":true,"path":"2023/06/05/反射获取父类字段/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/%E5%8F%8D%E5%B0%84%E8%8E%B7%E5%8F%96%E7%88%B6%E7%B1%BB%E5%AD%97%E6%AE%B5/","excerpt":"","text":"1234Class<?> superclass = itfBaseBO.getClass();while (!superclass.getName().equals(ExpandDomain.class.getName())) { superclass = superclass.getSuperclass();}","categories":[{"name":"学习笔记","slug":"学习笔记","permalink":"https://fairyeye.github.io/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"}],"tags":[{"name":"demo","slug":"demo","permalink":"https://fairyeye.github.io/tags/demo/"}]},{"title":"Jmeter","slug":"Jmeter简单使用","date":"2023-06-05T01:28:10.950Z","updated":"2023-06-05T06:02:13.637Z","comments":true,"path":"2023/06/05/Jmeter简单使用/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Jmeter%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/","excerpt":"","text":"","categories":[],"tags":[{"name":"软件","slug":"软件","permalink":"https://fairyeye.github.io/tags/%E8%BD%AF%E4%BB%B6/"}]},{"title":"Navicat数据库导出链接密码解析","slug":"Mac 数据库忘记密码","date":"2023-06-05T01:28:10.950Z","updated":"2023-06-05T01:28:10.950Z","comments":true,"path":"2023/06/05/Mac 数据库忘记密码/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Mac%20%E6%95%B0%E6%8D%AE%E5%BA%93%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81/","excerpt":"","text":"参考:https://blog.csdn.net/harris_lele/article/details/123588127 导出来,然后用 php 解析密码即可; 在线运行 php: https://www.toolfk.com/tools/online-runphp.html 运行代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141<?phpclass NavicatPassword{ protected $version = 0; protected $aesKey = 'libcckeylibcckey'; protected $aesIv = 'libcciv libcciv '; protected $blowString = '3DC5CA39'; protected $blowKey = null; protected $blowIv = null; public function __construct($version = 12) { $this->version = $version; $this->blowKey = sha1('3DC5CA39', true); $this->blowIv = hex2bin('d9c7c3c8870d64bd'); } public function encrypt($string) { $result = FALSE; switch ($this->version) { case 11: $result = $this->encryptEleven($string); break; case 12: $result = $this->encryptTwelve($string); break; default: break; } return $result; } protected function encryptEleven($string) { $round = intval(floor(strlen($string) / 8)); $leftLength = strlen($string) % 8; $result = ''; $currentVector = $this->blowIv; for ($i = 0; $i < $round; $i++) { $temp = $this->encryptBlock($this->xorBytes(substr($string, 8 * $i, 8), $currentVector)); $currentVector = $this->xorBytes($currentVector, $temp); $result .= $temp; } if ($leftLength) { $currentVector = $this->encryptBlock($currentVector); $result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector); } return strtoupper(bin2hex($result)); } protected function encryptBlock($block) { return openssl_encrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING); } protected function decryptBlock($block) { return openssl_decrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING); } protected function xorBytes($str1, $str2) { $result = ''; for ($i = 0; $i < strlen($str1); $i++) { $result .= chr(ord($str1[$i]) ^ ord($str2[$i])); } return $result; } protected function encryptTwelve($string) { $result = openssl_encrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv); return strtoupper(bin2hex($result)); } public function decrypt($string) { $result = FALSE; switch ($this->version) { case 11: $result = $this->decryptEleven($string); break; case 12: $result = $this->decryptTwelve($string); break; default: break; } return $result; } protected function decryptEleven($upperString) { $string = hex2bin(strtolower($upperString)); $round = intval(floor(strlen($string) / 8)); $leftLength = strlen($string) % 8; $result = ''; $currentVector = $this->blowIv; for ($i = 0; $i < $round; $i++) { $encryptedBlock = substr($string, 8 * $i, 8); $temp = $this->xorBytes($this->decryptBlock($encryptedBlock), $currentVector); $currentVector = $this->xorBytes($currentVector, $encryptedBlock); $result .= $temp; } if ($leftLength) { $currentVector = $this->encryptBlock($currentVector); $result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector); } return $result; } protected function decryptTwelve($upperString) { $string = hex2bin(strtolower($upperString)); return openssl_decrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv); }}; //需要指定版本两种,11或12//$navicatPassword = new NavicatPassword(11);$navicatPassword = new NavicatPassword(12); //解密//$decode = $navicatPassword->decrypt('15057D7BA390');$decode = $navicatPassword->decrypt('75008D0AE102C19EE3767E201AC9E4D2');echo $decode."\\n";?>","categories":[{"name":"实用","slug":"实用","permalink":"https://fairyeye.github.io/categories/%E5%AE%9E%E7%94%A8/"}],"tags":[{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"}]},{"title":"Ubuntu","slug":"Ubuntu","date":"2023-06-05T01:28:10.000Z","updated":"2024-06-12T01:54:04.756Z","comments":true,"path":"2023/06/05/Ubuntu/","link":"","permalink":"https://fairyeye.github.io/2023/06/05/Ubuntu/","excerpt":"","text":"# NETDATA # 1. 安装编译环境 1sudo apt install zlib1g-dev gcc make git autoconf autogen automake pkg-config uuid-dev # 2. 克隆项目 1git clone https://github.com/firehol/netdata.git --depth=1 # 3. 安装 netdata 123456https://www.cnblogs.com/beile/p/12875395.html官方:https://learn.netdata.cloud/docs/agent/packaging/installer/methods/offline./netdata-installer.sh 然后访问 IP:19999 # NGINX # 1. 安装 Nginx 12345678sudo suapt-get install nginx如果是Centos 浏览器地址栏输入 IP 看到如下页面表示已经安装好了 Nginx,如果有域名,输入域名也是同样的效果(前提是已经给域名添加了解析) # 2.Nginx 转发端口 我已经在服务器上安装了 netdata 服务,端口为 19999 , 但是通过 Nginx 访问服务器时默认是 80 端口,所以需要做一些配置,在我们输入域名的时候访问不同的端口。 123456789101112131415161718192021# nginx.conf里包含 include /etc/nginx/conf.d/*.conf; 所以可以在/etc/nginx/conf.d 文件夹下新增一个配置文件server { listen 80; server_name IP; location / { client_max_body_size 3000m; proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:19999; proxy_redirect default; proxy_connect_timeout 3000; }} 这样 当我们在浏览器地址栏输入 域名时 就会自动跳转到 netdata 的主页 # 3. 反向代理 + 负载均衡 => 12345678910111213141516171819202122# 实际服务upstream web_servers { server 192.168.139.128:9001; server 192.168.139.128:9002;}server { # 代理端口 listen 10086; server_name 192.168.139.128; location / { proxy_pass http://web_servers; proxy_set_header Host $host:$server_port; }}# 可以部署两个服务 9001、9002 发布时,等上一个成功发布后,启动第二个。# 未验证# 加上systemclt管理Java服务# 负载均衡的方式:https://mp.weixin.qq.com/s/yJyEwPkLD0V9G0451gbZYg# 1.轮询;2.权重;3.ip_hash;4.fair;5.url_hash # 时区 - https://blog.csdn.net/weixin_44109450/article/details/124259338 - # Github sudo vim /etc/hosts 140.82.112.4 github.com 199.232.69.194 github.global.ssl.fastly.net # 内网穿透 121. https://doc.natfrp.com/#/frpc/service/systemd2. # Item2 1https://zhuanlan.zhihu.com/p/550022490 # 青龙 12# 进入青龙容器docker exec -it qinglong bash # 使用 systemctl 管理服务 12345678https://www.jianshu.com/p/2deb0b79cb10# 路径/etc/systemd/system# 日志journalctl -u 服务名 # Arthas 123456789101112131415161718192021https://arthas.aliyun.com/doc/quick-start.html#_2-%E5%90%AF%E5%8A%A8-arthas[arthas@588425]$ watch net.lab1024.smartadmin.module.business.project.service.ProjectBaiscService queryProjects returnObjPress Q or Ctrl+C to abort.Affect(class count: 2 , method count: 2) cost in 170 ms, listenerId: 1method=net.lab1024.smartadmin.module.business.project.service.ProjectBaiscService.queryProjects location=AtExitts=2022-11-28 20:11:49; [cost=37.212667ms] result=@ResponseDTO[ code=@Integer[1], msg=@String[操作成功!], success=@Boolean[true], data=@ArrayList[isEmpty=false;size=13],]method=net.lab1024.smartadmin.module.business.project.service.ProjectBaiscService$$EnhancerBySpringCGLIB$$2f00139f.queryProjects location=AtExitts=2022-11-28 20:11:49; [cost=94.21994ms] result=@ResponseDTO[ code=@Integer[1], msg=@String[操作成功!], success=@Boolean[true], data=@ArrayList[isEmpty=false;size=13],] # Prometheus 123456789101112131415161718192021222324# prometheuswget https://github.com/prometheus/prometheus/releases/download/v2.40.4/prometheus-2.40.4.linux-amd64.tar.gztar -zxvf prometheus-2.40.4.linux-amd64.tar.gzsudo mv prometheus-2.40.4.linux-amd64 /usr/local/prometheusvim /usr/lib/systemd/system/prometheus.service[Unit]Description=prometheusAfter=network.target [Service]User=prometheusGroup=prometheusWorkingDirectory=/usr/local/prometheusExecStart=/usr/local/prometheus/prometheus[Install]WantedBy=multi-user.target# 启动并开启自启systemctl daemon-reloadsystemctl enable --now prometheus # 123456789101112131415161718192021222324cd ~/gitee_go/deoloylstar -zxf output.tar.gzcd targetpid=`ps -ef|grep xxx|grep -v grep|awk '{print $2}'`if [ $pid ]then kill -15 $pid finohup java -jar xxx.jar --server.port=8090 &# Ubuntu sh脚本不支持for循环 这里会报错for((i=1;i<=10;i++));do new_pid=`ps -ef|grep xxx|grep -v grep|awk '{print $2}'` if [ ! $new_pid ] then echo 'starting......' sleep 10 else echo "Deploy Success" break; fidone 1echo "alias python=/usr/bin/python3" >> ~/.bash_profile","categories":[],"tags":[{"name":"系统集成","slug":"系统集成","permalink":"https://fairyeye.github.io/tags/%E7%B3%BB%E7%BB%9F%E9%9B%86%E6%88%90/"}]},{"title":"算法","slug":"算法","date":"2023-02-13T08:12:42.000Z","updated":"2023-06-05T05:49:50.606Z","comments":true,"path":"2023/02/13/算法/","link":"","permalink":"https://fairyeye.github.io/2023/02/13/%E7%AE%97%E6%B3%95/","excerpt":"","text":"# LFU (最不经常使用缓存) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859import java.util.*;public class LFUCache<K, V> { private final int capacity; private Map<K, V> cache; private Map<K, Integer> freqMap; private Map<Integer, LinkedHashSet<K>> freqKeysMap; private int minFreq; public LFUCache(int capacity) { this.capacity = capacity; cache = new HashMap<>(); freqMap = new HashMap<>(); freqKeysMap = new HashMap<>(); minFreq = 0; } public V get(K key) { if (!cache.containsKey(key)) { return null; } int freq = freqMap.get(key); freqMap.put(key, freq + 1); freqKeysMap.get(freq).remove(key); if (freq == minFreq && freqKeysMap.get(freq).size() == 0) { minFreq++; } if (!freqKeysMap.containsKey(freq + 1)) { freqKeysMap.put(freq + 1, new LinkedHashSet<>()); } freqKeysMap.get(freq + 1).add(key); return cache.get(key); } public void put(K key, V value) { if (capacity <= 0) { return; } if (cache.containsKey(key)) { cache.put(key, value); get(key); return; } if (cache.size() >= capacity) { K evictKey = freqKeysMap.get(minFreq).iterator().next(); freqKeysMap.get(minFreq).remove(evictKey); cache.remove(evictKey); freqMap.remove(evictKey); } cache.put(key, value); freqMap.put(key, 1); minFreq = 1; if (!freqKeysMap.containsKey(1)) { freqKeysMap.put(1, new LinkedHashSet<>()); } freqKeysMap.get(1).add(key); }} LFU 缓存在处理缓存置换的时候会考虑到访问频率的因素。如果缓存空间已满,那么就要淘汰掉一些数据,以腾出空间存放新的数据。常见的淘汰算法有:先进先出(First In First Out,FIFO)、最近最少使用(Least Recently Used,LRU)和最不经常使用(Least Frequently Used,LFU)等。 这里我们实现了一个 LFU Cache,使用三个 Map 来存储缓存数据,缓存键的访问频率,以及不同访问频率下对应的缓存键集合。具体实现中,我们使用一个 minFreq 变量来记录当前最小访问频率,并在每次访问或插入数据时更新 minFreq。当缓存空间已满时,我们根据 minFreq 和缓存键集合中的顺序来选择要淘汰的数据。 该实现中,get 和 put 操作的时间复杂度均为 O (1)。如果需要支持高并发操作,可以在实现中加入线程安全机制。","categories":[],"tags":[{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"}]},{"title":"搭建SSR服务器","slug":"搭建SSR服务器","date":"2022-12-07T03:00:24.000Z","updated":"2023-06-05T05:49:50.606Z","comments":true,"path":"2022/12/07/搭建SSR服务器/","link":"","permalink":"https://fairyeye.github.io/2022/12/07/%E6%90%AD%E5%BB%BASSR%E6%9C%8D%E5%8A%A1%E5%99%A8/","excerpt":"","text":"1234567891011121314151617181920212223242526272829303132333435apt install git 报错:Temporary failure resolving 'archive.ubuntu.com具体如下:Ign:1 http://archive.ubuntu.com/ubuntu xenial/main i386 liberror-perl all 0.17-1.2Ign:2 http://archive.ubuntu.com/ubuntu xenial/main i386 git-man all 1:2.7.4-0ubuntu1Err:3 http://archive.ubuntu.com/ubuntu xenial/main amd64 git amd64 1:2.7.4-0ubuntu1 Temporary failure resolving 'archive.ubuntu.com'Err:1 http://archive.ubuntu.com/ubuntu xenial/main i386 liberror-perl all 0.17-1.2 Temporary failure resolving 'archive.ubuntu.com'Err:2 http://archive.ubuntu.com/ubuntu xenial/main i386 git-man all 1:2.7.4-0ubuntu1 Temporary failure resolving 'archive.ubuntu.com'E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/libe/liberror-perl/liberror-perl_0.17-1.2_all.deb Temporary failure resolving 'archive.ubuntu.com'E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.7.4-0ubuntu1_all.deb Temporary failure resolving 'archive.ubuntu.com'E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.7.4-0ubuntu1_amd64.deb Temporary failure resolving 'archive.ubuntu.com'E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?root@132157:~# apt-get updateErr:1 http://security.ubuntu.com/ubuntu xenial-security InRelease Temporary failure resolving 'security.ubuntu.com'Err:2 http://archive.ubuntu.com/ubuntu xenial InRelease Temporary failure resolving 'archive.ubuntu.com'Err:3 http://archive.ubuntu.com/ubuntu xenial-updates InRelease Temporary failure resolving 'archive.ubuntu.com'Err:4 http://archive.ubuntu.com/ubuntu xenial-backports InRelease Temporary failure resolving 'archive.ubuntu.com'Reading package lists... DoneW: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/xenial/InRelease Temporary failure resolving 'archive.ubuntu.com'W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/xenial-updates/InRelease Temporary failure resolving 'archive.ubuntu.com'W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/xenial-backports/InRelease Temporary failure resolving 'archive.ubuntu.com'W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/xenial-security/InRelease Temporary failure resolving 'security.ubuntu.com'W: Some index files failed to download. They have been ignored, or old ones used instead. 原因是DNS未配置 1234sudo vi /etc/resolv.conf+ nameserver 202.96.134.133+ nameserver 8.8.8.8","categories":[{"name":"杂项","slug":"杂项","permalink":"https://fairyeye.github.io/categories/%E6%9D%82%E9%A1%B9/"}],"tags":[{"name":"others","slug":"others","permalink":"https://fairyeye.github.io/tags/others/"}]},{"title":"Redis笔记","slug":"Redis","date":"2021-12-10T02:36:33.000Z","updated":"2023-06-05T05:49:50.603Z","comments":true,"path":"2021/12/10/Redis/","link":"","permalink":"https://fairyeye.github.io/2021/12/10/Redis/","excerpt":"","text":"// 201 个线程 应该扣减 201 -> 库存不足","categories":[{"name":"中间件","slug":"中间件","permalink":"https://fairyeye.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"}],"tags":[{"name":"redis","slug":"redis","permalink":"https://fairyeye.github.io/tags/redis/"}]},{"title":"数据库时间存储","slug":"数据库时间存储","date":"2020-04-29T08:12:42.000Z","updated":"2023-06-05T05:49:50.606Z","comments":true,"path":"2020/04/29/数据库时间存储/","link":"","permalink":"https://fairyeye.github.io/2020/04/29/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%97%B6%E9%97%B4%E5%AD%98%E5%82%A8/","excerpt":"","text":"# 数据库如何存储时间?你真的知道吗? 我们平时开发中不可避免的就是要存储时间,比如我们要记录操作表中这条记录的时间、记录转账的交易时间、记录出发时间等等。你会发现这个时间这个东西与我们开发的联系还是非常紧密的,用的好与不好会给我们的业务甚至功能带来很大的影响。所以,我们有必要重新出发,好好认识一下这个东西。 这是一篇短小精悍的文章,仔细阅读一定能学到不少东西! # 1. 切记不要用字符串存储日期 我记得我在大学的时候就这样干过,而且现在很多对数据库不太了解的新手也会这样干,可见,这种存储日期的方式的优点还是有的,就是简单直白,容易上手。 但是,这是不正确的做法,主要会有下面两个问题: 字符串占用的空间更大! 字符串存储的日期比较效率比较低(逐个字符进行比对),无法用日期相关的 API 进行计算和比较。 # 2.Datetime 和 Timestamp 之间抉择 Datetime 和 Timestamp 是 MySQL 提供的两种比较相似的保存时间的数据类型。他们两者究竟该如何选择呢? 通常我们都会首选 Timestamp。 下面说一下为什么这样做! # 2.1 DateTime 类型没有时区信息的 DateTime 类型是没有时区信息的(时区无关) ,DateTime 类型保存的时间都是当前会话所设置的时区对应的时间。这样就会有什么问题呢?当你的时区更换之后,比如你的服务器更换地址或者更换客户端连接时区设置的话,就会导致你从数据库中读出的时间错误。不要小看这个问题,很多系统就是因为这个问题闹出了很多笑话。 Timestamp 和时区有关。Timestamp 类型字段的值会随着服务器时区的变化而变化,自动换算成相应的时间,说简单点就是在不同时区,查询到同一个条记录此字段的值会不一样。 下面实际演示一下! 建表 SQL 语句: 123456CREATE TABLE `time_zone_test` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `date_time` datetime DEFAULT NULL, `time_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 插入数据: 1INSERT INTO time_zone_test(date_time,time_stamp) VALUES(NOW(),NOW()); 查看数据: 1select date_time,time_stamp from time_zone_test; 结果: 12345+---------------------+---------------------+| date_time | time_stamp |+---------------------+---------------------+| 2020-01-11 09:53:32 | 2020-01-11 09:53:32 |+---------------------+---------------------+ 现在我们运行 修改当前会话的时区: 1set time_zone='+8:00'; 再次查看数据: 12345+---------------------+---------------------+| date_time | time_stamp |+---------------------+---------------------+| 2020-01-11 09:53:32 | 2020-01-11 17:53:32 |+---------------------+---------------------+ 扩展:一些关于 MySQL 时区设置的一个常用 sql 命令 12345678910# 查看当前会话时区SELECT @@session.time_zone;# 设置当前会话时区SET time_zone = 'Europe/Helsinki';SET time_zone = "+00:00";# 数据库全局时区设置SELECT @@global.time_zone;# 设置全局时区SET GLOBAL time_zone = '+8:00';SET GLOBAL time_zone = 'Europe/Helsinki'; # 2.2 DateTime 类型耗费空间更大 Timestamp 只需要使用 4 个字节的存储空间,但是 DateTime 需要耗费 8 个字节的存储空间。但是,这样同样造成了一个问题,Timestamp 表示的时间范围更小。 DateTime :1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 Timestamp: 1970-01-01 00:00:01 ~ 2037-12-31 23:59:59 Timestamp 在不同版本的 MySQL 中有细微差别。 # 3 再看 MySQL 日期类型存储空间 下图是 MySQL 5.6 版本中日期类型所占的存储空间: 可以看出 5.6.4 之后的 MySQL 多出了一个需要 0 ~ 3 字节的小数位。Datatime 和 Timestamp 会有几种不同的存储空间占用。 为了方便,本文我们还是默认 Timestamp 只需要使用 4 个字节的存储空间,但是 DateTime 需要耗费 8 个字节的存储空间。 # 4. 数值型时间戳是更好的选择吗? 很多时候,我们也会使用 int 或者 bigint 类型的数值也就是时间戳来表示时间。 这种存储方式的具有 Timestamp 类型的所具有一些优点,并且使用它的进行日期排序以及对比等操作的效率会更高,跨系统也很方便,毕竟只是存放的数值。缺点也很明显,就是数据的可读性太差了,你无法直观的看到具体时间。 时间戳的定义如下: 时间戳的定义是从一个基准时间开始算起,这个基准时间是「1970-1-1 00:00:00 +0:00」,从这个时间开始,用整数表示,以秒计时,随着时间的流逝这个时间整数不断增加。这样一来,我只需要一个数值,就可以完美地表示时间了,而且这个数值是一个绝对数值,即无论的身处地球的任何角落,这个表示时间的时间戳,都是一样的,生成的数值都是一样的,并且没有时区的概念,所以在系统的中时间的传输中,都不需要进行额外的转换了,只有在显示给用户的时候,才转换为字符串格式的本地时间。 数据库中实际操作: 123456789101112131415mysql> select UNIX_TIMESTAMP('2020-01-11 09:53:32');+---------------------------------------+| UNIX_TIMESTAMP('2020-01-11 09:53:32') |+---------------------------------------+| 1578707612 |+---------------------------------------+1 row in set (0.00 sec)mysql> select FROM_UNIXTIME(1578707612);+---------------------------+| FROM_UNIXTIME(1578707612) |+---------------------------+| 2020-01-11 09:53:32 |+---------------------------+1 row in set (0.01 sec) 1转载:https://juejin.im/post/5e1d494a5188254c45778a14","categories":[],"tags":[{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"}]},{"title":"Go笔记","slug":"Go笔记","date":"2020-04-20T03:00:50.000Z","updated":"2023-06-05T05:49:50.601Z","comments":true,"path":"2020/04/20/Go笔记/","link":"","permalink":"https://fairyeye.github.io/2020/04/20/Go%E7%AC%94%E8%AE%B0/","excerpt":"","text":"1// 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。 报错: The 'main' file has the non-main package or does not contain the 'main' function main 函数需要再 main 包下","categories":[{"name":"学习笔记","slug":"学习笔记","permalink":"https://fairyeye.github.io/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"}],"tags":[]},{"title":"IDEA","slug":"IDEA","date":"2020-04-20T03:00:50.000Z","updated":"2023-06-05T05:49:50.602Z","comments":true,"path":"2020/04/20/IDEA/","link":"","permalink":"https://fairyeye.github.io/2020/04/20/IDEA/","excerpt":"","text":"# 1. Maven 设置 多 module 下,启动程序报错: 执行下,就可以了","categories":[{"name":"工具使用","slug":"工具使用","permalink":"https://fairyeye.github.io/categories/%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/"}],"tags":[]},{"title":"设计模式笔记","slug":"设计模式笔记","date":"2020-04-20T03:00:50.000Z","updated":"2023-06-05T05:49:50.607Z","comments":true,"path":"2020/04/20/设计模式笔记/","link":"","permalink":"https://fairyeye.github.io/2020/04/20/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%AC%94%E8%AE%B0/","excerpt":"","text":"设计模式遵循六⼤原则;单⼀职责 (⼀个类和⽅法只做⼀件事)、⾥⽒替换 ( 多态,⼦类可扩展⽗类 )、依赖 倒置 ( 细节依赖抽象,下层依赖上层 )、接⼝隔离 ( 建⽴单⼀接⼝ )、迪⽶特原则 ( 最少知道,降低耦合 )、开闭 原则 ( 抽象架构,扩展实现 )。 # 1. 工厂模式 举个例子: 吃:南方人爱吃饭,北方人爱吃面 定义一个接口:吃 定义两个实现类继承吃:饭、面 定义一个工厂:饭店 饭店通过判断是南方人还是北方人返回不同的实现类,从而让不同的人吃到了不同的饭 # 2. 抽象工厂模式 # 3. 建造者模式 实体类的 setXxx() 方法,一般是返回 void ,可以改造为: 1234public ObjectX setXxx(String xx) { this.xxx = xx return this;} # 4. 原型模式 考卷题目和答案乱序。","categories":[{"name":"学习笔记","slug":"学习笔记","permalink":"https://fairyeye.github.io/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"}],"tags":[]},{"title":"阿里巴巴手册","slug":"阿里巴巴手册","date":"2020-04-20T03:00:50.000Z","updated":"2023-06-05T05:49:50.607Z","comments":true,"path":"2020/04/20/阿里巴巴手册/","link":"","permalink":"https://fairyeye.github.io/2020/04/20/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4%E6%89%8B%E5%86%8C/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"时间戳转换日期问题","slug":"时间戳转换日期问题","date":"2020-04-17T02:59:54.000Z","updated":"2023-06-05T05:49:50.606Z","comments":true,"path":"2020/04/17/时间戳转换日期问题/","link":"","permalink":"https://fairyeye.github.io/2020/04/17/%E6%97%B6%E9%97%B4%E6%88%B3%E8%BD%AC%E6%8D%A2%E6%97%A5%E6%9C%9F%E9%97%AE%E9%A2%98/","excerpt":"","text":"# 基础依赖 后来发现是我拿到的时间戳是以 秒 为单位的,而转换时需要的是 毫秒。 1Date date = new Date(1579676844); // 1970-01-19T14:47:56.844+0800 1Date date = new Date(1579676844 * 1000L); // 2020-01-22T15:07:24.000+0800","categories":[],"tags":[{"name":"basic","slug":"basic","permalink":"https://fairyeye.github.io/tags/basic/"}]},{"title":"Hexo","slug":"15","date":"2020-04-15T06:34:30.000Z","updated":"2023-06-14T05:43:41.394Z","comments":true,"path":"2020/04/15/15/","link":"","permalink":"https://fairyeye.github.io/2020/04/15/15/","excerpt":"","text":"12345# hexo shokahttps://shoka.lostyu.me/computer-science/note/theme-shoka-doc/config/#%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA# 隐藏文章https://www.cnblogs.com/yangstar/articles/16690342.html","categories":[],"tags":[{"name":"hexo,theme,shoka","slug":"hexo-theme-shoka","permalink":"https://fairyeye.github.io/tags/hexo-theme-shoka/"}]},{"title":"内部类问题","slug":"14","date":"2020-04-14T01:41:32.000Z","updated":"2023-06-05T01:28:10.947Z","comments":true,"path":"2020/04/14/14/","link":"","permalink":"https://fairyeye.github.io/2020/04/14/14/","excerpt":"","text":"12错误描述信息:org.fairy.eye.domin.entity.User is not an enclosing class 在 new 一个内部类的对象时,idea 会生成如下的代码。 12// Info 是User的内部类User.Info info = new User.Info(); 然后会提示报错 org.fairy.eye.domin.entity.User is not an enclosing class 如果内部类没有用 static 修饰的话,是不能这样 new 的。 需要有外部类实例对象来支持。 12User user = new User();User.Info info = new user.Info();","categories":[],"tags":[{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"}]},{"title":"GIT代码无法合并+本地构建成功服务器构建失败","slug":"13","date":"2020-04-13T01:13:28.000Z","updated":"2024-01-19T07:43:47.000Z","comments":true,"path":"2020/04/13/13/","link":"","permalink":"https://fairyeye.github.io/2020/04/13/13/","excerpt":"","text":"# 1.GIT 相关 # 场景: GIT 版本出了点问题,无法正常合并代码 # 具体描述: 由于误操作,导致我的本地分支和 dev 分支不同,在 GIT 上对比的时候显示无差别,但是实际上最近一次的提交是没有合并的。 无奈之下,我选择回滚代码。 # 具体操作: 第一种方法: 注意:这种方法会把之后的代码完全覆盖掉,不建议使用,除非这个分支只有你自己操作,或者你回滚的版本之后没有别的人提交 首先要找到你要回滚的版本号。 在 idea 中操作:右键项目 -> Git -> Show History -> 右键提交的版本 -> Copy Revision Number。 在 GIT 中操作:找到提交的历史,SHA 值,即为版本号。 Git Bush 命令行 /idea TerMinal 命令行 git reset --hard 版本号 第二种方法: 找到版本号,同上。 右键项目 -> Git -> Repository -> Reset HEAD 在 To Commit 中输入版本号 # 最后: 记得一定要提交代码,即使看起来没有要 push 的东西,否则 git pull 之后等于没回滚。 # 2. 构建相关 # 场景: 回滚代码之后,本地可以成功构建,但服务器端无法正常构建。 # 具体描述: 服务器端构建一直失败,好像是拉取到了错误版本的代码。 # 具体操作: 暴脾气的我本来打算把项目删了重新拉一个下来,后来忍住了,在服务器端回滚了一下代码,然后手动构建了一下,居然成功了,完结。 # 最后: 虽然没什么有用的东西,不过思路还是有的。 首先你要确认本地构建的代码是否和服务器完全相同、服务器构建的时候是否 pull 了最新的代码... 12345678910111213141516171819202122随机限定皮肤礼:0.2%;碎月令:0.2%;随机传说皮肤:0.5%;随机皮肤:13.5%;剑意(8~68个):58.5%;皮肤碎片x8:15%;锦鲤纳福亲密度道具x8:12.1%;限定-288个、传说-120个、史诗-60个、勇者-40个、伴生-20个。其中,随机限定皮肤礼的奖励概率分布为:李白-碎月剑心:1%;甄姬-幽恒:33%;后羿-辉光之辰:33%;猪八戒-猪悟能:33%;随机皮肤的奖励概率分布为:史诗皮肤:10%,勇者皮肤:30%,伴生皮肤:60%;具体包含皮肤可见活动首页-【奖池】内信息;剑意的奖励概率分布为:剑意x8:37.6%;剑意x18:51.3%;剑意x28:9.4%A礼盒:0.2%; 1%:0个剑意 99%:288个剑意B礼盒:0.2%; 498个剑意C礼盒:0.5%; 120个剑意D礼盒:13.5%; 10%:60个剑意,30%:40个剑意,60%:20个剑意E礼盒(8~68个):58.5%; 37.6%:8个;51.3%:18个剑意;9.4%:28个剑意;12%:68个剑意F礼盒(皮肤碎片)0个:15%;G礼盒0个:12.1%;","categories":[],"tags":[{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"}]}],"categories":[{"name":"杂项","slug":"杂项","permalink":"https://fairyeye.github.io/categories/%E6%9D%82%E9%A1%B9/"},{"name":"java","slug":"java","permalink":"https://fairyeye.github.io/categories/java/"},{"name":"use","slug":"use","permalink":"https://fairyeye.github.io/categories/use/"},{"name":"学习笔记","slug":"学习笔记","permalink":"https://fairyeye.github.io/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"},{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"},{"name":"中间件","slug":"中间件","permalink":"https://fairyeye.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"},{"name":"杂谈","slug":"杂谈","permalink":"https://fairyeye.github.io/categories/%E6%9D%82%E8%B0%88/"},{"name":"实用","slug":"实用","permalink":"https://fairyeye.github.io/categories/%E5%AE%9E%E7%94%A8/"},{"name":"工具使用","slug":"工具使用","permalink":"https://fairyeye.github.io/categories/%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/"}],"tags":[{"name":"旅游","slug":"旅游","permalink":"https://fairyeye.github.io/tags/%E6%97%85%E6%B8%B8/"},{"name":"学习","slug":"学习","permalink":"https://fairyeye.github.io/tags/%E5%AD%A6%E4%B9%A0/"},{"name":"日常记录","slug":"日常记录","permalink":"https://fairyeye.github.io/tags/%E6%97%A5%E5%B8%B8%E8%AE%B0%E5%BD%95/"},{"name":"work","slug":"work","permalink":"https://fairyeye.github.io/tags/work/"},{"name":"迭代","slug":"迭代","permalink":"https://fairyeye.github.io/tags/%E8%BF%AD%E4%BB%A3/"},{"name":"git","slug":"git","permalink":"https://fairyeye.github.io/tags/git/"},{"name":"hexo","slug":"hexo","permalink":"https://fairyeye.github.io/tags/hexo/"},{"name":"demo","slug":"demo","permalink":"https://fairyeye.github.io/tags/demo/"},{"name":"utils","slug":"utils","permalink":"https://fairyeye.github.io/tags/utils/"},{"name":"mysql","slug":"mysql","permalink":"https://fairyeye.github.io/tags/mysql/"},{"name":"基础","slug":"基础","permalink":"https://fairyeye.github.io/tags/%E5%9F%BA%E7%A1%80/"},{"name":"Java","slug":"Java","permalink":"https://fairyeye.github.io/tags/Java/"},{"name":"dairy","slug":"dairy","permalink":"https://fairyeye.github.io/tags/dairy/"},{"name":"软件","slug":"软件","permalink":"https://fairyeye.github.io/tags/%E8%BD%AF%E4%BB%B6/"},{"name":"数据库","slug":"数据库","permalink":"https://fairyeye.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"},{"name":"系统集成","slug":"系统集成","permalink":"https://fairyeye.github.io/tags/%E7%B3%BB%E7%BB%9F%E9%9B%86%E6%88%90/"},{"name":"others","slug":"others","permalink":"https://fairyeye.github.io/tags/others/"},{"name":"redis","slug":"redis","permalink":"https://fairyeye.github.io/tags/redis/"},{"name":"basic","slug":"basic","permalink":"https://fairyeye.github.io/tags/basic/"},{"name":"hexo,theme,shoka","slug":"hexo-theme-shoka","permalink":"https://fairyeye.github.io/tags/hexo-theme-shoka/"}]}