静待时光清浅，岁月流转，把酒言欢，梦里，青春与你我不似初见。
#!/usr/bin/env python3 # wechat_multi_gui.py """ 微信多账号免密启动 - Tkinter GUI 版本 支持： - 保存当前账号（同名则覆盖） - 删除账号 """ import os import json import shutil import subprocess from pathlib import Path import tkinter as tk from tkinter import filedialog, messagebox, simpledialog CONFIG_FILE = Path(__file__).with_name("wechat_cli_config.json") def load_config(): if CONFIG_FILE.exists(): try: return json.loads(CONFIG_FILE.read_text(encoding="utf-8")) except Exception: return {} return {} def save_config(cfg: dict): try: CONFIG_FILE.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8") except Exception as e: messagebox.showerror("错误", f"保存配置文件失败: {e}") def ensure_backup_dir(config_path): cfg_parent = Path(config_path).parent backup_dir = cfg_parent / "config_backup" backup_dir.mkdir(parents=True, exist_ok=True) return backup_dir def list_saved_accounts(backup_dir): accounts = [] for p in backup_dir.iterdir(): if p.is_dir() and p.name.isdigit(): display = f"账号 {p.name}" meta = p / "account.meta" if meta.exists(): try: j = json.loads(meta.read_text(encoding="utf-8")) display = j.get("displayName", display) except Exception: pass accounts.append({"id": int(p.name), "folder": p, "displayName": display}) accounts.sort(key=lambda x: x["id"]) return accounts def save_current_account(config_path, backup_dir, display_name=None): # 如果同名则覆盖更新 if display_name: for acc in list_saved_accounts(backup_dir): if acc["displayName"] == display_name: shutil.copy2(config_path, acc["folder"] / "config.data") meta = {"displayName": display_name} (acc["folder"] / "account.meta").write_text(json.dumps(meta, ensure_ascii=False), encoding="utf-8") return acc["id"] # 否则新建 ids = [int(p.name) for p in backup_dir.iterdir() if p.is_dir() and p.name.isdigit()] new_id = max(ids) + 1 if ids else 1 new_dir = backup_dir / str(new_id) new_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(config_path, new_dir / "config.data") meta = {"displayName": display_name or f"账号 {new_id}"} (new_dir / "account.meta").write_text(json.dumps(meta, ensure_ascii=False), encoding="utf-8") return new_id def start_wechat_with_config(exe_path, config_path, backup_dir, account_folder=None): if not Path(config_path).exists(): raise FileNotFoundError("目标 config.data 不存在") # 备份当前 shutil.copy2(config_path, backup_dir / "current_config.data") if account_folder is None: raise ValueError("account_folder 必须提供") src = Path(account_folder) / "config.data" if not src.exists(): raise FileNotFoundError("选定账号的 config.data 丢失") shutil.copy2(src, config_path) subprocess.Popen([exe_path], shell=False) class WeChatGUI: def __init__(self, root): self.root = root self.root.title("微信多账号免密启动") self.config = load_config() # 路径输入 tk.Label(root, text="微信 EXE 路径:").grid(row=0, column=0, sticky="e") self.exe_var = tk.StringVar(value=self.config.get("ExePath", "")) tk.Entry(root, textvariable=self.exe_var, width=50).grid(row=0, column=1) tk.Button(root, text="选择", command=self.select_exe).grid(row=0, column=2) tk.Label(root, text="config.data 路径:").grid(row=1, column=0, sticky="e") self.config_var = tk.StringVar(value=self.config.get("ConfigPath", "")) tk.Entry(root, textvariable=self.config_var, width=50).grid(row=1, column=1) tk.Button(root, text="选择", command=self.select_config).grid(row=1, column=2) tk.Button(root, text="保存设置", command=self.save_settings).grid(row=2, column=0, columnspan=3, pady=5) # 账号列表 self.account_listbox = tk.Listbox(root, height=10, width=50) self.account_listbox.grid(row=3, column=0, columnspan=3, pady=5) tk.Button(root, text="启动选中账号", command=self.start_selected).grid(row=4, column=0) tk.Button(root, text="保存当前账号", command=self.save_current).grid(row=4, column=1) tk.Button(root, text="删除选中账号", command=self.delete_selected).grid(row=4, column=2) tk.Button(root, text="刷新列表", command=self.refresh_list).grid(row=5, column=0, columnspan=3, pady=5) self.refresh_list() def select_exe(self): path = filedialog.askopenfilename(title="选择 WeChat.exe", filetypes=[("微信程序", "*.exe")]) if path: self.exe_var.set(path) def select_config(self): path = filedialog.askopenfilename(title="选择 config.data", filetypes=[("config.data", "config.data")]) if path: self.config_var.set(path) def save_settings(self): exe_path = self.exe_var.get() cfg_path = self.config_var.get() if not Path(exe_path).exists(): messagebox.showerror("错误", "微信 exe 路径无效") return if not Path(cfg_path).exists(): messagebox.showerror("错误", "config.data 路径无效") return self.config["ExePath"] = exe_path self.config["ConfigPath"] = cfg_path save_config(self.config) messagebox.showinfo("提示", "设置已保存") def refresh_list(self): self.account_listbox.delete(0, tk.END) cfg_path = self.config.get("ConfigPath") if not cfg_path or not Path(cfg_path).exists(): self.account_listbox.insert(tk.END, "⚠ 请先设置正确的 config.data 路径") self.accounts = [] return backup_dir = ensure_backup_dir(cfg_path) accounts = list_saved_accounts(backup_dir) for acc in accounts: self.account_listbox.insert(tk.END, f"{acc['displayName']} (ID: {acc['id']})") self.accounts = accounts def start_selected(self): if not hasattr(self, "accounts") or not self.accounts: return sel = self.account_listbox.curselection() if not sel: messagebox.showwarning("提示", "请先选择一个账号") return idx = sel[0] account = self.accounts[idx] try: start_wechat_with_config(self.config["ExePath"], self.config["ConfigPath"], ensure_backup_dir(self.config["ConfigPath"]), account["folder"]) messagebox.showinfo("提示", "已启动微信（如无异常会免密登录）") except Exception as e: messagebox.showerror("错误", str(e)) def save_current(self): cfg_path = self.config.get("ConfigPath") if not cfg_path or not Path(cfg_path).exists(): messagebox.showerror("错误", "config.data 路径无效") return name = simpledialog.askstring("保存账号", "输入显示名称（可留空使用默认）") try: new_id = save_current_account(cfg_path, ensure_backup_dir(cfg_path), name) messagebox.showinfo("提示", f"账号已保存/更新（ID: {new_id}）") self.refresh_list() except Exception as e: messagebox.showerror("错误", str(e)) def delete_selected(self): if not hasattr(self, "accounts") or not self.accounts: return sel = self.account_listbox.curselection() if not sel: messagebox.showwarning("提示", "请先选择一个账号") return idx = sel[0] account = self.accounts[idx] confirm = messagebox.askyesno("确认删除", f"确定要删除账号 {account['displayName']} 吗？") if confirm: try: shutil.rmtree(account["folder"]) messagebox.showinfo("提示", "账号已删除") self.refresh_list() except Exception as e: messagebox.showerror("错误", str(e)) if __name__ == "__main__": root = tk.Tk() app = WeChatGUI(root) root.mainloop()
DBeaverUE 23.2.0 分析说明通过学习DBeaver Ultimate Edition License验证分析得到此文档，仅供研究学习使用，请勿用于非法用途，请支持正版！一、直接使用分析过的密钥确保是修改过的DBeaverUE压缩包打开后输入如下授权码：[binary] kr49KOrCKzWMjpmA4WWbhiSOcykC4kRXT21w0Q==二、分析过程下载官方DBeaverUE压缩包压缩包：https://download.dbeaver.com/ultimate/23.2.0/dbeaver-ue-23.2.0-win32.win32.x86_64.zip解压到软件需要安装的位置，打开目录：plugins找到这三个文件，后面的数字版本号可能不同：org.jkiss.utils_2.1.196.202309120446.jar org.jkiss.lm_1.0.166.202309120446.jar org.jkiss.dbeaver.core_23.2.0.202309120446.jar同时需要知道com.dbeaver.app.ultimate这个jar包在哪，后续需要修改里面的公钥文件把DBeaverLicenseActiveMain.java文件和上面三个jar文件放进去打开vscode，安装插件”Extension Pack for Java”后左下角有Java项目栏,在”Referenced Libraries”添加jar，把上面的三个jar添加进去打开DBeaverLicenseActiveMain.java文件，点击右上角的运行，终端中会输出无报错的信息，即为环境正常；复制终端的运行命令，在命令后面追加参数 gen-keys，会生成公钥和私钥；创建文件public-key.txt，private-key.txt，dbeaver-ue-public.key，将私钥复制到private-key.txt中，将公钥复制到public-key.txt和dbeaver-ue-public.key中；将public-key.txt和private-key.txt文件放到用户目录下的.jkiss-lm文件夹中，用dbeaver-ue-public.key文件替换com.dbeaver.app.ultimate_22.1.0.202206121739.jar内的keysdbeaver-ue-public.key文件；复制终端的运行命令，在命令后面追加参数 encrypt-license，生成License字符串，此时License为授权码，已经可用；修改配置文件dbeaver.ini，增加lm.debug.mode=true配置，在最后一行添加：-Dlm.debug.mode=true 打开DBeaver，导入License，即可完成分析；三、DBeaverLicenseActiveMain.java 代码import org.jkiss.lm.*; import org.jkiss.utils.Base64; import java.io.*; import java.security.Key; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Date; public class DBeaverLicenseActiveMain { private static final LMProduct TEST_PRODUCT; static { TEST_PRODUCT = new LMProduct( "dbeaver-ue", "DB", "DBeaver Ultimate", "DBeaver Ultimate Edition", "23.2", LMProductType.DESKTOP, new Date(), new String[0]); } public static void main(String[] args) throws Exception { System.out.println("LM 2.0"); // 生成RAS密钥对 if (args.length > 0 && args[0].equals("gen-keys")) { System.out.println("Test key generation"); generateKeyPair(); // 生成加密License } else if (args.length > 0 && args[0].equals("encrypt-license")) { System.out.println("Encrypt license"); encryptLicense(); // 解密License } else if (args.length > 0 && args[0].equals("decrypt-license")) { System.out.println("Decrypt license"); decryptLicense(); // 导入License测试 } else { System.out.println("Test license generation"); generateLicense(); } } private static void encryptLicense() throws Exception { PrivateKey privateKey = readPrivateKey(); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String licenseID = LMUtils.generateLicenseId(TEST_PRODUCT); System.out.println("License ID: " + licenseID); System.out.println("Product ID (" + TEST_PRODUCT.getId() + "):"); String productID = in.readLine(); if (productID.isEmpty()) { productID = TEST_PRODUCT.getId(); } System.out.println("Product version (" + TEST_PRODUCT.getVersion() + "):"); String productVersion = in.readLine(); if (productVersion.isEmpty()) { productVersion = TEST_PRODUCT.getVersion(); } System.out.println("Owner ID (10000):"); String ownerID = in.readLine(); if (ownerID.isEmpty()) { ownerID = "10000"; } System.out.println("Owner company (JKISS):"); String ownerCompany = in.readLine(); if (ownerCompany.isEmpty()) { ownerCompany = "JKISS"; } System.out.println("Owner name (Serge Rider):"); String ownerName = in.readLine(); if (ownerName.isEmpty()) { ownerName = "Serge Rider"; } System.out.println("Owner email (serge@jkiss.org):"); String ownerEmail = in.readLine(); if (ownerEmail.isEmpty()) { ownerEmail = "serge@jkiss.org"; } LMLicense license = new LMLicense( licenseID, LMLicenseType.ULTIMATE, new Date(), new Date(), (Date) null, LMLicense.FLAG_UNLIMITED_SERVERS, productID, productVersion, ownerID, ownerCompany, ownerName, ownerEmail); byte[] licenseData = license.getData(); byte[] licenseEncrypted = LMEncryption.encrypt(licenseData, privateKey); System.out.println("--- LICENSE ---"); System.out.println(Base64.splitLines(Base64.encode(licenseEncrypted), 76)); } private static void decryptLicense() throws Exception { PublicKey publicKey = readPublicKey(); System.out.println("License:"); byte[] encryptedLicense = LMUtils.readEncryptedString(System.in); LMLicense license = new LMLicense(encryptedLicense, publicKey); System.out.println(license); } private static void generateKeyPair() throws LMException { KeyPair keyPair = LMEncryption.generateKeyPair(2048); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); System.out.println("--- PUBLIC KEY ---"); System.out.println(Base64.splitLines(Base64.encode(publicKey.getEncoded()), 76)); System.out.println("--- PRIVATE KEY ---"); System.out.println(Base64.splitLines(Base64.encode(privateKey.getEncoded()), 76)); } private static void generateLicense() throws LMException { System.out.println("Gen keys"); KeyPair keyPair = LMEncryption.generateKeyPair(2048); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); System.out.println("Gen Test license"); String licenseID = LMUtils.generateLicenseId(TEST_PRODUCT); LMLicense license = new LMLicense( licenseID, LMLicenseType.ULTIMATE, new Date(), new Date(), (Date) null, LMLicense.FLAG_UNLIMITED_SERVERS, TEST_PRODUCT.getId(), TEST_PRODUCT.getVersion(), "10000", "JKISS", "Serge Rider", "serge@jkiss.org"); byte[] subData = license.getData(); byte[] encrypted = LMEncryption.encrypt(subData, privateKey); String encodedBase64 = Base64.splitLines(Base64.encode(encrypted), 76); byte[] encodedBinary = Base64.decode(encodedBase64); LMLicense licenseCopy = new LMLicense(encodedBinary, publicKey); System.out.println(licenseCopy); System.out.println("Gen subscription"); LMSubscription subscription = new LMSubscription( licenseID, LMSubscriptionPeriod.MONTH, new Date(), new Date(), 1, true); subData = LMEncryption.encrypt(subscription.getData(), privateKey); String subBase64 = Base64.splitLines(Base64.encode(subData), 76); byte[] subBinary = Base64.decode(subBase64); LMSubscription subCopy = new LMSubscription(subBinary, publicKey); System.out.println(subCopy); } private static PrivateKey readPrivateKey() throws LMException { File keyFile = new File(new File(System.getProperty("user.home"), ".jkiss-lm"), "private-key.txt"); if (!keyFile.exists()) { throw new LMException("Cannot find private key file (" + keyFile.getAbsolutePath() + ")"); } else { try { Throwable var1 = null; Object var2 = null; try { InputStream keyStream = new FileInputStream(keyFile); PrivateKey var10000; try { byte[] privateKeyData = LMUtils.readEncryptedString(keyStream); var10000 = LMEncryption.generatePrivateKey(privateKeyData); } finally { if (keyStream != null) { keyStream.close(); } } return var10000; } catch (Throwable var12) { if (var1 == null) { var1 = var12; } else if (var1 != var12) { var1.addSuppressed(var12); } throw var1; } } catch (Throwable var13) { throw new LMException(var13); } } } private static PublicKey readPublicKey() throws LMException { File keyFile = new File(new File(System.getProperty("user.home"), ".jkiss-lm"), "public-key.txt"); if (!keyFile.exists()) { throw new LMException("Cannot find public key file (" + keyFile.getAbsolutePath() + ")"); } else { try { Throwable var1 = null; try { InputStream keyStream = new FileInputStream(keyFile); PublicKey var10000; try { byte[] keyData = LMUtils.readEncryptedString(keyStream); var10000 = LMEncryption.generatePublicKey(keyData); } finally { if (keyStream != null) { keyStream.close(); } } return var10000; } catch (Throwable var12) { if (var1 == null) { var1 = var12; } else if (var1 != var12) { var1.addSuppressed(var12); } throw var1; } } catch (Throwable var13) { throw new LMException(var13); } } } } 后记仅供研究学习使用，请勿用于非法用途，请支持正版！
业务场景：后期会做一个类似“实验楼”的在线编程站点，需要用到在线使用Api创建指定镜像的虚拟机。这里先记录一下如何使用Ubuntu 21来进行Docker的安装和api的基础使用。一、Docker网上有很多相关教程文档，这里列举几个常用的1.安装$ sudo apt-get install -y docker.io2.启动$ systemctl start docker3.停止$ systemctl stop docker4.开机自启$ systemctl enable docker5.查看状态$ systemctl status docker6.查看版本$ docker version接下来验证Docker1.输入以下命令，此时不用在乎命令具体含义，后面会解释分析。$ sudo docker run hello-world2.如出现类似信息，表示Docker可正常使用了Hello from Docker!二、Docker操作镜像说明:这里会讲解Docker对镜像的一些简单操作。所谓镜像，可以简单理解为一个软件，在Docker中安装一个软件，Docker即为容器。任何软件都可以进行封装成为Docker中的软件，包括操作系统。1.拉取新的镜像到Docker$ sudo docker pull docker.io/portainer/portainer拉取镜像可以理解为下载软件的行为，将别人封装好的软件安装进自己的容器中，使用如下命令。这里以安装一个docker可视化web面板为例。其中使用pull表示拉取，后面的docker.io/portainer/portainer为镜像名称2.查看Docker中都有哪些镜像$ sudo docker images在未安装镜像之前，使用此命令仅能看到一个名为hello-world的镜像。在上一步使用命令安装了docker.io/portainer/portainer，所以本次的执行结果中会出现名为portainer/portainer的镜像。3.创建Docker中的镜像容器$ sudo docker run -d --name portainerUI -p 192.168.0.182:9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer这里参数可参考菜鸟教程中的说明 Docker run命令 ，意思为：以后台运行的方式创建启动并返回一个唯一ID,这个容器的名称被指定为portainerUI，同时进行端口映射，将宿主机的192.168.0.182:9000端口映射到容器中9000端口中。这里容器端口为固定的，宿主机IP和端口可自行调整。后面-v 进行绑定卷,大概是管理的意思，最后的portainer/portainer表示为启动的容器名称。4.验证启动的容器$ sudo docker ps使用ps命令可查看正在运行的容器仅针对上面的容器，在浏览器中打开地址 http://192.168.0.182:9000 能显示页面，即为成功运行这里提到的是使用命令的方式创建和启动容器，我们后面使用HTTP API的方式进行容器的创建启动5.终止正在运行的容器$ sudo docker stop 68692b82a394其中68692b82a394为使用ps查询到容器对应的id6.启动已经创建的容器$ sudo docker start 68692b82a394这里是启动，使用第3步的方式是创建，这里是启动已经创建的容器。三、Docker开放APIDocker的API需要用到2375端口，当然，这个端口你也可以自定义，此端口默认不开放，需要进行受到开放1.开启API功能，用vim或者vi编辑sudo vim /usr/lib/systemd/system/docker.service2.找到[Service]，在里面添加如下内容ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock3.重启dockersystemctl daemon-reload systemctl restart docker四、使用Docker Api进行容器创建这里开始讲解如何使用Docker的api进行容器的操作，不再使用终端。使用postman或者其他工具,POST方式,发送的数据为JSON格式。其中name=xxxx中的xxx为创建容器的名称，类似于上面的-name参数1.创建容器POST请求地址：http://192.168.0.182:2375/containers/create?name=xxxx请求参数：{ "Hostname": "testname", "Domainname": "hehe", "Image": "centos:7.9.2009", "ExposedPorts": { "80/tcp": {} }, "HostConfig": { "PortBindings": { "6901/tcp": [{"HostPort": "666"}] } }, }2.查看容器列表GET请求参数：http://192.168.0.182:2375/containers/json?filters={"status":["created","restarting","running","removing","paused","exited","dead"]}这里要参考这篇文章docker remote api 1.1 容器查询Docker Api官方文档 官方文档3.启动/停止/重启 一个containers容器$ curl -X POST http://192.168.0.182:2375/containers/{id}/start (启动 注意这里是POST方法） $ curl -X POST http://192.168.0.182:2375/containers/{id}/stop (停止 注意这里是POST方法） $ curl -X POST http://192.168.0.182:2375/containers/{id}/restart (重启 注意这里是POST方法）自此，完成了所有配置。后续可通过各种语言调用api进行docker操作了！
function convertCurrency(currencyDigits) { // Constants: var MAXIMUM_NUMBER = 99999999999.99; // Predefine the radix characters and currency symbols for output: var CN_ZERO = "零"; var CN_ONE = "壹"; var CN_TWO = "贰"; var CN_THREE = "叁"; var CN_FOUR = "肆"; var CN_FIVE = "伍"; var CN_SIX = "陆"; var CN_SEVEN = "柒"; var CN_EIGHT = "捌"; var CN_NINE = "玖"; var CN_TEN = "拾"; var CN_HUNDRED = "佰"; var CN_THOUSAND = "仟"; var CN_TEN_THOUSAND = "万"; var CN_HUNDRED_MILLION = "亿"; var CN_SYMBOL = ""; var CN_DOLLAR = "元"; var CN_TEN_CENT = "角"; var CN_CENT = "分"; var CN_INTEGER = "整"; // Variables: var integral; // Represent integral part of digit number. var decimal; // Represent decimal part of digit number. var outputCharacters; // The output result. var parts; var digits, radices, bigRadices, decimals; var zeroCount; var i, p, d; var quotient, modulus; // Validate input string: currencyDigits = currencyDigits.toString(); if (currencyDigits == "") { alert("您的输入为空！"); return ""; } if (currencyDigits.match(/[^,.\d]/) != null) { alert("输入包含非法字符!"); return ""; } if ((currencyDigits).match(/^((\d{1,3}(,\d{3})*(.((\d{3},)*\d{1,3}))?)|(\d+(.\d+)?))$/) == null) { alert("输入有误!"); return ""; } // Normalize the format of input digits: currencyDigits = currencyDigits.replace(/,/g, ""); // Remove comma delimiters. currencyDigits = currencyDigits.replace(/^0+/, ""); // Trim zeros at the beginning. // Assert the number is not greater than the maximum number. if (Number(currencyDigits) > MAXIMUM_NUMBER) { alert("数额太大，转换金额上限小于1千亿!"); return ""; } // Process the coversion from currency digits to characters: // Separate integral and decimal parts before processing coversion: parts = currencyDigits.split("."); if (parts.length > 1) { integral = parts[0]; decimal = parts[1]; // Cut down redundant decimal digits that are after the second. decimal = decimal.substr(0, 2); } else { integral = parts[0]; decimal = ""; } // Prepare the characters corresponding to the digits: digits = new Array(CN_ZERO, CN_ONE, CN_TWO, CN_THREE, CN_FOUR, CN_FIVE, CN_SIX, CN_SEVEN, CN_EIGHT, CN_NINE); radices = new Array("", CN_TEN, CN_HUNDRED, CN_THOUSAND); bigRadices = new Array("", CN_TEN_THOUSAND, CN_HUNDRED_MILLION); decimals = new Array(CN_TEN_CENT, CN_CENT); // Start processing: outputCharacters = ""; // Process integral part if it is larger than 0: if (Number(integral) > 0) { zeroCount = 0; for (i = 0; i < integral.length; i++) { p = integral.length - i - 1; d = integral.substr(i, 1); quotient = p / 4; modulus = p % 4; if (d == "0") { zeroCount++; } else { if (zeroCount > 0) { outputCharacters += digits[0]; } zeroCount = 0; outputCharacters += digits[Number(d)] + radices[modulus]; } if (modulus == 0 && zeroCount < 4) { outputCharacters += bigRadices[quotient]; } } outputCharacters += CN_DOLLAR; } // Process decimal part if there is: if (decimal != "") { for (i = 0; i < decimal.length; i++) { d = decimal.substr(i, 1); if (d != "0") { outputCharacters += digits[Number(d)] + decimals[i]; } } } // Confirm and return the final output string: if (outputCharacters == "") { outputCharacters = CN_ZERO + CN_DOLLAR; } if (decimal == "") { outputCharacters += CN_INTEGER; } outputCharacters = CN_SYMBOL + outputCharacters; return outputCharacters; }
代码调用: makePy('张三');
函数// 将1、2、3、4、5、6... 转为 '一','二','三','四','五','六' 等汉字 function numberToChinese(num) { var bit = Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"); //各位数 if (num <= 10) { return bit[num]; } //十位数 if(num < 100){ var array = num.toString().split(""); if(array[0] < 2){ return bit[10]+''+bit[array[1]]; }else{ if(array[1] == 0){ return bit[array[0]]+''+bit[10]; }else{ return bit[array[0]]+''+bit[10]+''+bit[array[1]]; } } } //百位数 if(num < 1000){ var array = num.toString().split(""); if(array[1] == 0 && array[2] == 0){ return bit[array[0]]+"百"; }else if(array[1] == 0 && array[2] != 0){ return bit[array[0]]+"百"+bit[array[1]]+''+bit[array[2]]; }else if(array[1] != 0 && array[2] == 0 ){ return bit[array[0]]+"百"+bit[array[1]]+''+bit[10]; }else{ return bit[array[0]]+"百"+bit[array[1]]+''+bit[10]+''+bit[array[2]]; } } //千位数 if(num < 10000){ var array = num.toString().split(""); if(array[1] == 0 && array[2] == 0 && array[3] == 0){ return bit[array[0]]+"千"; }else if(array[1] == 0 && array[2] != 0 && array[3] != 0){ return bit[array[0]]+"千"+bit[array[1]]+''+bit[array[2]]+''+bit[10]+''+bit[array[3]]; }else if(array[1] == 0 && array[2] == 0 && array[3] != 0){ return bit[array[0]]+"千"+bit[array[1]]+''+bit[array[3]]; }else if(array[1] == 0 && array[2] != 0 && array[3] == 0){ return bit[array[0]]+"千"+bit[array[1]]+''+bit[array[2]]+''+bit[10]; }else if(array[1] != 0 && array[2] == 0 && array[3] == 0){ return bit[array[0]]+"千"+bit[array[1]]+"百"; }else if(array[1] != 0 && array[2] != 0 && array[3] == 0){ return bit[array[0]]+"千"+bit[array[1]]+"百"+bit[array[2]]+''+bit[10]; }else if(array[1] != 0 && array[2] == 0 && array[3] != 0){ return bit[array[0]]+"千"+bit[array[1]]+"百"+bit[array[2]]+''+bit[array[3]]; }else{ return bit[array[0]]+"千"+bit[array[1]]+"百"+bit[array[2]]+''+bit[10]+''+bit[array[3]]; } } return num; } 调用console.log(numberToChinese(3943)) //输出:三千九百四十三
EduSoho开发指南研究背景：该Edusoho基于symfony2.8开发，但根据symfony2.8的文档描述和实际Edusoho项目结构对比，二者有较大的差别，同时Edusoho的开发文档并不全面，有许多坑。通过Symfony提供的文档，无法在Edusoho中通过命令行创建数据表和数据表对应的Dao和Service。我想应该是Edusoho团队已经将其进行了大量的修改，导致原本的Symfony文档无法为Edusoho服务。所以我这里开始对该项目进行数据库新建表实行梳理。在srcBiz目录下，随便找一个文件夹，这里以Activity文件夹为例。在ActivityBizDao下创建XXXActivityDao.php接口文件，这是接口文件，继承基类GeneralDaoInterface；例如TestActivityDao.php<?php # 接口：一种成员属性全部为抽象或常量的特殊抽象类。 namespace Biz\Activity\Dao; #命名空间 use Codeages\Biz\Framework\Dao\GeneralDaoInterface; #基类 interface TestActivityDao extends GeneralDaoInterface { public function getById($id); #自定义一个函数 }在ActivityBizDaoImpl目录下创建XXXActivityDaoImpl.php文件，继承GereralDaoImpl基类，调用上面创建的接口TestActivityDao.php;<?php # 这里的内容参考：https://blog.csdn.net/jyl_sh/article/details/106616322 namespace Biz\Activity\Dao\Impl; # 命名空间 use Biz\Activity\Dao\TestActivityDao; # 刚刚创建的接口文件 use Codeages\Biz\Framework\Dao\GeneralDaoImpl; # 基类 class TestActivityDaoImpl extends GeneralDaoImpl implements TestActivityDao { protected $table = 'test'; # 要操作的表名 public function declares() # 这是必须的函数 { // TODO: Implement declares() method. return array( 'timestamps' => array('created_time', 'update_time'), 'serializes' => array(), 'orderbys' => array(), 'conditions' => array( 'type = :type', ), ); } public function getById($id) # 这是接口中有的函数，必须在这里也调用，并实现增删改查的逻辑 { return $this->get($id); } }完成上面的Dao创建，下面需要创建Service。找到ActivityBizService,创建XXXXActivityService.php接口文件，不用继承任何基类；例如：TestActivityService.php<?php namespace Biz\Activity\Service; interface TestActivityService { public function getActivity($id); public function findActivitiesByIds($ids); public function findActivitiesByMediaIds($mediaIds); public function createActivity($fields); public function updateActivity($id, $fields); public function deleteActivity($id); }找到ActivityBizServiceImpl目录，创建XXXXActivityServiceImpl.php文件，继承基类BaseService，调用刚刚创建的接口文件TestActivityService。其中接口文件中有的函数必须在此文件中创建相同的函数<?php namespace Biz\Activity\Service\Impl; # 命名空间 use Biz\BaseService; # 服务基类 use Biz\Activity\Dao\TestActivityDao; #之前创建的Dao use Biz\Activity\Service\TestActivityService; # 上一步创建的服务接口文件 class TestActivityServiceImpl extends BaseService implements TestActivityService { public function getActivity($id) { // TODO: Implement getActivity() method. return $this->getTestActivityDao()->getById($id); } public function findActivitiesByIds($ids) { // TODO: Implement findActivitiesByIds() method. } public function findActivitiesByMediaIds($mediaIds) { // TODO: Implement findActivitiesByMediaIds() method. } public function createActivity($fields) { // TODO: Implement createActivity() method. } public function updateActivity($id, $fields) { // TODO: Implement updateActivity() method. } public function deleteActivity($id) { // TODO: Implement deleteActivity() method. } /** * @return TestActivityDao */ protected function getTestActivityDao() { # 使用createDao来使用之前创建的Dao return $this->createDao('Activity:TestActivityDao'); } }可以看到上一个文件底部创建了一个接口文件中不存在的函数getTestActivityDao()，这个函数用于调用之前已经创建号的Dao，其中$this->createDao('Activity:TestActivityDao')中的Activity是Dao所在的文件夹，TestActivityDao必须是已经创建好的Impl文件，即为TestActivityDaoImpl.php。在函数getActivity中通过已经创建好的getTestActivityDao()函数来调用Dao中的getById函数，其中已经实现了增删改查的逻辑。如何调用服务来实现具体的业务呢？这里我们先在控制器获取服务，在控制器中创建如下函数。该函数的功能用于调用已经创建好的服务，服务再调用Dao，Dao再执行具体的业务逻辑。# 获取具体的服务 function getTestServer() { return $this->getBiz()->service('Activity:TestActivityService'); }服务获取到后，通过如下方法调用到具体的Dao中的函数。其中getTestServer是上一步创建好的获取服务的函数，getActivity()是在服务的Impl中定义的函数，此函数中又调用了Dao中的getById函数，以达到查询的目的。$arr = $this->getTestServer()->getActivity(1); var_dump($arr);
说明:给指定图片添加指定文本的水印from PIL import Image,ImageDraw,ImageFont import base64,time def imgToBase64(image_path,string="发生异常-11"): print("图片地址: "+image_path+",正在添加水印---") try: oriImg = Image.open(image_path) addText(oriImg,image_path,string) except: print(image_path+"图片添加水印失败---") # time.sleep(1) with open(image_path, 'rb') as f: image = f.read() image_base64 = "data:image/png;base64,"+str(base64.b64encode(image), encoding='utf-8') print(image_base64) #图片加水印显示错误信息 def addText(oriImg,path,string): fontpath = "font/kt.ttf" # 64为字体大小 font = ImageFont.truetype(fontpath, 64) draw = ImageDraw.Draw(oriImg) draw.text(xy=(50,50),text=string,fill='red',font=font) # oriImg.show() oriImg.save(path) path = "J:\\Users\\username\\Pictures\\壁纸\\test.png" imgToBase64(path,"HelloWorld")
说明:基于FTP的程序自动更新，配合bat脚本执行import os,os.path,time,zipfile,socket from ftplib import FTP # 引入ftp模块 #FTP操作 class MyFtp: ftp = FTP() def __init__(self,host,port=21): try: self.ftp.connect(host,port) except: print("FTP服务器连接失败！") def login(self,username,pwd): self.ftp.set_debuglevel(0) # 打开调试级别2，显示详细信息 try: self.ftp.login(username,pwd) except: print("FTP服务器登录失败！") return False print(self.ftp.welcome) data = [] self.ftp.dir(data.append) for line in data: print("-", line) def downloadFile(self,localpath,remotepath,filename): os.chdir(localpath) # 切换工作路径到下载目录 try: self.ftp.cwd(remotepath) # 要登录的ftp目录 except: print("无法打开FTP目录！") return False self.ftp.nlst() # 获取目录下的文件 file_handle = open(filename,"wb").write # 以写模式在本地打开文件 try: self.ftp.retrbinary('RETR %s' % os.path.basename(filename),file_handle,blocksize=1024) # 下载ftp文件 except: print("从FTP下载【"+str(filename)+"】文件失败！") return False # ftp.delete（filename） # 删除ftp服务器上的文件 def close(self): self.ftp.set_debuglevel(0) # 关闭调试 self.ftp.quit() #解压缩操作 class MyZip: def __init__(self): pass # 压缩 def zip_dir(self,dirname,zipfilename): filelist = [] if os.path.isfile(dirname): filelist.append(dirname) else : for root, dirs, files in os.walk(dirname): for name in files: filelist.append(os.path.join(root, name)) zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED) for tar in filelist: arcname = tar[len(dirname):] #print arcname zf.write(tar,arcname) zf.close() # 解压 def unzip_file(self,zipfilename, unziptodir): if not os.path.exists(unziptodir): os.mkdir(unziptodir, '0777') zfobj = zipfile.ZipFile(zipfilename) for name in zfobj.namelist(): name = name.replace('\\','/') if name.endswith('/'): try: os.mkdir(os.path.join(unziptodir, name)) except: print("【"+name+"】文件夹已存在，不再创建~") continue # if 'Public.py' in name: # print("【"+name+"】文件夹不允许被替换，跳过~") # continue ext_filename = os.path.join(unziptodir, name) ext_dir= os.path.dirname(ext_filename) if not os.path.exists(ext_dir) : os.mkdir(ext_dir,'0777') print("ext_filename:"+ext_filename) try: outfile = open(ext_filename, 'w+b') except: print("【"+ext_filename+"】文件创建失败~") continue outfile.write(zfobj.read(name)) outfile.close() def unzip2_file(self,zipfilename, unziptodir): if not os.path.exists(unziptodir): os.mkdir(unziptodir, '0777') zfobj = zipfile.ZipFile(zipfilename) for name in zfobj.namelist(): name = name.replace('\\','/') if name.endswith('/'): try: os.mkdir(os.path.join(unziptodir, name)) except: print("文件夹已存在，不再创建~") ext_filename = os.path.join(unziptodir, name) ext_dir= os.path.dirname(ext_filename) if not os.path.exists(ext_dir) : os.mkdir(ext_dir,'0777') print("ext_filename:"+ext_filename) outfile = open(ext_filename, 'w+b') outfile.write(zfobj.read(name)) outfile.close() if __name__ == '__main__': socket.setdefaulttimeout(120) try: #当前文件位置 dPath = os.getcwd() zipObj = MyZip() ftp = MyFtp('127.0.0.1') ftp.login('root','root') while True: version = ftp.downloadFile(dPath+'/update','/','version.txt') if version == False : print("出现异常，1分钟后重试~") time.sleep(60) continue newV = "" oldV = "" with open(dPath+"/update/version.txt", "r") as f: # 打开文件 newV = f.read() # 读取文件 print("最新版本:"+str(newV)) with open(dPath+"/version.txt", "r") as fS: # 打开文件 oldV = fS.read() # 读取文件 print("当前版本:"+str(oldV)) if newV == "" or oldV == "": print("版本对比失败，1分钟后重试~") ftp.close() time.sleep(60) continue if float(newV) > float(oldV): print("需要更新~") cmdObj = os.popen('tasklist /nh|find /i "chrome.exe"') tasklist = cmdObj.read() cmdObj.close() if len(tasklist) > 0: print("有任务正在执行，30秒后重试~") time.sleep(30) continue os.system('taskkill /f /im cmd.exe') os.system('taskkill /f /im python.exe') zipFile = ftp.downloadFile(dPath+'/update','/','Hello.zip') if zipFile == False : print("出现异常，30秒后重试~") time.sleep(30) continue zipObj.unzip_file(dPath+'/update/Hello.zip',dPath+'\\') os.system('start Main.bat') print("更新完成~") else: print("不用更新~") print("操作完成，60秒后再次执行~") # ftp.quit() time.sleep(60) except Exception as e: print("发生不可预料的错误，日志如下:") print(e) time.sleep(9999)
说明:Chrom自动更新后，对应的Chromedriver驱动也要手动下载更新，比较麻烦。这里的程序将根据单曲Chrome的版本将最新的驱动下载到本地。import requests import re import os import zipfile import time dPath = os.getcwd() # 获取chrome版本 def get_chrome_version(): cmd = 'reg query "HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon" /v version' cmdRes = os.popen(cmd).read() #执行命令拿到版本信息 cmdList = cmdRes.split(' ') #根据空格把拿到的信息转为列表 version = cmdList[len(cmdList)-1].replace('\n', '') #只要最后一个数据就是版本号，不要其他的信息 varsionList = version.split('.') #版本号更具.转为列表 varsionList.pop() #删除列表最后一个小版本号 varsionStr = ".".join(varsionList) #组装大版本号 return varsionStr # 已有Chromedriver的版本 def get_fz_chromedriver_version(): cmd = dPath+'\chromedriver\chromedriver.exe -version' cmdRes = os.popen(cmd).read() #执行命令拿到版本信息 cmdList = cmdRes.split(' ') #根据空格把拿到的信息转为列表 return cmdList[1] def get_latest_version(url): '''查询最新的Chromedriver版本''' rep = requests.get(url).text time_list = [] # 用来存放版本时间 time_version_dict = {} # 用来存放版本与时间对应关系 result = re.compile(r'\d.*?/.*?Z').findall(rep) # 匹配文件夹（版本号）和时间 for i in result: time = i[-24:-1] # 提取时间 version = re.compile(r'.*?/').findall(i)[0] # 提取版本号 time_version_dict[time] = version # 构建时间和版本号的对应关系，形成字典 time_list.append(time) # 形成时间列表 latest_version = time_version_dict[max(time_list)][:-1] # 用最大（新）时间去字典中获取最新的版本号 return latest_version def download_driver(download_url): '''下载文件''' file = requests.get(download_url) with open("chromedriver.zip", 'wb') as zip_file: # 保存文件到脚本所在目录 zip_file.write(file.content) print('下载成功') def get_version(): '''查询系统内的Chromedriver版本''' outstd2 = os.popen('chromedriver --version').read() return outstd2.split(' ')[1] def get_path(): '''查询系统内Chromedriver的存放路径''' return dPath+'\chromedriver\\' # outstd1 = os.popen('where chromedriver').read() # return outstd1.strip('chromedriver.exe\n') def unzip_driver(path): '''解压Chromedriver压缩包到指定目录''' f = zipfile.ZipFile("chromedriver.zip",'r') for file in f.namelist(): f.extract(file, path) if __name__ == "__main__": version = get_chrome_version() # 原来是get_version() print('当前系统内的Chrome版本为：', version) url = 'http://npm.taobao.org/mirrors/chromedriver/' latest_version = get_latest_version(url) print('最新的chromedriver版本为：', latest_version) locaVersion = get_fz_chromedriver_version() print('当前FZ的chromedriver版本为：', locaVersion) if version not in latest_version: print('当前系统内的Chrome和最新Chromedriver版本不匹配，不执行更新！') else: if locaVersion == latest_version: print('已是最新版本，不执行更新！') else: print('当前系统内的Chrome和最新Chromedriver版本匹配，执行更新！') download_url = url + latest_version + '/chromedriver_win32.zip' # 拼接下载链接 download_driver(download_url) path = get_path() print('替换路径为：', path) unzip_driver(path) print('更新后的Chromedriver版本为：', get_version()) time.sleep(3)