ssh / common-util
PHP 常用工具类集合,包含阿里云IoT/OSS/SMS、国密SM2/SM3/SM4加解密、AES加解密、商米云打印等工具
Requires
- php: >=8.1
- ext-curl: *
- ext-gmp: *
- ext-openssl: *
- alibabacloud/credentials: ^1.2
- alibabacloud/dysmsapi-20170525: ^4.0
- alibabacloud/iot-20180120: ^5.0
- alibabacloud/oss-v2: ^0.4.0
- lpilp/guomi: ^2.0
- monolog/monolog: ^3.10
- psr/log: ^2.0 || ^3.0
- workerman/webman-framework: ^2.0
Requires (Dev)
- alibabacloud/aliyun-log-php-sdk: ^0.7.0
- phpunit/phpunit: ^10.0
- webman/database: ^2.1
- webman/redis: ^2.1
Suggests
- ext-gd: 商米云打印图片处理需要GD扩展
- ext-redis: IdGenerator 需要 Redis 扩展用于原子自增
- alibabacloud/aliyun-log-php-sdk: SlsHandler 将日志投递到阿里云 SLS 日志服务
- ssh/spms-contracts: 业务枚举与常量(AppType/RoleConstant)已迁移至该零依赖契约包
- webman/database: IdGenerator 使用 support\Db 作为 MySQL 兜底
- webman/redis: IdGenerator 使用 support\Redis 访问 Redis
README
基于 Webman 框架的 PHP 公共组件库,为微服务提供统一的响应输出、错误码体系、结构化日志、HTTP 中间件、鉴权加密、多租户数据库路由、审计日志,以及阿里云 IoT/OSS/SMS、国密 SM2/SM3/SM4、AES 加解密、商米云打印等工具。
安装
composer require ssh/common-util
环境要求
- PHP >= 8.1
- workerman/webman-framework ^2.0
- monolog/monolog ^3.10
- ext-curl、ext-openssl(1.1.1+,SM4 需要)、ext-gmp
- ext-gd(商米云打印图片处理需要,可选)
- ext-redis + webman/redis + webman/database(IdGenerator 需要,可选)
功能一览
响应与错误码
| 类名 | 说明 |
|---|---|
ResponseUtil |
统一响应输出(含业务错误码、异常转响应) |
ErrorCode |
统一错误码常量与 HTTP 状态码映射 |
Exception\BusinessException |
业务异常(携带错误码,支持字符串快捷构造) |
结构化日志
| 类名 | 说明 |
|---|---|
Log\JsonFormatter |
JSON 结构化日志格式化器(生产) |
Log\DevLineFormatter |
人类可读行格式化器(开发) |
Log\ServiceProcessor |
注入 service / server_ip / server_port 字段 |
Log\RequestIdProcessor |
注入 request_id / client_ip / client_ua / app_id 字段 |
Log\UserIdProcessor |
注入 user_id / org_id 字段 |
Log\SlsHandler |
阿里云 SLS 日志投递(环境变量控制) |
Bootstrap\LaravelLog |
SQL 执行日志监听器 |
HTTP 中间件
| 类名 | 说明 |
|---|---|
Middleware\RequestIdMiddleware |
请求追踪(X-Request-Id 透传 + 访问日志) |
Middleware\InternalMiddleware |
内部调用鉴权(Token / 内网 IP 白名单) |
Middleware\StaticFile |
静态文件安全拦截 |
鉴权与安全
| 类名 | 说明 |
|---|---|
Auth\JwtService |
JWT(HS256)签发与校验 |
Security\CryptoService |
AES-256-GCM 加解密(敏感字段存储) |
审计日志
| 类名 | 说明 |
|---|---|
Audit\AuditLogService |
审计日志服务 |
Audit\AuditLog |
审计日志模型(sys_audit_log) |
Trait
| 类名 | 说明 |
|---|---|
Traits\HasTimestampColumns |
统一时间戳列名(gmt_create/gmt_modified/delete_time) |
业务枚举与常量(
AppType、RoleConstant)已迁移至零依赖契约包ssh/spms-contracts。
通用工具
| 类名 | 说明 |
|---|---|
IdGenerator |
唯一号生成器(Redis 原子自增 + MySQL 兜底) |
AliIotUtil |
阿里云 IoT 消息推送、设备管理 |
AliIotAmqpUtil |
阿里云 IoT AMQP 认证凭证生成 |
AliOssUtil |
阿里云 OSS 对象存储操作 |
AliSmsUtil |
阿里云短信发送 |
SmCryptoUtil |
国密 SM2/SM3/SM4 加解密 |
SymmetricEncoder |
AES 加解密(兼容 Java SHA1PRNG 密钥派生) |
SunmiCloudPrinter |
商米云打印机 ESC/POS 指令 |
ResponseUtil - 统一响应输出
基于 Webman 的 support\Response,构建统一格式的响应对象,可直接作为控制器返回值。
use Ssh\CommonUtil\ResponseUtil; // 基础格式: ['code' => 200, 'msg' => 'success', 'data' => [...]] $result = ResponseUtil::toArray(200, 'success', ['id' => 1]); // 返回 webman Response 对象(直接在控制器中 return) return ResponseUtil::success('操作成功', ['id' => 1]); // 200 return ResponseUtil::fail('参数错误'); // 400 return ResponseUtil::error(); // 500 return ResponseUtil::notFound(); // 404 return ResponseUtil::unauthenticated(); // 401 return ResponseUtil::notBind(); // 402 return ResponseUtil::forbidden(); // 403 return ResponseUtil::notFoundController(); // 404 return ResponseUtil::notImplemented(); // 501 return ResponseUtil::serviceUnavailable(); // 503 return ResponseUtil::tooManyRequests(); // 429 // 自定义业务码(HTTP 200,body 携带业务 code,如 4002 需图形验证码) return ResponseUtil::code(4002, '需要图形验证码', ['captcha' => true]); // 业务错误码响应(HTTP 状态码由 ErrorCode 映射表决定) return ResponseUtil::failWithCode(ErrorCode::TOKEN_EXPIRED);
error()、notFound()、unauthenticated()等方法内部使用trans()读取国际化翻译,需配合symfony/translation使用。
异常转响应
use Ssh\CommonUtil\ResponseUtil; use Ssh\CommonUtil\Exception\BusinessException; // 从 BusinessException 创建响应 return ResponseUtil::fromException(new BusinessException(40401, '用户不存在')); // 统一异常处理:BusinessException 返回对应业务码,其他异常返回 500 return ResponseUtil::handleException($e);
ErrorCode - 统一错误码
错误码规则:400xx 参数错误、401xx 认证失败、403xx 授权失败、404xx 资源不存在、409xx 业务冲突、500xx 服务端错误、502xx 下游调用失败。
| 常量 | 值 | HTTP | 说明 |
|---|---|---|---|
SUCCESS |
200 | 200 | 成功 |
PARAM_ERROR |
40000 | 400 | 参数错误(类目基码) |
PARAM_INVALID |
40001 | 400 | 参数校验失败 |
PARAM_MISSING |
40002 | 400 | 缺少必填参数 |
UNAUTHENTICATED |
40100 | 401 | 未登录/未认证 |
TOKEN_EXPIRED |
40101 | 401 | Token 已过期 |
TOKEN_INVALID |
40102 | 401 | Token 无效 |
FORBIDDEN |
40300 | 403 | 无权限 |
ACCOUNT_DISABLED |
40301 | 403 | 账号已禁用 |
ACCOUNT_LOCKED |
40302 | 403 | 账号已锁定 |
NOT_FOUND |
40400 | 404 | 资源不存在 |
USER_NOT_FOUND |
40401 | 404 | 用户不存在 |
THIRD_PARTY_NOT_FOUND |
40402 | 404 | 第三方账号记录不存在 |
DATA_EXISTS |
40900 | 409 | 数据已存在 |
VERSION_CONFLICT |
40901 | 409 | 数据版本冲突(乐观锁) |
STATUS_CONFLICT |
40902 | 409 | 状态冲突 |
SERVER_ERROR |
50000 | 500 | 系统内部错误 |
SERVICE_CALL_FAILED |
50200 | 502 | 下游服务调用超时或异常 |
SERVICE_UNAVAILABLE |
50300 | 503 | 服务不可用 |
use Ssh\CommonUtil\ErrorCode; $httpStatus = ErrorCode::toHttpStatus(ErrorCode::TOKEN_EXPIRED); // 401
BusinessException - 业务异常
use Ssh\CommonUtil\Exception\BusinessException; use Ssh\CommonUtil\ErrorCode; // 两种构造方式 throw new BusinessException(ErrorCode::USER_NOT_FOUND, '用户不存在'); // 显式错误码 throw new BusinessException('机构不存在'); // 字符串 → 默认业务码 400 // 快捷工厂方法 throw BusinessException::notFound('订单不存在'); // 40400 throw BusinessException::userNotFound(); // 40401 throw BusinessException::dataExists('手机号已注册'); // 40900 throw BusinessException::versionConflict(); // 40901 // 获取错误码 $e->getErrorCode();
结构化日志
提供双格式输出,共用同一套 Processor,按环境切换:
- 生产环境:
JsonFormatter输出单行 JSON,供 ELK/Loki/SLS 等采集系统解析 - 开发环境:
DevLineFormatter输出人类可读的行格式,extra 关键字段内联为标签
生产格式示例:
{"time":"2026-07-01 12:00:00","level_name":"INFO","service":"spms-auth","server_ip":"172.18.0.3","server_port":"8781","request_id":"a1b2c3d4","user_id":"1001","org_id":"10","client_ip":"203.0.113.5","app_id":"wx123456789","client_ua":"Mozilla/5.0 ...","message":"登录成功","context":{}}
开发格式示例(无值的标签自动省略):
[2026-07-01 12:00:00] webman.INFO [svc=spms-auth req=a1b2c3d4 uid=1001 org=10 ip=203.0.113.5]: 登录成功 {"uid":1001}
Webman 配置示例(双环境自动切换)
config/log.php:
use Monolog\Level; use Ssh\CommonUtil\Log\JsonFormatter; use Ssh\CommonUtil\Log\DevLineFormatter; use Ssh\CommonUtil\Log\ServiceProcessor; use Ssh\CommonUtil\Log\RequestIdProcessor; use Ssh\CommonUtil\Log\UserIdProcessor; // 生产环境输出 JSON 供采集系统解析,其他环境输出可读行格式 $isProd = getenv('APP_ENV') === 'production'; return [ 'default' => [ 'handlers' => [ [ 'class' => Monolog\Handler\StreamHandler::class, 'constructor' => [ runtime_path() . '/logs/app.log', Level::Debug, ], 'formatter' => [ 'class' => $isProd ? JsonFormatter::class : DevLineFormatter::class, 'constructor' => [], ], ], ], 'processors' => [ new ServiceProcessor('spms-auth', getenv('SERVER_IP') ?: '-', getenv('SERVER_PORT') ?: '-'), new RequestIdProcessor(), new UserIdProcessor(), ], ], ];
在中间件/鉴权中注入上下文
use Ssh\CommonUtil\Log\RequestIdProcessor; use Ssh\CommonUtil\Log\UserIdProcessor; // 请求开始 RequestIdProcessor::setRequestId($requestId); RequestIdProcessor::setClientIp($request->getRealIp()); RequestIdProcessor::setClientUa($request->header('user-agent', '')); RequestIdProcessor::setAppId($appId); // 鉴权通过后 UserIdProcessor::setUserId((string) $userId); UserIdProcessor::setOrgId((string) $orgId); // 请求结束(RequestIdMiddleware 已自动处理) RequestIdProcessor::reset(); UserIdProcessor::reset();
SlsHandler - 阿里云 SLS 日志投递
将日志推送到阿里云日志服务(SLS),通过环境变量控制开关与配置,未配置时零影响:
| 环境变量 | 必填 | 说明 |
|---|---|---|
SLS_ENABLED |
✓ | 总开关,=1 启用 |
SLS_ENDPOINT |
✓ | 服务接入点,如 cn-shenzhen.log.aliyuncs.com |
SLS_ACCESS_KEY_ID / SLS_ACCESS_KEY_SECRET |
✓ | AccessKey |
SLS_PROJECT |
✓ | Project 名称 |
SLS_LOGSTORE |
✓ | Logstore 名称 |
SLS_TOPIC |
日志主题,默认空 | |
SLS_SOURCE |
日志来源,默认主机名 | |
SLS_BATCH_SIZE |
批量条数阈值,默认 50 | |
SLS_FLUSH_INTERVAL |
最长刷出间隔(秒),默认 3 | |
SLS_LOG_LEVEL |
最低上报级别,默认 WARNING |
特性:
- 批量发送:缓冲达到条数阈值或时间阈值才调用
putLogs,避免逐条远程写入阻塞请求 - 级别分流:默认只上报 WARNING+,DEBUG/INFO 留本地文件,控制 SLS 成本
- 失败降级:发送异常吞掉并写入 PHP 错误日志,不影响业务
- 字段一致:与
JsonFormatter同构,SLS 侧可直接按字段建索引
需安装 SDK:composer require alibabacloud/aliyun-log-php-sdk
config/log.php 条件挂载:
use Ssh\CommonUtil\Log\SlsHandler; $handlers = [ [ 'class' => Monolog\Handler\StreamHandler::class, 'constructor' => [runtime_path() . '/logs/app.log', Monolog\Level::Debug], 'formatter' => [ 'class' => $isProd ? JsonFormatter::class : DevLineFormatter::class, 'constructor' => [], ], ], ]; // SLS_ENABLED=1 且配置齐全时自动追加 SLS Handler if (SlsHandler::isConfigured()) { $handlers[] = [ 'class' => SlsHandler::class, 'constructor' => [ getenv('SLS_ENDPOINT'), getenv('SLS_ACCESS_KEY_ID'), getenv('SLS_ACCESS_KEY_SECRET'), getenv('SLS_PROJECT'), getenv('SLS_LOGSTORE'), getenv('SLS_TOPIC') ?: '', getenv('SLS_SOURCE') ?: '', (int) (getenv('SLS_BATCH_SIZE') ?: 50), (float) (getenv('SLS_FLUSH_INTERVAL') ?: 3), Monolog\Level::fromName(strtoupper(getenv('SLS_LOG_LEVEL') ?: 'WARNING')), ], ]; } return [ 'default' => [ 'handlers' => $handlers, 'processors' => [ new ServiceProcessor('spms-auth', getenv('SERVER_IP') ?: '-', getenv('SERVER_PORT') ?: '-'), new RequestIdProcessor(), new UserIdProcessor(), ], ], ];
也可直接用环境变量工厂创建:SlsHandler::fromEnv()(配置缺失时抛异常)。
LaravelLog - SQL 执行日志
实现 Webman\Bootstrap,通过 Db::listen 捕获所有 SQL 写入日志通道(自动替换绑定参数、过滤 select 1 心跳)。
日志通道可配置,优先级:setChannel() 运行时覆盖 > 环境变量 SQL_LOG_CHANNEL > 默认 sql:
// 方式1:环境变量 SQL_LOG_CHANNEL=sql_debug // 方式2:代码中指定(需在服务启动前调用,如 config/bootstrap.php 顶部) use Ssh\CommonUtil\Bootstrap\LaravelLog; LaravelLog::setChannel('sql_debug');
注意:所配置的通道需在
config/log.php中定义对应的 handler。
config/bootstrap.php 中注册:
return [ // ... Ssh\CommonUtil\Bootstrap\LaravelLog::class, ];
HTTP 中间件
config/middleware.php 中注册:
use Ssh\CommonUtil\Middleware\RequestIdMiddleware; use Ssh\CommonUtil\Middleware\InternalMiddleware; return [ '' => [ RequestIdMiddleware::class, ], // 内部接口路由单独挂载 'internal' => [ InternalMiddleware::class, ], ];
RequestIdMiddleware - 请求追踪
- 从
X-Request-Id请求头读取(支持网关透传),无则生成 32 位十六进制 ID - 注入
client_ip/client_ua到RequestIdProcessor - 请求结束记录 HTTP 访问日志(方法、URI、状态码、耗时)并重置请求级静态变量
- 响应头回写
X-Request-Id,便于全链路追踪
InternalMiddleware - 内部调用鉴权
校验请求来源是否为内部服务调用,满足任一条件即放行:
- 请求头
X-Internal-Token与环境变量INTERNAL_TOKEN_SECRET匹配(推荐) - 客户端 IP 属于内网段(127/8、10/8、172.16/12、192.168/16,备用)
StaticFile - 静态文件拦截
拒绝访问路径中带 /. 的隐藏文件请求(Webman 默认实现)。
JwtService - JWT 签发与校验
HS256 轻量实现,密钥取环境变量 JWT_SECRET。双 token 约定:机构 token(scope=org,claims:uid/org_id/role)、平台 token(scope=platform,claims:uid/role_id),校验时必须检查 scope 防止 token 混用。
use Ssh\CommonUtil\Auth\JwtService; // 签发(默认有效期 7200 秒) $token = JwtService::sign([ 'scope' => 'org', 'uid' => 1001, 'org_id' => 10, 'role' => 'admin', ], ttl: 3600); // 校验(失败或过期返回 null) $payload = JwtService::verify($token); if ($payload && $payload['scope'] === 'org') { // 鉴权通过 }
CryptoService - AES-256-GCM 加解密
用于机构库密码、设备 Secret 等敏感字段的存储加密。密钥取 env('APP_KEY') 的 sha256 前 32 字节,存储格式 base64(iv12 + tag16 + ciphertext)。
注意:与
SymmetricEncoder(AES-128-ECB,Java 兼容)是两套算法,勿混用。
use Ssh\CommonUtil\Security\CryptoService; $encrypted = CryptoService::encrypt('db-password'); $plain = CryptoService::decrypt($encrypted);
审计日志
记录关键业务操作的 who/what/when/where/result,支持两种存储驱动,通过环境变量 AUDIT_DRIVER 切换:
| 驱动 | 环境变量 | 说明 |
|---|---|---|
db |
默认 | 写入 sys_audit_log 表,多微服务共享同一数据库,通过 service 字段区分来源 |
log |
AUDIT_DRIVER=log |
写入 audit 日志通道(本地文件 + SLS),无需建表 |
db 驱动:表结构
CREATE TABLE `sys_audit_log` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `service` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '服务名', `module` VARCHAR(50) NOT NULL COMMENT '功能模块', `action` VARCHAR(50) NOT NULL COMMENT '操作类型', `method` VARCHAR(10) NOT NULL DEFAULT '' COMMENT 'HTTP 方法', `uri` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '请求路径', `params` JSON NULL COMMENT '请求参数(已脱敏)', `user_id` BIGINT NULL COMMENT '操作人ID', `org_id` BIGINT NULL COMMENT '机构ID', `client_ip` VARCHAR(45) NOT NULL DEFAULT '-', `client_ua` VARCHAR(512) NOT NULL DEFAULT '-', `app_id` VARCHAR(64) NOT NULL DEFAULT '', `request_id` VARCHAR(64) NOT NULL DEFAULT '-', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0=失败 1=成功', `error_msg` VARCHAR(500) NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`), KEY `idx_module_action` (`module`, `action`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作审计日志';
log 驱动:audit 通道配置
在 config/log.php 增加 audit 通道,本地文件兜底(防丢)+ SLS 集中化(SLS 可开 WORM 合规保留,防篡改):
'audit' => [ 'handlers' => [ // 本地文件兜底,JSON 格式便于采集器重传 [ 'class' => Monolog\Handler\StreamHandler::class, 'constructor' => [runtime_path() . '/logs/audit.log', Monolog\Level::Debug], 'formatter' => ['class' => JsonFormatter::class, 'constructor' => []], ], // SLS 集中化:注意 level 必须为 Debug,审计含 INFO 级别, // 不能复用 SLS_LOG_LEVEL 默认的 WARNING,否则会被过滤 [ 'class' => SlsHandler::class, 'constructor' => [ getenv('SLS_ENDPOINT'), getenv('SLS_ACCESS_KEY_ID'), getenv('SLS_ACCESS_KEY_SECRET'), getenv('SLS_PROJECT'), getenv('SLS_LOGSTORE_AUDIT') ?: getenv('SLS_LOGSTORE'), 'audit', getenv('SLS_SOURCE') ?: '', 50, 3.0, Monolog\Level::Debug, ], ], ], 'processors' => [ new ServiceProcessor('spms-auth', getenv('SERVER_IP') ?: '-', getenv('SERVER_PORT') ?: '-'), new RequestIdProcessor(), new UserIdProcessor(), ], ],
审计记录成功为 INFO、失败为 WARNING,业务字段(module/action/method/uri/params/status/error_msg)在 context,上下文字段由 Processor 自动注入 extra,SLS 侧可直接按字段索引查询。
用法
use Ssh\CommonUtil\Audit\AuditLogService; // 成功操作 AuditLogService::record('用户管理', '创建', $request->all()); // 失败操作 AuditLogService::record('用户管理', '创建', $request->all(), 0, $e->getMessage());
user_id/org_id不传时自动从UserIdProcessor上下文获取- 请求上下文(client_ip / client_ua / app_id / request_id)自动注入
- 敏感字段(password / token / app_secret 等)自动脱敏为
**** - fire-and-forget:写入失败仅记 warning 日志,不影响业务
Trait
HasTimestampColumns - 时间戳列名
use Ssh\CommonUtil\Traits\HasTimestampColumns; class User extends Model { use HasTimestampColumns; // CREATED_AT = 'gmt_create',UPDATED_AT = 'gmt_modified',DELETED_AT = 'delete_time' }
原
Enum\AppType、Enum\RoleConstant已迁移至零依赖契约包ssh/spms-contracts,请改为use Ssh\SpmsContracts\Enum\AppType/use Ssh\SpmsContracts\Enum\RoleConstant。
IdGenerator - 唯一号生成器
基于 Redis 原子自增 + MySQL 兜底的分布式唯一号生成器,支持 Swoole 协程异步回写。
生成格式: YYYYMMDD + 类型码(2位) + 当日序号(补零)
示例: 202607140100000001(18位,类型 01=用户,序号 1)
MySQL 表结构
CREATE TABLE `sys_id_sequence` ( `type_code` VARCHAR(10) NOT NULL COMMENT '类型码', `biz_date` VARCHAR(8) NOT NULL COMMENT '业务日期 YYYYMMDD', `current_seq` BIGINT NOT NULL DEFAULT 0 COMMENT '当前序号', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`type_code`, `biz_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='唯一号序列表';
基本用法
use Ssh\CommonUtil\IdGenerator; // 生成单个唯一号(类型名或类型码方式) $id = IdGenerator::next('user'); $id = IdGenerator::next('01'); // => "202607140100000001" // 指定总长度(默认 18 位) $id = IdGenerator::next('org', totalLength: 20); // 指定日期 $id = IdGenerator::next('user', date: '2026-07-15'); // 批量生成(1-1000) $ids = IdGenerator::batch('user', count: 10);
内置类型码
| 类型名 | 类型码 | 常量 |
|---|---|---|
user |
01 |
IdGenerator::TYPE_USER |
org |
02 |
IdGenerator::TYPE_ORG |
app |
03 |
IdGenerator::TYPE_APP |
device |
04 |
IdGenerator::TYPE_DEVICE |
也支持直接传入 2 位数字类型码(如 '99')来自定义类型。
解析唯一号
$info = IdGenerator::parse('202607140100000001'); // => ['date' => '2026-07-14', 'type_code' => '01', 'sequence' => 1, 'type_name' => 'user']
工作原理
- Redis 优先:通过
INCR原子自增获取序号,高性能无锁 - Redis 初始化校准:首次创建 key 时从 MySQL 读取最大序号,防止 Redis 重启后序号冲突
- 异步回写 MySQL:Redis 生成成功后异步回写
sys_id_sequence表保持同步(Swoole 协程优先,无协程时同步写入) - MySQL 兜底:Redis 不可用时自动回退到 MySQL
INSERT ... ON DUPLICATE KEY UPDATE原子自增
AliIotUtil - 阿里云 IoT 消息推送与设备管理
需要配置环境变量:
ALIBABA_CLOUD_ACCESS_KEY_ID=your-key-id
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-key-secret
use Ssh\CommonUtil\AliIotUtil; // 发送消息到单个设备 $result = AliIotUtil::publish( productKey: 'your-product-key', deviceName: 'your-device-name', payload: ['cmd' => 'restart'], // 数组或字符串 iotInstanceId: 'your-instance-id', topic: 'get', // 自定义 Topic 后缀,默认 'get' qos: 0 // QoS0 或 QoS1 ); // 批量发送消息(deviceName 必须为数组) $result = AliIotUtil::batchPublish( productKey: 'your-product-key', deviceName: ['device1', 'device2'], payload: ['cmd' => 'update'], iotInstanceId: 'your-instance-id' ); // 广播消息 $result = AliIotUtil::pubBroadcast('your-product-key', ['msg' => 'hello all']); // RPC 同步调用 / 异步 RPC 调用 $result = AliIotUtil::rRpc('product-key', 'device-name', ['cmd' => 'status'], timeout: 8000); $result = AliIotUtil::asyncRRpc('product-key', 'device-name', ['cmd' => 'notify']); // 设备管理 $result = AliIotUtil::registerDevice('product-key', 'device-name', 'nickname'); $result = AliIotUtil::queryDeviceInfo('product-key', 'device-name'); $result = AliIotUtil::queryDeviceDetail('product-key', 'device-name'); $result = AliIotUtil::getDeviceStatus('product-key', 'device-name'); $result = AliIotUtil::deleteDevice('product-key', 'device-name'); // ClientId 管理 $result = AliIotUtil::queryClientIds('iot-id'); $result = AliIotUtil::transformClientId('iot-id', 'client-id'); $result = AliIotUtil::deleteClientIds('iot-id');
所有方法返回统一格式数组:
['code' => 200/400/500, 'msg' => '...', 'data' => [...]]
AliIotAmqpUtil - 阿里云 IoT AMQP 认证
用于生成阿里云 IoT 平台 AMQP 客户端连接所需的用户名和密码。
use Ssh\CommonUtil\AliIotAmqpUtil; $amqp = AliIotAmqpUtil::getInstance( accessKey: 'your-access-key', accessSecret: 'your-access-secret', consumerGroupId: 'your-consumer-group-id', iotInstanceId: 'your-iot-instance-id' ); $amqp->getIotLoginPasscode(); $userName = $amqp->getUserName(); $passWord = $amqp->getPassWord(); // 将 $userName 和 $passWord 传入 AMQP 客户端连接配置中
AliOssUtil - 阿里云 OSS 对象存储
需要配置环境变量:
ALIYUN.OSS.ACCESS_KEY_ID=your-key-id
ALIYUN.OSS.ACCESS_KEY_SECRET=your-key-secret
ALIYUN.OSS.REGION_ID=cn-shenzhen
use Ssh\CommonUtil\AliOssUtil; // 列出 Bucket 下所有对象 $result = AliOssUtil::listObjects('my-bucket', prefix: 'images/'); // 判断对象是否存在 $result = AliOssUtil::isObjectExist('my-bucket', 'images/a.jpg'); // 上传文件 / 获取上传预签名 URL(前端直传用) $result = AliOssUtil::putObject('my-bucket', 'images/a.jpg', '/local/path/a.jpg'); $result = AliOssUtil::putObjectSignUrl('my-bucket', 'images/b.jpg'); // 下载对象(返回 Base64 编码内容)/ 获取下载预签名 URL $result = AliOssUtil::getObject('my-bucket', 'images/a.jpg'); $result = AliOssUtil::getObjectSignUrl('my-bucket', 'images/a.jpg', expire: 3600); // 删除对象 $result = AliOssUtil::deleteObject('my-bucket', 'images/a.jpg');
AliSmsUtil - 阿里云短信
需要配置环境变量:
ALIYUN.SMS.ACCESS_KEY_ID=your-key-id
ALIYUN.SMS.ACCESS_KEY_SECRET=your-key-secret
use Ssh\CommonUtil\AliSmsUtil; $result = AliSmsUtil::sendSms( phone: '13800138000', signName: '你的签名', templateCode: 'SMS_123456789', templateParam: ['code' => '123456'] ); if ($result['code'] == 200) { echo '发送成功'; }
SmCryptoUtil - 国密 SM2/SM3/SM4 加解密
完整实现了与 Java 端互通的国密加解密流程,支持请求发送和接收两种场景。
依赖: lpilp/guomi、PHP GMP 扩展、OpenSSL 1.1.1+
use Ssh\CommonUtil\SmCryptoUtil; // ===== 发送加密请求 ===== $result = SmCryptoUtil::sendRequestMessage( data: ['orderId' => '123', 'amount' => 100], publicKey: '02a1b2c3d4...', // 对方公钥(压缩格式或非压缩格式均可) url: 'https://api.example.com/endpoint', appId: 'your-app-id', channelCode: 'your-channel', appChannelCode: 'your-app-channel' ); // $result => ['code' => 200, 'msg' => '验签成功', 'data' => [...]] // ===== 接收并解密请求 ===== $decrypted = SmCryptoUtil::receiveRequestMessage( json: $requestBodyJson, // 收到的请求 JSON 字符串 privateKey: 'your-private-key' // 自己的私钥 );
单独使用 SM4 加解密:
// SM4-CBC 加密 / 解密 $encrypted = SmCryptoUtil::sm4EncryptCbc('hello world', $keyHex, $ivHex); $decrypted = SmCryptoUtil::sm4DecryptCbc($encryptedHex, $keyHex, $ivHex);
SM2 公钥处理与工具方法:
$uncompressed = SmCryptoUtil::decompressSm2PublicKey('02a1b2c3...'); // 压缩 → 非压缩公钥 $cleanKey = SmCryptoUtil::stripPrivateKeyPrefix('00abcdef...'); // 去除 Java 私钥前导 "00" $random = SmCryptoUtil::getRandom(16); // 随机字符串 $hex = SmCryptoUtil::string2HexString('Ab0'); // "416230" $sorted = SmCryptoUtil::getSortJson($array); // 递归按键名排序
SymmetricEncoder - AES 加解密
兼容 Java SHA1PRNG 密钥派生 + AES/ECB/PKCS5Padding + 双重 Base64 编码的加解密逻辑(适配 Java 21)。
use Ssh\CommonUtil\SymmetricEncoder; $seed = 'your-secret-seed'; // 加密(返回双重 Base64 字符串) $encrypted = SymmetricEncoder::aesEncrypt($seed, 'hello world'); // 解密 $decrypted = SymmetricEncoder::aesDecrypt($seed, $encrypted); // 调试:查看派生密钥的十六进制值(32 字符 hex) $keyHex = SymmetricEncoder::getDerivedKeyHex($seed);
SunmiCloudPrinter - 商米云打印机
支持 ESC/POS 指令构建打印内容,并通过商米云 API 推送到打印机。构造时可传入 PSR-3 Logger 实现日志记录。
use Ssh\CommonUtil\SunmiCloudPrinter; // 不传 logger 则不记录日志 $printer = new SunmiCloudPrinter(dots_per_line: 384, logger: $psr3Logger); // ===== 构建打印内容 ===== $printer->restoreDefaultSettings(); $printer->setUtf8Mode(1); // 标题居中、放大 $printer->setAlignment(SunmiCloudPrinter::ALIGN_CENTER); $printer->setCharacterSize(2, 2); $printer->appendText("收银小票"); $printer->lineFeed(); // 正文左对齐、正常大小 $printer->restoreDefaultSettings(); $printer->setUtf8Mode(1); $printer->setAlignment(SunmiCloudPrinter::ALIGN_LEFT); $printer->appendText("商品: 拿铁咖啡 x1 ¥28.00"); $printer->lineFeed(); // 分列打印 $printer->setupColumns( [192, SunmiCloudPrinter::ALIGN_LEFT, 0], [192, SunmiCloudPrinter::ALIGN_RIGHT, 0] ); $printer->printInColumns("合计:", "¥28.00"); $printer->lineFeed(2); // 二维码 $printer->setAlignment(SunmiCloudPrinter::ALIGN_CENTER); $printer->appendQRcode(4, 1, "https://example.com/order/123"); $printer->lineFeed(3); $printer->cutPaper(true); // ===== 推送到打印机 ===== $printer->pushContent( sn: '打印机SN号', trade_no: 'ORDER_001', order_type: 1, count: 1 ); // ===== 其他设备管理接口 ===== $printer->onlineStatus('打印机SN号'); // 查询在线状态 $printer->clearPrintJob('打印机SN号'); // 清除打印队列 $printer->printStatus('ORDER_001'); // 查询打印状态 $printer->bindShop('打印机SN号', 'shop1'); // 绑定店铺 $printer->unbindShop('打印机SN号', 'shop1'); // 解绑店铺
许可证
MIT License