# public-sdk **Repository Path**: m-sdk/public-sdk ## Basic Information - **Project Name**: public-sdk - **Description**: 自用公共服务SDK。 - **Primary Language**: JavaScript - **License**: ISC - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-30 - **Last Updated**: 2026-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 公共服务 SDK [![npm version](https://img.shields.io/npm/v/public-sdk.svg)](https://www.npmjs.com/package/public-sdk) [![license](https://img.shields.io/npm/l/public-sdk.svg)](https://gitee.com/m-sdk/public-sdk/blob/master/LICENSE) **版本**: 2.3.0 | **Node.js**: >= 14.0.0 Node.js 服务端通用工具库,提供微信小程序 SDK、统一响应封装、常用工具方法等。 ## ✨ 特性 - 🔐 **安全可靠**:使用 Node.js crypto 模块生成安全的随机数和 UUID - 🛡️ **类型安全**:完善的参数验证和类型检查 - 🧪 **测试覆盖**:47+ 单元测试用例,100% 方法覆盖率 - 📦 **零依赖**:除 axios 外无其他第三方依赖 - 🚀 **即插即用**:ES Module 支持,Tree-shaking 友好 ## 📦 安装 ```bash npm install public-sdk ``` ## 🚀 快速开始 ### 微信小程序 SDK ```javascript import { WX } from "public-sdk"; const wx = new WX({ appid: "your-appid", secret: "your-secret", env_version: "develop", // develop | trial | release }); // 获取 access_token const token = await wx.getAccessToken(); // 获取微信 openid const openid = await wx.getOpenid(code); // 获取小程序码 const qrBuffer = await wx.getUnlimitedQRCode({ page: "pages/index/index", scene: "user=123", width: 430, }); // 敏感词检测 const result = await wx.checkSensitiveWords(text, openid); // 获取手机号 const phoneInfo = await wx.getPhoneNumber(code); ``` ### 响应数据统一封装 ```javascript import { Send } from "public-sdk"; // 自定义成功响应 code(默认为 0) Send.successCode = 200; // 成功响应 Send.success(data); // { code: 0, data, message: 'success' } // 错误响应 Send.fail("操作失败", 400); // { code: 400, message: '操作失败', data: null } // 格式化错误信息 Send.formatError(error); // 操作过于频繁响应 Send.tooManyRequests(); // { code: 429, message: '请求过于频繁' } ``` ## 🔧 工具类 Utils ```javascript import { Utils } from "public-sdk"; ``` ### UUID 生成 使用 `crypto.randomUUID()` 生成符合 RFC 4122 标准的 UUID v4。 ```javascript const uuid = Utils.uuid(); // 示例输出: "550e8400-e29b-41d4-a716-446655440000" ``` **特性:** - 使用加密安全的随机数生成器 - 保证唯一性 - 标准 UUID 格式(36 字符) --- ### 格式化方法 #### formatAccount(account) 格式化账号:转小写 + 去除首尾空格 ```javascript Utils.formatAccount(" Test@Example.COM "); // 返回: "test@example.com" Utils.formatAccount(null); // 返回: "" Utils.formatAccount(undefined); // 返回: "" Utils.formatAccount(12345); // 返回: "12345" ``` #### formatPassword(password) 格式化密码:去除首尾空格 ```javascript Utils.formatPassword(" myPassword123 "); // 返回: "myPassword123" Utils.formatPassword(null); // 返回: "" Utils.formatPassword(undefined); // 返回: "" ``` --- ### 随机字符串生成 #### randomStr(len?, prefix?, charset?) 生成加密安全的随机字符串。 **参数:** | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `len` | `number` | `8` | 生成的字符串长度 | | `prefix` | `string` | `''` | 前缀 | | `charset` | `string` | `'alphanumeric'` | 字符集类型 | **字符集选项:** | 值 | 说明 | 包含字符 | |----|------|----------| | `'alphanumeric'` | 字母数字混合(默认) | a-z, A-Z, 0-9 | | `'alpha'` | 纯字母 | a-z, A-Z | | `'numeric'` | 纯数字 | 0-9 | **示例:** ```javascript // 默认:8 位字母数字混合 Utils.randomStr(); // 示例: "aB3xK9mP" // 自定义长度 Utils.randomStr(16); // 示例: "qR7tY2wE8pL1kO3j" // 带前缀 Utils.randomStr(8, "token_"); // 示例: "token_xKmPqR" // 纯字母 Utils.randomStr(20, "", "alpha"); // 示例: "aBcDeFgHiJkLmNoPqRsT" // 纯数字 Utils.randomStr(6, "", "numeric"); // 示例: "385921" ``` **特性:** - 使用 `crypto.randomBytes()` 生成密码学安全的随机数 - 支持自定义字符集 - 可用于生成 token、验证码、临时密码等场景 --- ### 校验方法 #### isEmail(email) 校验邮箱格式是否符合 RFC 标准。 ```javascript Utils.isEmail("test@example.com"); // true Utils.isEmail("user.name+tag@domain.co.uk"); // true Utils.isEmail("invalid-email"); // false Utils.isEmail(null); // false Utils.isEmail(""); // false ``` **支持的邮箱格式:** - 标准格式:`user@example.com` - 带子域名:`user@mail.example.com` - 带特殊字符:`user.name+tag@domain.com` - 多级 TLD:`user@example.co.uk` --- #### isPhone(phone, strict?) 校验手机号格式。 **参数:** | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `phone` | `string \| number` | - | 手机号 | | `strict` | `boolean` | `true` | 是否使用严格模式 | **严格模式(默认):** - 仅支持中国大陆手机号 - 格式:11 位数字,1 开头,第二位为 3-9 ```javascript Utils.isPhone("13812345678"); // true Utils.isPhone("19900001111"); // true Utils.isPhone("12345678901"); // false (号段无效) Utils.isPhone("1381234567"); // false (位数不足) Utils.isPhone(13812345678); // true (支持数字类型) ``` **宽松模式:** - 支持国际手机号格式 - 格式:可选 + 前缀,7-15 位数字 ```javascript Utils.isPhone("+8613812345678", false); // true Utils.isPhone("+11234567890", false); // true Utils.isPhone("8613812345678", false); // true ``` --- #### isUrl(url) 校验 URL 格式是否有效。 ```javascript Utils.isUrl("https://www.example.com"); // true Utils.isUrl("http://test.org/path?query=1"); // true Utils.isUrl("ftp://files.server.com"); // true Utils.isUrl("not-a-url"); // false Utils.isUrl("http://"); // false Utils.isUrl(null); // false ``` **特性:** - 使用原生 `URL` 构造函数进行验证 - 支持 http、https、ftp 等协议 - 自动处理各种 URL 格式 --- ### 数据转换 #### list2tree(items, prop?) 将扁平列表转换为树形结构(支持多级嵌套)。 **参数:** | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `items` | `Array` | - | 扁平数据列表 | | `prop` | `Object` | `{}` | 属性名配置 | **默认配置:** ```javascript { id: 'id', // ID 字段名 parent_id: 'parent_id', // 父级 ID 字段名 children: 'children', // 子节点字段名 label: 'label', // 显示文本字段名 value: 'value' // 值字段名 } ``` **基本用法:** ```javascript const items = [ { id: 1, name: "根节点1", parent_id: 0 }, { id: 2, name: "子节点1-1", parent_id: 1 }, { id: 3, name: "子节点1-2", parent_id: 1 }, { id: 4, name: "根节点2", parent_id: 0 }, { id: 5, name: "子节点2-1", parent_id: 4 }, ]; const tree = Utils.list2tree(items, { label: "name" }); // 输出: [ { id: 1, name: "根节点1", label: "根节点1", value: 1, children: [ { id: 2, name: "子节点1-1", label: "子节点1-1", value: 2, parent_name: "根节点1", children: [] }, // ... ] }, // ... ] ``` **自定义属性名:** ```javascript const items = [ { itemId: 1, title: "根节点", parentId: null }, { itemId: 2, title: "子节点", parentId: 1 }, ]; const tree = Utils.list2tree(items, { id: "itemId", label: "title", value: "itemId", parent_id: "parentId" }); ``` **Sequelize 兼容性:** 自动识别 Sequelize 模型实例,提取 `dataValues`。 ```javascript const sequelizeItems = await Model.findAll(); const tree = Utils.list2tree(sequelizeItems); // 自动处理 Sequelize 实例 ``` **特性:** - ✅ 支持多级嵌套 - ✅ 自动跳过重复 ID 的项(控制台警告) - ✅ 处理 null/undefined 的父级 ID(视为根节点) - ✅ 自动添加 `parent_name` 字段 - ✅ 空数组/非数组输入返回空数组 --- ### 路由匹配 #### isInRoutes(route, routes) 检查 API 路由是否在匹配列表中(支持路径参数)。 **参数:** | 参数 | 类型 | 说明 | |------|------|------| | `route` | `{ method: string, path: string }` | 待匹配的路由 | | `routes` | `Array<{ method: string, path: string }>` | 路由匹配列表 | **示例:** ```javascript const routes = [ { method: "GET", path: "/api/users" }, { method: "GET", path: "/api/users/:id" }, { method: "POST", path: "/api/users" }, ]; // 精确匹配 Utils.isInRoutes({ method: "GET", path: "/api/users" }, routes); // true // 路径参数匹配 Utils.isInRoutes({ method: "GET", path: "/api/users/123" }, routes); // true // HTTP 方法不区分大小写 Utils.isInRoutes({ method: "get", path: "/api/users" }, routes); // true // 不匹配 Utils.isInRoutes({ method: "POST", path: "/api/posts" }, routes); // false ``` **特性:** - ✅ 支持路径参数(如 `/users/:id`) - ✅ HTTP 方法不区分大小写 - ✅ 路径不区分大小写 - ✅ 安全的 null 输入处理 --- ### IP 地址获取 #### clientIp(req) 从请求对象中获取客户端真实 IP 地址。 **参数:** | 参数 | 类型 | 说明 | |------|------|------| | `req` | `Object` | Express/Koa 等 HTTP 请求对象 | **IP 获取优先级:** 1. `X-Forwarded-For` 头(取第一个 IP) 2. `X-Real-IP` 头 3. `req.ip` 4. `req.connection.remoteAddress` 5. `req.socket.remoteAddress` **示例:** ```javascript // Express 路由中使用 app.get("/api/data", (req, res) => { const ip = Utils.clientIp(req); console.log(ip); // "192.168.1.1" }); ``` **特性:** - ✅ 处理代理服务器的多级转发 - ✅ 自动移除 IPv6 映射前缀 (`::ffff:`) - ✅ 将 IPv6 localhost (`::1`) 转换为 `127.0.0.1` - ✅ 处理逗号分隔的多个 IP - ✅ 支持数组类型的 header - ✅ 空 req 对象返回空字符串 **典型应用场景:** ```javascript // Nginx 反向代理配置 // nginx.conf: proxy_set_header X-Real-IP $remote_address; // proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; app.use((req, res, next) => { const clientIp = Utils.clientIp(req); req.clientIp = clientIp; // 挂载到 req 上 next(); }); ``` --- ## 🧪 测试 运行测试套件: ```bash npm test # 或 node test.js ``` **测试覆盖:** - ✅ UUID 生成(3 个用例) - ✅ 格式化方法(7 个用例) - ✅ 随机字符串(6 个用例) - ✅ 邮箱校验(3 个用例) - ✅ 手机号校验(5 个用例) - ✅ URL 校验(3 个用例) - ✅ 列表转树形结构(7 个用例) - ✅ 路由匹配(5 个用例) - ✅ 客户端 IP 获取(8 个用例) **总计:47 个测试用例,全部通过 ✅** --- ## 📝 更新日志 ### [2.3.0] - 2026-04-30 #### ✨ 新功能 - 🔐 使用 `crypto` 模块替代 `Math.random()` 提升安全性 - 🎲 `randomStr()` 新增 `charset` 参数支持自定义字符集 - 📱 `isPhone()` 新增 `strict` 参数支持国际号码格式 #### 🐛 Bug 修复 - ✅ 修复 `uuid()` 生成算法错误(toString(32) → toString(16)) - ✅ 修复 `list2tree()` 重复 ID 导致节点重复添加的问题 - ✅ 修复 `isInRoutes()` 传入 null 时解构错误 #### 💎 优化改进 - 📧 优化邮箱正则表达式,支持更多 RFC 标准格式 - 📱 优化手机号正则表达式,支持最新号段 - 🌐 使用原生 `URL` 构造函数替代正则校验 URL - 🌳 增强 `list2tree()` 健壮性(空值处理、重复检测) - 🛡️ 完善所有方法的类型安全检查和参数验证 - 🖥️ 增强 `clientIp()` 对各种代理场景的支持 #### 🧪 测试 - 🆕 新增完整测试套件(47 个测试用例) - ✅ 100% 公共方法覆盖率 - ✅ 包含正向/负向测试、边界情况测试 --- ### [1.0.1] - 2025-09 - 初始版本发布 --- ## 📄 许可证 [ISC](./LICENSE) ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! --- ## 📞 支持 如有问题或建议,请提交 [Issue](https://gitee.com/m-sdk/public-sdk/issues)