Compare commits

..

30 Commits

Author SHA1 Message Date
ch4o5
c060e84476 chore(release): 0.1.9 2024-09-12 11:06:35 +00:00
李东云
bebac5992a feat(email): 增加了单独定制发件人的方法
Signed-off-by: 李东云 <dongyu.li@luxcreo.ai>
2024-09-12 19:06:23 +08:00
ch4o5
7d15d85515 chore(release): 0.1.8 2024-09-12 10:31:25 +00:00
李东云
04719839ad build(composer): 更新依赖
Signed-off-by: 李东云 <dongyu.li@luxcreo.ai>
2024-09-12 18:31:00 +08:00
李东云
1c057722bc feat(email): 增加了密送参数
Signed-off-by: 李东云 <dongyu.li@luxcreo.ai>
2024-09-12 18:28:29 +08:00
ch4o5
52f4867c72 chore(release): 0.1.7 2023-07-05 08:49:21 +00:00
李东云
b51699b0d9 feat(session): 增加了定制的 session 中间件
只有已登录的 Session 才保存

Signed-off-by: 李东云 <dongyu.li@luxcreo.ai>
2023-07-05 16:49:04 +08:00
李东云
a570e1964e build(composer): 更新依赖
限定了 hyperf/crontab 的版本,避开有问题的版本
2023-03-30 18:00:00 +08:00
ch4o5
def9f0469d chore(release): 0.1.6 2023-03-21 03:02:49 +00:00
李东云
5ed5070f32 build(composer): 更新依赖 2023-03-21 11:02:17 +08:00
李东云
8dcf586baf test(email): 更新了邮件发送的测试账号 2023-02-27 11:05:13 +08:00
李东云
a7a8087509 fix(middleware.i18n): 尝试修复了多语言无法正确获取的问题 2023-02-18 15:50:46 +08:00
ch4o5
e50a2e08df chore(release): 0.1.5 2023-02-03 02:28:18 +00:00
李东云
fca8f9d013 fix(commonHandler): 修复了一处兼容问题 2023-02-03 10:28:00 +08:00
ch4o5
113e7fca57 chore(release): 0.1.4 2023-02-02 08:03:53 +00:00
李东云
24161f0841 style(cs-fixer): prettier 2023-02-02 16:03:44 +08:00
ch4o5
fe3181c7d1 chore(release): 0.1.3 2023-02-02 07:33:26 +00:00
李东云
6c27804856 fix(extend): 修复了解析不到为空不为数组的问题 2023-02-02 15:32:11 +08:00
李东云
c3eb95d300 fix(extend): 修复了解析不到为null不为数组的问题 2023-02-01 21:11:46 +08:00
李东云
a7a61bd674 fix(extend): 修复了解析不到为null不为数组的问题 2023-02-01 21:05:51 +08:00
李东云
7fd7d23051 feat(email/sms): 添加了发送邮件和短信的事件 2023-01-29 16:41:30 +08:00
李东云
0b4fb23779 build(composer): 修复 php 版本限制 2023-01-28 18:43:57 +08:00
李东云
eb20bc9fdb build(composer): 修复 php 版本限制 2023-01-28 18:42:49 +08:00
ch4o5
01350cbb6c chore(release): 0.1.2 2023-01-28 10:37:19 +00:00
李东云
1f0ef04c7a build(composer): 修复 ConfigProvider 命名空间 2023-01-28 18:37:11 +08:00
ch4o5
4b2c03f8ec chore(release): 0.1.1 2023-01-28 10:33:27 +00:00
李东云
0fa3c23a3e refactor: 向后兼容到7.4 2023-01-28 18:25:44 +08:00
李东云
c83728cad2 feat: 增加了inArray、hasExtends方法 2023-01-19 17:43:57 +08:00
李东云
3e2d3400b4 docs(README): 提示版本过期,需要更新 2023-01-16 22:33:56 +08:00
李东云
135db1bf6a docs(README): 修复一直报错的文件名 2023-01-16 22:30:01 +08:00
43 changed files with 3586 additions and 1943 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
vendor/
.phpunit.result.cache
.php-cs-fixer.cache
.php-cs-fixer.cache
runtime/

8
.idea/HDK-Core.iml generated
View File

@@ -2,7 +2,6 @@
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="Singularity\HDK\Core\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Singularity\HDK\Test\Core\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
@@ -64,8 +63,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/exception-handler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/macroable" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/resource" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/paginator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/engine" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/pool" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/event" />
@@ -94,7 +91,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php81" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
@@ -135,14 +131,12 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/pestphp/pest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/stopwatch" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpstan" />
<excludeFolder url="file://$MODULE_DIR$/vendor/pestphp/pest-plugin" />
<excludeFolder url="file://$MODULE_DIR$/vendor/friendsofphp/php-cs-fixer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/pest-plugin-test-time" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/deprecations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hyperf/coordinator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition-contracts" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

24
.idea/php-docker-settings.xml generated Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpDockerContainerSettings">
<list>
<map>
<entry key="9fb85f67-19fd-423f-9358-1b155a12eb7d">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
<option name="hostPath" value="$PROJECT_DIR$" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
</map>
</list>
</component>
</project>

22
.idea/php.xml generated
View File

@@ -79,8 +79,6 @@
<path value="$PROJECT_DIR$/vendor/hyperf/http-message" />
<path value="$PROJECT_DIR$/vendor/hyperf/exception-handler" />
<path value="$PROJECT_DIR$/vendor/hyperf/macroable" />
<path value="$PROJECT_DIR$/vendor/hyperf/resource" />
<path value="$PROJECT_DIR$/vendor/hyperf/paginator" />
<path value="$PROJECT_DIR$/vendor/hyperf/engine" />
<path value="$PROJECT_DIR$/vendor/hyperf/pool" />
<path value="$PROJECT_DIR$/vendor/hyperf/event" />
@@ -109,7 +107,6 @@
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
@@ -150,17 +147,28 @@
<path value="$PROJECT_DIR$/vendor/pestphp/pest-plugin" />
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
<path value="$PROJECT_DIR$/vendor/spatie/test-time" />
<path value="$PROJECT_DIR$/vendor/spatie/pest-plugin-test-time" />
<path value="$PROJECT_DIR$/vendor/facade/ignition-contracts" />
<path value="$PROJECT_DIR$/vendor/friendsofphp/php-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/hyperf/coordinator" />
<path value="$PROJECT_DIR$/vendor/psr/clock" />
<path value="$PROJECT_DIR$/vendor/hyperf/crontab" />
<path value="$PROJECT_DIR$/vendor/fidry/cpu-core-counter" />
<path value="$PROJECT_DIR$/vendor/carbonphp/carbon-doctrine-types" />
<path value="$PROJECT_DIR$/vendor/cooper/hyperf-pest" />
<path value="$PROJECT_DIR$/vendor/clue/ndjson-react" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/react/child-process" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.0" />
<component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
<component name="PhpStan">
<PhpStan_settings>
<PhpStanConfiguration tool_path="$PROJECT_DIR$/vendor/bin/phpstan" />

View File

@@ -30,10 +30,6 @@
"type": "perf",
"section": "⚡ Performance Improvements | 性能优化"
},
{
"type": "tests",
"section": "✅ Tests | 测试"
},
{
"type": "test",
"section": "✅ Tests | 测试"
@@ -57,7 +53,7 @@
],
"bumpFiles": [
{
"filename": "VERSION_TRACKER.txt",
"filename": "MY_VERSION_TRACKER.txt",
"type": "plain-text"
},
{

View File

@@ -1,55 +1,94 @@
# 版本更新日志
### [0.2.6](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.5...v0.2.6) (2023-01-16)
### [0.1.9](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.8...v0.1.9) (2024-09-12)
### ✨ Features | 新功能
* **email:** 增加了单独定制发件人的方法 ([bebac59](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/bebac5992a89ebe41eb20ea926938f4b3fde2cc1))
### [0.1.8](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.7...v0.1.8) (2024-09-12)
### ✨ Features | 新功能
* **email:** 增加了密送参数 ([1c05772](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/1c057722bcf407ab1faf6b5feb285e1e472fc627))
### 📦‍ Build System | 打包构建
* **composer:** 更新依赖 ([0471983](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/04719839ad518ef3c00ffb67b8b563561de36858))
### [0.1.7](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.6...v0.1.7) (2023-07-05)
### 📦‍ Build System | 打包构建
* **composer:** 更新依赖 ([a570e19](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a570e1964e01b9e29de05f4a7607a4ed567beffa))
### ✨ Features | 新功能
* **session:** 增加了定制的 session 中间件 ([b51699b](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/b51699b0d98322df33fb3a0bccebe5f4034e2a71))
### [0.1.6](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.5...v0.1.6) (2023-03-21)
### 🐛 Bug Fixes | Bug 修复
* **middleware.i18n:** 尝试修复了多语言无法正确获取的问题 ([a7a8087](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a7a80875092653e56d8dc9351743a08933d59282))
### ✅ Tests | 测试
* **email:** 更新了邮件发送的测试账号 ([8dcf586](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/8dcf586baf1960f906d63c15f3d659b16b456048))
### 📦‍ Build System | 打包构建
* **composer:** 更新依赖 ([5ed5070](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/5ed5070f323d0ea34b8c99b5ac826dc6c24431df))
### [0.1.5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.4...v0.1.5) (2023-02-03)
### 🐛 Bug Fixes | Bug 修复
* **commonHandler:** 修复了一处兼容问题 ([fca8f9d](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/fca8f9d013e1d121bf8bc997710d77ff038fdae0))
### [0.1.4](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.3...v0.1.4) (2023-02-02)
### 💄 Styles | 风格
* 移除冗余的注释 ([3d09207](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/3d092074b3784b120cabb1561a9d6b9af5c3c428))
* **cs-fixer:** prettier ([24161f0](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/24161f084127602a32d9a7ca3af122eb26169fec))
### [0.1.3](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.2...v0.1.3) (2023-02-02)
### 🐛 Bug Fixes | Bug 修复
### 📦‍ Build System | 打包构建
* **configure:** 修复配置文件和实际应用的字段名不统一 ([d1bae3e](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/d1bae3e623dde3839acb44d4a7905c7dab78e8ce))
### [0.2.5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.4...v0.2.5) (2023-01-13)
* **composer:** 修复 php 版本限制 ([0b4fb23](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/0b4fb2377985dc70a2cfa5a603f73e2ced14dc8c))
* **composer:** 修复 php 版本限制 ([eb20bc9](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/eb20bc9fdbc887d0e478a3ceaa4396001fbbbdfd))
### ✨ Features | 新功能
* **CoreMiddleware:** 增加restful场景下的分页响应处理 ([aeddb55](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/aeddb55988e714e53b7458ead8668dee6ebec63e))
### [0.2.4](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.3...v0.2.4) (2023-01-13)
### ⏪ Revert | 回退
* **constant:** 移除冗余的http头authentication的枚举类型 ([cdb1937](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/cdb193778fb888851682bb1671bc55dd8f0e3423))
* **email/sms:** 添加了发送邮件和短信的事件 ([7fd7d23](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/7fd7d2305120f60b70390b49c6256b01fbec6485))
### 🐛 Bug Fixes | Bug 修复
* **oss:** 修复了oss的配置缺失和错误 ([922c780](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/922c780c49edc2cddbce3e66e6b9dfde7be364f8))
* **extend:** 修复了解析不到为null不为数组的问题 ([c3eb95d](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/c3eb95d300f312b14204edd6b97d80b76c273ff3))
* **extend:** 修复了解析不到为null不为数组的问题 ([a7a61bd](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a7a61bd6748ca432e95c396738ebdd7dcfe96463))
* **extend:** 修复了解析不到为空不为数组的问题 ([6c27804](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/6c278048568c6b070192b290f54562f05c1803e6))
### [0.2.3](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.2...v0.2.3) (2023-01-13)
### [0.1.2](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.1.1...v0.1.2) (2023-01-28)
### ✏️ Documentation | 文档
### 📦‍ Build System | 打包构建
* **link:** 增加错误码对应的语雀文档 ([3a1f269](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/3a1f2695fef03840d9afe3578f3e7ed33da45926))
* **composer:** 修复 ConfigProvider 命名空间 ([1f0ef04](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/1f0ef04c7a092e2d7df1cafb94b3a50a04ee66cd))
### ✨ Features | 新功能
* **constant:** 增加http头authentication的枚举类型 ([08c1f66](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/08c1f6609c3035dd7d7f39e8389b66a8ba2e34a5))
### [0.2.2](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/compare/v0.2.1...v0.2.2) (2023-01-13)
### ⚡ Performance Improvements | 性能优化
* **exceptions.handler:** 优化trace显示格式 ([9f9ff31](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/9f9ff31a83ea5662500c688471b78f02ff67eb15))
### 0.2.1 (2023-01-13)
### 0.1.1 (2023-01-28)
### 🎉 Init | 初始化
@@ -58,35 +97,15 @@
* 初始化项目 ([a13779a](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a13779ab3fb617189227b9d2c0462588be8ceee5))
### ✅ Tests | 测试
* **oss:** 增加 oss 票据生成的测试文件 ([b5970d5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/b5970d543435495706502c5b67897ff1cfd5fb85))
* **service:** 添加了base64Wrapper的测试 ([69ce665](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/69ce66561f19e44d35b134c6d032ff69f48201a3))
* **service:** 添加了发送邮件的测试 ([65e3485](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/65e34857e2978a9952ee89df71b1e0c9101ec69c))
* **service:** 添加了解析 extends 的测试 ([da1d6c6](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/da1d6c68d21b2739c8bb1701243e6419eae14975))
### ✨ Features | 新功能
* **utils:** 重写了构造URL的方法增加对应的单元测试 ([9f79d14](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/9f79d1465ba6ed4b4c0d0c30e23ffa34bbeece65))
* 迁移到了 Core 命名空间下 ([a8a582f](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a8a582f3aa0948c982e31bf8938b6f3331d8dc6d))
### ✏️ Documentation | 文档
* **core:** 添加作者信息 ([096d4e5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/096d4e56ed30c23de1338bb6f057a91e629c6ea5))
* **README:** 增加说明文件 ([4cf16ae](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/4cf16ae0cdcf6f7a1eb99fde95a6c3ef01bd7b2b))
### ⚡ Performance Improvements | 性能优化
* **utils:** 获取真实IP方法增加apache的处理 ([47e522c](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/47e522c67bf595282df7671f1d045e44593022bf))
### ♻️ Code Refactoring | 代码重构
### 🚀 Chore | 构建/工程依赖/工具
* **migrate:** 迁移注解以支持hyperf3 ([480f406](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/480f406cccde21f5fb39ba431b6f633c562e5f96))
* **migrate:** 迁移注解以支持hyperf3 ([91db3c7](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/91db3c731f651e178f830953bf97958c5ddc973d))
* 移除调试用的打印语句 ([37ed1ce](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/37ed1cef77d094f6cd78dd1c97f5d2e3cf6376a3))
* **tests:** 移除了示例测试文件 ([e705bed](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/e705bedd5d3e72887e9e5fac35926237db844116))
### 📦‍ Build System | 打包构建
@@ -97,17 +116,23 @@
* **composer:** 更新依赖 ([1e4fbe5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/1e4fbe59276ed2913aa21973f9502505115d2628))
* **composer:** 添加依赖到开发环境 ([9f5699f](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/9f5699f39cd7a89c37e9365322c6082a97a973f4))
* **version:** 添加changelog配置 ([4deffe0](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/4deffe055ebe8586a183103d84dcd5b97969ca7d))
* **version:** 添加changelog配置及脚本 ([3bc4e27](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/3bc4e275a761bf71bb0ca227770b82c3d5ec76bb))
* 迁移到hyperf3 ([9fedbb4](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/9fedbb40f4c9f3221a97986dc7063d049c914728))
### 🚀 Chore | 构建/工程依赖/工具
### ✏️ Documentation | 文档
* **release:** 0.2.1-0 ([ab44630](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/ab44630db8c2d05c21bccdf0899569f911b01473))
* 移除调试用的打印语句 ([37ed1ce](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/37ed1cef77d094f6cd78dd1c97f5d2e3cf6376a3))
* **tests:** 移除了示例测试文件 ([e705bed](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/e705bedd5d3e72887e9e5fac35926237db844116))
* **core:** 添加作者信息 ([096d4e5](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/096d4e56ed30c23de1338bb6f057a91e629c6ea5))
* **README:** 修复一直报错的文件名 ([135db1b](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/135db1bf6aeeedbb0d59405567bb4837f3221e49))
* **README:** 增加说明文件 ([4cf16ae](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/4cf16ae0cdcf6f7a1eb99fde95a6c3ef01bd7b2b))
* **README:** 提示版本过期,需要更新 ([3e2d340](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/3e2d3400b45edf4b503a91c078aaa3ef0fff553e))
### 🐛 Bug Fixes | Bug 修复
### ✨ Features | 新功能
* **composer:** 修复ConfigProvider命名空间 ([3989f59](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/3989f5913ea56cba8751afc225c19b8f6730d2c3))
* 增加了inArray、hasExtends方法 ([c83728c](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/c83728cad28438c3bcef9d946680fe1637a6814f))
* **utils:** 重写了构造URL的方法增加对应的单元测试 ([9f79d14](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/9f79d1465ba6ed4b4c0d0c30e23ffa34bbeece65))
* 迁移到了 Core 命名空间下 ([a8a582f](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/a8a582f3aa0948c982e31bf8938b6f3331d8dc6d))
### ♻️ Code Refactoring | 代码重构
* 向后兼容到7.4 ([0fa3c23](http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore/commit/0fa3c23a3e249cd2473a468ee64d7efc8b6eafe3))

View File

@@ -2,6 +2,15 @@
The Common HDK for Any project by Hyperf.
> DEPRECATED!!!
>
> This Branch is outdated,
> only used for support old versions.
> Such as Hyperf 2.2 or less.
>
> Please UPGRADE your version to Hyperf 3.0
> and HDK-Core 0.2.*
## PHP Versions
This version will work on Hyperf version 2.2 and above.
@@ -22,7 +31,7 @@ After cloning this repository, change into the newly created directory and run
composer config secure-http false \
&& composer config repo.lux-map composer https://satis.luxcreo.cn/ \
&& composer require singularity/hdk-core \
&& php bin/hyperf vendor:publish singularity/hdk-core
&& php bin/hyperf.php vendor:publish singularity/hdk-core
```
This will install all dependencies needed for the project.

View File

@@ -1 +0,0 @@
0.2.6

View File

@@ -15,55 +15,62 @@
}
],
"require": {
"php": "~8.0",
"php": "^7.4|^8.0",
"ext-redis": "^5.3",
"ext-swoole": "*",
"composer/composer": ">=2.0",
"ergebnis/http-method": "^2.2",
"hyperf/config": "3.0.*",
"hyperf/constants": "3.0.*",
"hyperf/context": "3.0.*",
"hyperf/database": "3.0.*",
"hyperf/di": "3.0.*",
"hyperf/framework": "3.0.*",
"hyperf/http-server": "3.0.*",
"hyperf/logger": "3.0.*",
"hyperf/redis": "3.0.*",
"hyperf/resource": "3.0.*",
"hyperf/translation": "3.0.*",
"hyperf/config": "^2.2",
"hyperf/constants": "^2.2",
"hyperf/context": "^2.2",
"hyperf/database": "^2.2",
"hyperf/di": "^2.2",
"hyperf/framework": "^2.2",
"hyperf/http-server": "^2.2",
"hyperf/logger": "^2.2",
"hyperf/redis": "^2.2",
"hyperf/translation": "^2.2",
"jetbrains/phpstorm-attributes": "^1.0",
"lmc/http-constants": "^1.2",
"myclabs/php-enum": "^1.8",
"roave/dont": "^1.5",
"symfony/polyfill-php81": "^1.26",
"myclabs/php-enum": "^1.8.4",
"roave/dont": "^1.1",
"symfony/polyfill-php81": "^1.27",
"teapot/status-code": "^1.1"
},
"require-dev": {
"alibabacloud/dysmsapi-20170525": "^2.0",
"firebase/php-jwt": "^6.1",
"firebase/php-jwt": "^6.3",
"friendsofphp/php-cs-fixer": "^3.13",
"guzzlehttp/guzzle": "^7.5",
"hyperf/session": "3.0.*",
"hyperf/validation": "3.0.*",
"hyperf/crontab": "<=3.0.9 || >3.0.13 <3.1",
"hyperf/session": "^2.2",
"hyperf/validation": "^2.2",
"pestphp/pest": "^1.22",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"roave/security-advisories": "dev-latest",
"spatie/pest-plugin-test-time": "^1.1",
"swoole/ide-helper": "*",
"symfony/mailer": "^6.0"
"symfony/mailer": "^5.4"
},
"suggest": {
"hyperf/resource": "If you need",
"firebase/php-jwt": "JWT 鉴权必需",
"hyperf/session": "Session 鉴权必需",
"symfony/mailer": "用于发送电子邮件",
"guzzlehttp/guzzle": "需要发起 http 请求时必需",
"alibabacloud/dysmsapi-20170525": "阿里云短信服务必需"
"alibabacloud/dysmsapi-20170525": "阿里云短信服务必需",
"laminas/laminas-text": "用于cli展示表格和大字",
"gevman/interactive-cli": "用于提供漂亮的交互式命令行",
"clue/stdio-react": "一个基于 reactphp 的交互式命令行",
"brainmaestro/composer-git-hooks": "用于 php 项目的 git-hooks"
},
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"secure-http": false
"secure-http": false,
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"autoload": {
"psr-4": {
@@ -107,5 +114,5 @@
"url": "https://repo.huaweicloud.com/repository/php/"
}
},
"version": "0.2.6"
"version": "0.1.9"
}

3788
composer.lock generated Executable file → Normal file

File diff suppressed because it is too large Load Diff

76
docs/Email.md Normal file
View File

@@ -0,0 +1,76 @@
# Email | 邮件
提供一套通用 Service 用于定制需求,和一套现成的事件/监听器用于异步发送。
## 服务类
[\Singularity\HDK\Core\Service\EmailService](../src/Service/EmailService.php)
* 发送纯文本邮件的 `\Singularity\HDK\Core\Service\EmailService::sendText()`
* 发送 Html 富文本邮件的 `\Singularity\HDK\Core\Service\EmailService::sendHtml()`
## 事件机制
> 这种方式无法监听邮件发送的结果!
>
> 如果你需要根据邮件发送的结果进行处理,
> 请使用服务类进行自定义
更好的方法可能是在事件发生后触发异步队列,
但如果将异步队列封装在本包中,
相当于强制引用本库的项目使用异步队列机制,
这是我们不愿意看到的。
所以这里的监听器只是集成了同步发送的操作。
如果你想要定义更好的设计,
你可以直接使用提供的 Service 类进行定制。
> 或许这里还有优化空间😄
### 事件
[\Singularity\HDK\Core\Events\EmailWillSent](../src/Events/EmailWillSent.php)
### 监听器
[\Singularity\HDK\Core\Listener\EmailWillSentListener](../src/Listener/EmailWillSentListener.php)
### 使用方法
监听器已经通过 [ConfigProvider](../src/ConfigProvider.php) 机制注册,
所以你只需要在要发送邮件的地方调度这个事件即可。
> 这也就意味着,你无法调用这个事件,但不触发这个监听器。
> 如果你有这样做的必要,请创建一个自己的事件。
> 这也是为了保证功能的完整性和原子性。
具体使用方法,如下代码所示:
```php
<?php
namespace App\Service;
use Hyperf\Di\Annotation\Inject;
use Psr\EventDispatcher\EventDispatcherInterface;
use App\Event\UserRegistered;use Singularity\HDK\Core\Events\EmailWillSent;
class UserService
{
/**
* @var EventDispatcherInterface
*/
#[Inject]
private $eventDispatcher;
public function register()
{
// 我们假设存在 User 这个实体
$user = new User();
$result = $user->save();
// ↓
// 这里 dispatch(object $event) 会逐个运行监听该事件的监听器
$this->eventDispatcher->dispatch(new EmailWillSent(
target: $user->mobile,
subject: '注册成功通知',
type: 'html', // 默认 text 可以不传
content: <<<HTML
<h1>恭喜你,注册成功!</h1>
HTML,
));
// ↑
return $result;
}
}
```

75
docs/Sms.md Normal file
View File

@@ -0,0 +1,75 @@
# SMS | 短信
提供一套通用 Service 用于定制需求,和一套现成的事件/监听器用于异步发送。
## 服务类
[\Singularity\HDK\Core\Service\SmsService](../src/Service/SmsService.php)
* 发送国内短信的 `\Singularity\HDK\Core\Service\SmsService::sendSmsCountryside()`
## 事件机制
> 这种方式无法监听短信发送的结果!
>
> 如果你需要根据短信发送的结果进行处理,
> 请使用服务类进行自定义
更好的方法可能是在事件发生后触发异步队列,
但如果将异步队列封装在本包中,
相当于强制引用本库的项目使用异步队列机制,
这是我们不愿意看到的。
所以这里的监听器只是集成了同步发送的操作。
如果你想要定义更好的设计,
你可以直接使用提供的 Service 类进行定制。
> 或许这里还有优化空间😄
### 事件
[\Singularity\HDK\Core\Events\SmsWillSent](../src/Events/SmsWillSent.php)
### 监听器
[\Singularity\HDK\Core\Listener\SmsWillSentListener](../src/Listener/SmsWillSentListener.php)
### 使用方法
监听器已经通过 [ConfigProvider](../src/ConfigProvider.php) 机制注册,
所以你只需要在要发送短信的地方调度这个事件即可。
> 这也就意味着,你无法调用这个事件,但不触发这个监听器。
> 如果你有这样做的必要,请创建一个自己的事件。
> 这也是为了保证功能的完整性和原子性。
具体使用方法,如下代码所示:
```php
<?php
namespace App\Service;
use Hyperf\Di\Annotation\Inject;
use Psr\EventDispatcher\EventDispatcherInterface;
use App\Event\UserRegistered;use Singularity\HDK\Core\Events\EmailWillSent;
class UserService
{
/**
* @var EventDispatcherInterface
*/
#[Inject]
private $eventDispatcher;
public function register()
{
// 我们假设存在 User 这个实体
$user = new User();
$result = $user->save();
// ↓
// 这里 dispatch(object $event) 会逐个运行监听该事件的监听器
$this->eventDispatcher->dispatch(new EmailWillSent(
target: $user->mobile,
subject: '注册成功通知',
type: 'html', // 默认 text 可以不传
content: <<<HTML
<h1>恭喜你,注册成功!</h1>
HTML,
));
// ↑
return $result;
}
}
```

View File

@@ -6,4 +6,6 @@ parameters:
- tests
ignoreErrors:
- '#Constant BASE_PATH not found#'
- '#Property [a-zA-Z0-9\\_]+::\$[a-zA-Z0-9]+ is never written, only read.#'
- '#Property [a-zA-Z0-9\\_]+::\$[a-zA-Z0-9]+ is never written, only read.#'
- '#Method [a-zA-Z0-9\\_]+::[a-zA-Z0-9_]+\(\) has parameter \$[a-zA-Z0-9_]+ with no value type specified in iterable type array.#'
- '#Method [a-zA-Z0-9\\_]+::[a-zA-Z0-9_]+\(\) has parameter \$[a-zA-Z0-9_]+ with no type specified.#'

View File

@@ -68,7 +68,7 @@ return [
'aliyun' => [
'access_key_id' => env('ACCESS_KEY_ID', ''),
'access_key_secret' => env('ACCESS_KEY_SECRET', ''),
'sign_name' => '', // 短信签名名称
'sign' => '', // 短信签名名称
'template_code' => '', // 短信模板CODE
],
],
@@ -76,8 +76,6 @@ return [
'oss' => [
'access_key_id' => env('ACCESS_KEY_ID', ''),
'access_key_secret' => env('ACCESS_KEY_SECRET', ''),
'oss_host' => env('OSS_HOST', ''),
'oss_callback' => env('OSS_CALLBACK'), // 可选
],
],
],

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env sh
docker login harbor.luxcreo.cn -u php -p fTr6oVvqqdzVlYgnZhRPPAP54u7SmqGA
image_name=harbor.luxcreo.cn/php/composer-template
docker build --tag $image_name .
#docker push $image_name
#docker push ${image_name}-dev

View File

@@ -6,4 +6,4 @@ docker run \
-w "/srv/www" \
-v "$(pwd)":/srv/www \
-v ~/.ssh:/root/.ssh \
harbor.luxcreo.cn/library/hyperf:8.0-swoole /bin/ash
harbor.luxcreo.cn/library/hyperf:7.4-swoole /bin/ash

View File

@@ -14,6 +14,8 @@ namespace Singularity\HDK\Core;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Framework\Logger\StdoutLogger;
use Singularity\HDK\Core\Listener\EmailWillSentListener;
use Singularity\HDK\Core\Listener\SmsWillSentListener;
class ConfigProvider
{
@@ -38,7 +40,10 @@ class ConfigProvider
'commands' => [
],
// 与 commands 类似
'listeners' => [],
'listeners' => [
EmailWillSentListener::class,
SmsWillSentListener::class
],
// 组件默认配置文件,即执行命令后会把 source 的对应的文件复制为 destination 对应的的文件
'publish' => [
[

View File

@@ -1,5 +1,4 @@
<?php
/**
* CommonErrorCode.php@hyperf-development-kit
*
@@ -14,307 +13,382 @@ use Hyperf\Constants\AbstractConstants;
use Hyperf\Constants\Annotation\Constants;
/**
* @Constants
* Singularity\HyperfDevelopmentKit\Utils\Constants\CommonErrorCode@hyperf-development-kit
*
* @author 李东云<dongyun.li@luxcreo.cn>
* Powered by PhpStorm
* Created on 2022/4/25
*
* @see https://lux-software.yuque.com/htnx76/vcm2oc/pkzgpv
*/
#[Constants]
class CommonErrorCode extends AbstractConstants
{
// =============== 9 服务器异常 =================
/**
* @Message("common_error.server_common_error")
*/
public const SERVER_ERROR = 9000000;
/**
* @Message("common_error.program.default")
*/
public const PROGRAM_ERROR = 9000000;
// 901 逻辑错误
/**
* @Message("common_error.program.syntax.default")
*/
public const PROGRAM_SYNTAX_ERROR = 9010001;
// 902 SQL 错误
/**
* @Message("common_error.program.sql.default")
*/
public const PROGRAM_SQL_ERROR = 9020001;
/**
* @Message("common_error.program.sql.update")
*/
public const PROGRAM_SQL_UPDATE_ERROR = 9020101;
// 90201 字段不存在
/**
* @Message("common_error.program.sql.column_not_found")
*/
public const PROGRAM_SQL_COLUMN_NOT_FOUND = 9020101;
/**
* @Message("common_error.program.sql.insert")
*/
public const PROGRAM_SQL_INSERT_ERROR = 9020201;
// =============== 1 请求错误 =================
// 101 请求方式有误
/**
* @Message("common_error.params.method.default")
*/
public const REQUEST_METHOD_ERROR = 1010001;
// 102 参数非法
/**
* 参数非法.
* @Message("common_error.params.format_error")
*/
public const FORMATTER_ERROR = 1020001;
/**
* 缺少参数.
* @Message("common_error.params.error.default")
*/
public const REQUEST_PARAMS_MISS = 1020002;
/**
* 签名非法.
* @Message("common_error.params.error.default")
*/
public const REQUEST_SIGN_ERROR = 1020003;
// 103 参数错误
/**
* @Message("common_error.params.error.default")
*/
public const REQUEST_PARAMS_ERROR = 1030001;
// 10301 用户信息错误
/**
* @Message("common_error.params.error.user.default")
*/
public const REQUEST_PARAMS_ERROR_USER = 1030101;
/**
* @Message("common_error.params.error.user.sign_up.default")
*/
public const REQUEST_PARAMS_ERROR_USER_REGISTERED = 1030102;
/**
* @Message("common_error.params.error.user.username.unique")
*/
public const REQUEST_PARAMS_ERROR_USER_USERNAME_UNIQUE = 1030111;
/**
* @Message("common_error.params.error.user.sec_phone.unique")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_PHONE_UNIQUE = 1030121;
/**
* @Message("common_error.params.error.user.sec_phone.required")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_PHONE_REQUIRED = 1030122;
/**
* @Message("common_error.params.error.user.sec_phone.undefined")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_PHONE_UNDEFINED = 1030123;
/**
* @Message("common_error.params.error.user.sec_email.unique")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_EMAIL_UNIQUE = 1030131;
/**
* @Message("common_error.params.error.user.sec_email.required")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_EMAIL_REQUIRED = 1030132;
/**
* @Message("common_error.params.error.user.sec_email.undefined")
*/
public const REQUEST_PARAMS_ERROR_USER_SEC_EMAIL_UNDEFINED = 1030133;
/**
* @Message("common_error.params.error.user.action.error")
*/
public const REQUEST_PARAMS_ERROR_USER_ACTION_ERROR = 1030141;
// 10302 验证码错误
/**
* @Message("common_error.params.error.code.error")
*/
public const REQUEST_PARAMS_ERROR_CODE_ERROR = 1030201;
/**
* @Message("common_error.params.error.code.timeout")
*/
public const REQUEST_PARAMS_ERROR_CODE_TIMEOUT = 1030202;
/**
* @Message("common_error.params.error.code.limit.minute")
*/
public const REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_MINUTE = 1030301;
/**
* @Message("common_error.params.error.code.limit.hour")
*/
public const REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_HOUR = 1030302;
/**
* @Message("common_error.params.error.code.limit.day")
*/
public const REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_DAY = 1030303;
/**
* @Message("common_error.params.error.target.format")
*/
public const REQUEST_PARAMS_ERROR_TARGET_FORMAT = 1030401;
/**
* @Message("common_error.params.error.target.missing")
*/
public const REQUEST_PARAMS_ERROR_TARGET_MISSING = 1030402;
// =============== 2 鉴权问题 =================
// 201 JWT 鉴权失败
/**
* @Message("common_error.auth.default")
*/
public const UNAUTHORIZED = 200000;
/**
* @Message("common_error.auth.jwt.default")
*/
public const AUTH_JWT_ERROR = 201000;
// 20101 验证
/**
* @Message("common_error.auth.jwt.iat")
*/
public const AUTH_JWT_IAT_ERROR = 2010101;
/**
* @Message("common_error.auth.jwt.nbf")
*/
public const AUTH_JWT_NBF_ERROR = 2010102;
/**
* @Message("common_error.auth.jwt.exp")
*/
public const AUTH_JWT_EXP_TIMEOUT = 2010103;
/**
* @Message("common_error.auth.jwt.iss")
*/
public const AUTH_JWT_ISS_ERROR = 2010104;
/**
* @Message("common_error.auth.jwt.uid")
*/
public const AUTH_JWT_UID_ERROR = 2010105;
// 202 Session 鉴权
/**
* @Message("common_error.auth.session.default")
*/
public const AUTH_SESSION_ERROR = 202000;
// 20201 验证
/**
* @Message("common_error.auth.session.uid")
*/
public const AUTH_SESSION_UID_ERROR = 2020101;
/**
* @Message("common_error.auth.session.created_at")
*/
public const AUTH_SESSION_CREATED_AT_ERROR = 2020102;
// 203 SAML 鉴权
/**
* @Message("common_error.auth.saml.default")
*/
public const AUTH_SAML_ERROR = 203000;
// 20301 验证
/**
* @Message("common_error.auth.saml.params.default")
*/
public const AUTH_SAML_REQUEST_PARAMS_ERROR = 2030100;
/**
* @Message("common_error.auth.saml.params.saml_request")
*/
public const AUTH_SAML_REQUEST_PARAMS_SAML_REQUEST = 2030101;
// 204 无权访问
/**
* @Message("common_error.auth.forbidden.default")
*/
public const FORBIDDEN = 204000;
// 20401 无权访问
/**
* @Message("common_error.auth.forbidden.read.default")
*/
public const FORBIDDEN_READ_ERROR = 2040101;
// 20402 无权修改
/**
* @Message("common_error.auth.forbidden.update.default")
*/
public const FORBIDDEN_UPDATE_ERROR = 2040201;
/**
* @Message("common_error.auth.forbidden.update.address.unique")
*/
public const FORBIDDEN_UPDATE_ADDRESS_DEFAULT_UNIQUE_ERROR = 2040211;
// 20403 无权创建
/**
* @Message("common_error.auth.forbidden.create.default")
*/
public const FORBIDDEN_CREATE_ERROR = 2040301;
/**
* @Message("common_error.auth.forbidden.create.wechat.redundant")
*/
public const FORBIDDEN_CREATE_WECHAT_REDUNDANT_ERROR = 2040311;
// 20404 无权删除
/**
* @Message("common_error.auth.forbidden.delete.default")
*/
public const FORBIDDEN_DELETE_ERROR = 2040401;
/**
* @Message("common_error.auth.forbidden.delete.wechat.only")
*/
public const FORBIDDEN_DELETE_ONLY_USERNAME_WITH_WECHAT = 2040411;
// 205 App 鉴权
/**
* @Message("common_error.auth.app.default")
*/
public const AUTH_APP_ERROR = 205000;
// ============== 3 依赖服务出错 ===============
// 303 缓存异常
// 30301 Redis 异常
/**
* @Message("common_error.server.cache.redis.default")
*/
public const SERVER_CACHE_REDIS_ERROR = 3030101;
/**
* @Message("common_error.server.cache.redis.refused")
*/
public const SERVER_CACHE_REDIS_REFUSED_ERROR = 3030110;
// 30302 SMS 异常
/**
* @Message("common_error.server.dependency_sms_common_error")
*/
public const SERVER_DEPENDENCY_SMS_ERROR = 3030201;
// 30303 微信错误
/**
* @Message("common_error.server.dependency.wechat.default")
*/
public const SERVER_DEPENDENCY_WECHAT_ERROR = 3030301;
/**
* @Message("common_error.server.dependency.wechat.code")
*/
public const SERVER_DEPENDENCY_WECHAT_CODE_ERROR = 3030302;
/**
* @Message("common_error.server.dependency.wechat.timeout")
*/
public const SERVER_DEPENDENCY_WECHAT_CODE_TIMEOUT_ERROR = 3030311;
// 306 消息异常
// 30601 自建邮箱发件服务异常
/**
* @Message("common_error.server.message.email.default")
*/
public const SERVER_MESSAGE_EMAIL_ERROR = 3060101;
/**
* @Message("common_error.server.message.email.not_found")
*/
public const SERVER_MESSAGE_EMAIL_NOT_FOUND = 3060102;
// =============== 4 资源不存在 ================
// 401 路由不存在
/**
* @Message("common_error.not_found.route.default")
*/
public const ROUTE_NOT_FOUND = 4010001;
// 402 模型不存在
/**
* @Message("common_error.model.not_found")
*/
public const MODEL_NOT_FOUND = 4020001;
// 40201 用户不存在
/**
* @Message("common_error.model.user.default")
* @note !!!这个码一定不要改,前端根据这个做判断了!!!!
*/
public const USER_NOT_FOUND = 4020101;
/**
* @Message("common_error.model.user.not_valid")
*/
public const USER_NOT_VALID = 4020102;
/**
* @Message("common_error.model.document.default")
*/

View File

@@ -1,5 +1,4 @@
<?php
/**
* AbstractController.php@hyperf-development-kit
*
@@ -17,7 +16,6 @@ use Dont\DontDeserialise;
use Dont\DontGet;
use Dont\DontSerialise;
use Dont\DontSet;
use Dont\DontToString;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
@@ -41,17 +39,28 @@ abstract class AbstractController
use DontClone;
use DontSerialise;
use DontDeserialise;
use DontToString;
#[Inject]
/**
* @Inject
* @var ContainerInterface
*/
protected ContainerInterface $container;
#[Inject]
/**
* @Inject
* @var RequestInterface
*/
protected RequestInterface $request;
#[Inject]
/**
* @Inject
* @var ResponseInterface
*/
protected ResponseInterface $response;
#[Inject]
/**
* @Inject
* @var \Hyperf\Contract\StdoutLoggerInterface
*/
protected StdoutLoggerInterface $stdoutLogger;
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* EmailWillSent.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
namespace Singularity\HDK\Core\Events;
use Singularity\HDK\Core\Service\EmailService;
/**
* Singularity\HDK\Core\Events\EmailWillSent@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*
* @link ../../docs/Email.md
*/
class EmailWillSent
{
/**
* @var string|string[] $target
*/
public $target;
/**
* @var non-empty-string $subject
*/
public string $subject;
/**
* @var non-empty-string $content
*/
public string $content;
/**
* @var string[] $cc
*/
public array $cc = [];
/**
* @var string[] $bcc
*/
public array $bcc = [];
/**
* @var string 'text'|'html' $type
*/
public string $type = 'text';
/**
* @var EmailService|null
*/
public ?EmailService $sender;
public function __construct(
/**
* @phpstan-param string|string[] $target
*/
$target,
string $subject,
string $content,
/**
* @var non-empty-string[] $cc
*/
array $cc = [],
string $type = 'text',
array $bcc = [],
?EmailService $sender = null
) {
$this->type = $type;
$this->cc = $cc;
$this->content = $content;
$this->subject = $subject;
$this->target = $target;
$this->bcc = $bcc;
$this->sender = $sender;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* SmsWillSent.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
namespace Singularity\HDK\Core\Events;
/**
* Singularity\HDK\Core\Events\SmsWillSent@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*
* @link ../../docs/SMS.md
*/
class SmsWillSent
{
/**
* @var non-empty-string $phone 目标手机号
* @example +8613700000001
* @example 8613700000001
* @example 13700000001
*/
public string $phone;
/**
* @var array<literal-string, literal-string|int>|null $templateParam 模板变量
*/
public ?array $templateParam = null;
/**
* @var literal-string|null $templateCode 模板代码
*/
public ?string $templateCode = null;
/**
* @var literal-string|null $signName 模板签名
*/
public ?string $signName = null;
public function __construct(
/**
* @var non-empty-string $phone 目标手机号
* @example +8613700000001
* @example 8613700000001
* @example 13700000001
*/
string $phone,
/**
* @var array<literal-string, literal-string|int>|null $templateParam 模板变量
*/
?array $templateParam = null,
/**
* @var literal-string|null $templateCode 模板代码
*/
?string $templateCode = null,
/**
* @var literal-string|null $signName 模板签名
*/
?string $signName = null
) {
$this->signName = $signName;
$this->templateCode = $templateCode;
$this->templateParam = $templateParam;
$this->phone = $phone;
}
}

View File

@@ -12,14 +12,17 @@ use Throwable;
*/
class DbException extends HttpException
{
public string $sql;
public function __construct(
int $code = CommonErrorCode::PROGRAM_SQL_ERROR,
?string $message = null,
public string $sql = '',
string $sql = '',
Throwable $previous = null
) {
$this->sql = $sql;
if ($code == CommonErrorCode::FORBIDDEN) {
$previous_code = $previous?->getCode();
$previous_code = empty($previous) ? null : $previous->getCode();
$code = empty($previous_code) ? $code : $previous_code;
}
parent::__construct(

View File

@@ -18,7 +18,7 @@ class Forbidden extends HttpException
Throwable $previous = null
) {
if ($code == CommonErrorCode::FORBIDDEN) {
$previous_code = $previous?->getCode();
$previous_code = empty($previous) ? null : $previous->getCode();
$code = empty($previous_code) ? $code : $previous_code;
}
parent::__construct(

View File

@@ -45,86 +45,154 @@ use Throwable;
class CommonHandler extends ExceptionHandler
{
/**
* @Inject(required=false)
*
* @var RequestInterface|null
*/
#[Inject(required: false)]
private ?RequestInterface $request;
/**
* @Inject
* @var StdoutLogger
*/
#[Inject]
private StdoutLogger $logger;
/**
* {@inheritDoc}
*/
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
{
public function handle(
Throwable $throwable,
ResponseInterface $response
): ResponseInterface {
// 阻止异常冒泡
$this->stopPropagation();
$restful = config('common.response.restful');
$code_name = config('common.response.code_name');
$message_name = config('common.response.message_name');
$is_testing = config('app_status') === true;
$this->request?->url();
$is_debug = $this->request?->hasHeader('Postman-Token') || str_starts_with($this->request?->header('User-Agent'), 'apifox');
$error_type = $throwable::class;
$error_type = get_class($throwable);
$request_time = date('Y-m-d H:i:s');
$request_data = Json::encode($this->request?->getParsedBody());
$request_headers = Json::encode($this->request?->getHeaders());
if (!empty($this->request)) {
$this->request->url();
$is_debug = $this->request->hasHeader('Postman-Token')
|| str_starts_with($this->request->header('User-Agent', ''), 'apifox');
$request_data = Json::encode($this->request->getParsedBody());
$request_headers = Json::encode($this->request->getHeaders());
} else {
$is_debug = false;
$request_data = null;
$request_headers = null;
}
// 901 程序语法错误
// 902 SQL 语法错误
if ($throwable instanceof QueryException) {
$code = match ($throwable->getCode()) {
'42S22' => CommonErrorCode::PROGRAM_SQL_COLUMN_NOT_FOUND,
default => CommonErrorCode::PROGRAM_SQL_ERROR,
};
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::PROGRAM_SQL_ERROR)];
if ($throwable->getCode() === '42S22') {
$code = CommonErrorCode::PROGRAM_SQL_COLUMN_NOT_FOUND;
} else {
$code = CommonErrorCode::PROGRAM_SQL_ERROR;
}
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::PROGRAM_SQL_ERROR
),
];
if ($is_testing) {
$data['details'] = ['sql' => $throwable->getSql(), 'error' => $throwable->getMessage()];
$data['details'] = [
'sql' => $throwable->getSql(),
'error' => $throwable->getMessage(),
];
}
}
// 101 请求方式错误
if ($throwable instanceof MethodNotAllowedHttpException) {
$message = explode(': ', $throwable->getMessage());
$allow_method = explode(', ', $message[1]);
$code = CommonErrorCode::REQUEST_METHOD_ERROR;
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($code, ['methods' => join(', ', $allow_method)]), 'currentMethod' => $this->request?->getMethod(), 'allowedMethod' => $allow_method];
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage($code, [
'methods' => join(', ', $allow_method),
]),
'currentMethod' => empty($this->request) ? null : $this->request->getMethod(),
'allowedMethod' => $allow_method,
];
}
// 验证失败
if ($throwable instanceof BadRequestHttpException) {
$data = [$code_name => CommonErrorCode::REQUEST_PARAMS_ERROR, $message_name => $is_testing ? $throwable->getMessage() : CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR)];
$data = [
$code_name => CommonErrorCode::REQUEST_PARAMS_ERROR,
$message_name => $is_testing
? $throwable->getMessage()
: CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR),
];
}
if ($throwable instanceof ValidationException) {
$data = $throwable->validator->errors()->first();
if (is_numeric($data)) {
$code = (int) $data;
$code = (int)$data;
$data = CommonErrorCode::getMessage($code);
}
$data = [$code_name => $code ?? CommonErrorCode::REQUEST_PARAMS_ERROR, $message_name => $data];
$data = [
$code_name => $code ?? CommonErrorCode::REQUEST_PARAMS_ERROR,
$message_name => $data,
];
}
if ($throwable instanceof ValidateException) {
$code = $throwable->getCode();
$message = $throwable->getMessage();
$data = [$code_name => $code, $message_name => empty($message) ? CommonErrorCode::getMessage($code, ['param' => $throwable->getFieldName()]) : $message];
$data = [
$code_name => $code,
$message_name => empty($message) ? CommonErrorCode::getMessage($code, [
'param' => $throwable->getFieldName(),
]) : $message,
];
if ($is_debug) {
$data['currentValue'] = $throwable->getCurrentValue();
$data['availableValue'] = $throwable->getAvailableValue();
}
}
// 路由不存在
if ($throwable instanceof NotFoundHttpException) {
$code = CommonErrorCode::ROUTE_NOT_FOUND;
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($code)];
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage($code),
];
$status_code = 404;
}
// 模型不存在
if ($throwable instanceof ModelNotFoundException) {
$code = empty($throwable->getCode()) ? CommonErrorCode::MODEL_NOT_FOUND : $throwable->getCode();
$message = empty($throwable->getCode()) ? CommonErrorCode::getMessage($code, ['resource' => '资源']) : $throwable->getMessage();
$data = [$code_name => $code, $message_name => $message];
$message = empty($throwable->getCode()) ? CommonErrorCode::getMessage($code, [
'resource' => '资源',
]) : $throwable->getMessage();
$data = [
$code_name => $code,
$message_name => $message,
];
$status_code = 404;
}
// 300 服务出错
// 303 缓存异常
if ($throwable instanceof RedisException) {
@@ -132,8 +200,16 @@ class CommonHandler extends ExceptionHandler
if ($throwable->getMessage() === 'Connection refused') {
$code = CommonErrorCode::SERVER_CACHE_REDIS_REFUSED_ERROR;
}
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::SERVER_CACHE_REDIS_ERROR)];
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::SERVER_CACHE_REDIS_ERROR
),
];
}
// 306 消息异常
// 30601 邮箱发件异常
if ($throwable instanceof TransportException) {
@@ -141,46 +217,75 @@ class CommonHandler extends ExceptionHandler
if (strpos($throwable->getMessage(), '500 Error: bad syntax')) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_NOT_FOUND;
}
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR)];
$data = [
$code_name => $code,
$message_name => CommonErrorCode::getMessage(
$is_testing
? $code
: CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR
),
];
}
if (empty($data)) {
// 其他情况
$data = [$code_name => $is_testing ? $throwable->getCode() == 0 ? CommonErrorCode::SERVER_ERROR : $throwable->getCode() : CommonErrorCode::SERVER_ERROR, $message_name => $is_testing ? $throwable->getMessage() : __(CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR))];
$data = [
$code_name => $is_testing
? ($throwable->getCode() == 0
? CommonErrorCode::SERVER_ERROR
: $throwable->getCode())
: CommonErrorCode::SERVER_ERROR,
$message_name => $is_testing ? $throwable->getMessage() : __(
CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR)
),
];
// 其他错误
if ($throwable instanceof HttpException) {
$data = [$code_name => $throwable->getCode() ?: $throwable->getStatusCode(), $message_name => $throwable->getMessage()];
$data = [
$code_name => $throwable->getCode() ?: $throwable->getStatusCode(),
$message_name => $throwable->getMessage(),
];
}
}
$response = $response->withHeader(Header::CONTENT_TYPE, 'application/json; charset=utf-8');
$response = $response->withHeader(
Header::CONTENT_TYPE,
'application/json; charset=utf-8'
);
if ($is_debug && $is_testing) {
$data += ['errorType' => $error_type, 'errorTrack' => $throwable->getTrace()];
$data['trace'] = [
'errorType' => $error_type,
'errorTrack' => $throwable->getTrace(),
];
}
$cookies = json_encode($this->request->getCookieParams(), JSON_UNESCAPED_UNICODE);
$cookies = Json::encode($this->request->getCookieParams());
$this->logger->error(
<<<ERROR_LOG
TYPE: {$error_type}
[{$data[$code_name]}] {$data[$message_name]}
TYPE: $error_type
[$data[$code_name]] $data[$message_name]
{$throwable->getMessage()}
-------------------------------
REQUEST_TIME: {$request_time}
REQUEST_TIME: $request_time
-------------------------------
REQUEST_HEADERS:
{$request_headers}
$request_headers
-------------------------------
REQUEST_COOKIES:
{$cookies}
$cookies
-------------------------------
REQUEST_METHOD:
{$this->request?->getMethod()}
{$this->request->getMethod()}
-------------------------------
REQUEST_URL:
{$this->request?->getUri()}
{$this->request->getUri()}
-------------------------------
REQUEST_QUERY:
{$this->request?->getQueryString()}
{$this->request->getQueryString()}
-------------------------------
REQUEST_DATA:
{$request_data}
$request_data
-------------------------------
TRACE:
{$throwable->getTraceAsString()}
@@ -189,11 +294,21 @@ TRACE:
ERROR_LOG
);
$data = Json::encode($data);
if ($restful) {
$response = $response->withStatus($status_code ?? $throwable->status ?? $throwable->statusCode ?? RFC7231::INTERNAL_SERVER_ERROR);
$response = $response->withStatus(
$status_code ??
$throwable->status ??
$throwable->statusCode ??
RFC7231::INTERNAL_SERVER_ERROR
);
}
return $response->withBody(new SwooleStream($data));
return $response
->withBody(
new SwooleStream($data)
);
}
/**
* 判断该异常处理器是否要对该异常进行处理.
*/

View File

@@ -13,13 +13,17 @@ use Throwable;
*/
class Unauthorized extends HttpException
{
/** @var string $authenticationType */
private string $authenticationType;
public function __construct(
int $code = CommonErrorCode::UNAUTHORIZED,
Throwable $previous = null,
private string $authenticationType = RFC7616::DIGEST
string $authenticationType = RFC7616::DIGEST
) {
$this->authenticationType = $authenticationType;
if ($code == CommonErrorCode::UNAUTHORIZED) {
$previous_code = $previous?->getCode();
$previous_code = empty($previous) ? null : $previous->getCode();
$code = empty($previous_code) ? $code : $previous_code;
}

View File

@@ -12,6 +12,15 @@ use Throwable;
*/
class ValidateException extends HttpException
{
/** @var string $field */
private $field;
/** @var mixed $currentValue */
private $currentValue;
/** @var array<int, mixed> $availableValue */
private $availableValue;
/**
* @param int $code
* @param string|null $message
@@ -23,13 +32,16 @@ class ValidateException extends HttpException
public function __construct(
int $code = CommonErrorCode::FORMATTER_ERROR,
?string $message = null,
private string $field = '',
private mixed $currentValue = null,
private array $availableValue = [],
string $field = '',
$currentValue = null,
array $availableValue = [],
Throwable $previous = null
) {
$this->availableValue = $availableValue;
$this->currentValue = $currentValue;
$this->field = $field;
if ($code == CommonErrorCode::FORMATTER_ERROR) {
$previous_code = $previous?->getCode();
$previous_code = empty($previous) ? null : $previous->getCode();
$code = empty($previous_code) ? $code : $previous_code;
}
parent::__construct(
@@ -54,7 +66,7 @@ class ValidateException extends HttpException
/**
* @return mixed
*/
public function getCurrentValue(): mixed
public function getCurrentValue()
{
return $this->currentValue;
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* AbstractListener.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
namespace Singularity\HDK\Core\Listener;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Event\Contract\ListenerInterface;
abstract class AbstractListener implements ListenerInterface
{
/**
* @var ContainerInterface
* @Inject()
*/
protected ContainerInterface $container;
/**
* @var StdoutLoggerInterface
* @Inject()
*/
protected StdoutLoggerInterface $stdoutLogger;
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* EmailWillSentListener.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
namespace Singularity\HDK\Core\Listener;
use Hyperf\Contract\StdoutLoggerInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Singularity\HDK\Core\Constants\CommonErrorCode;
use Singularity\HDK\Core\Events\EmailWillSent;
use Singularity\HDK\Core\Service\EmailService;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
/**
* Singularity\HDK\Core\Listener\EmailWillSentListener@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*
* @link ../../docs/Email.md
*/
class EmailWillSentListener extends AbstractListener
{
/**
* @inheritDoc
*/
public function listen(): array
{
return [
EmailWillSent::class,
];
}
/**
* @param object $event
*
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function process(object $event): void
{
$stdoutLogger = $this->container->get(StdoutLoggerInterface::class);
/** @var $event EmailWillSent */
$emailService = $event->sender ?? EmailService::make();
try {
$event->type === 'html'
? $emailService->sendHtml(
$event->target,
$event->subject,
$event->content,
$event->cc,
$event->bcc,
)
: $emailService->sendText(
$event->target,
$event->subject,
$event->content,
$event->cc,
$event->bcc,
);
$stdoutLogger->info('邮件发送成功!');
$stdoutLogger->info("To: $event->target");
$stdoutLogger->info("Subject: $event->subject");
$stdoutLogger->debug('Content: ');
$stdoutLogger->debug($event->content);
} catch (TransportExceptionInterface $e) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR;
$msg = $e->getMessage();
if (strpos($msg, '500 Error: bad syntax')) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_NOT_FOUND;
$msg = CommonErrorCode::getMessage($code);
}
$stdoutLogger->alert('邮件发送失败!');
$stdoutLogger->error(
<<<ERROR_LOG
[$code] $msg
ERROR_LOG
);
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* SmsWillSentListener.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
namespace Singularity\HDK\Core\Listener;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Singularity\HDK\Core\Events\SmsWillSent;
use Singularity\HDK\Core\Service\SmsService;
use Throwable;
/**
* Singularity\HDK\Core\Listener\SmsWillSentListener@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/16
*/
class SmsWillSentListener extends AbstractListener
{
/**
* @inheritDoc
*/
public function listen(): array
{
return [
SmsWillSent::class,
];
}
/**
* @param SmsWillSent $event
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function process(object $event): void
{
$smsService = $this->container->get(SmsService::class);
try {
$smsService->sendSmsCountryside(
$event->phone,
$event->templateCode,
$event->templateParam,
$event->signName
);
} catch (Throwable $throwable) {
$code = $throwable->getCode();
$message = $throwable->getMessage();
$this->stdoutLogger->alert('短信发送失败!');
$this->stdoutLogger->error(sprintf("[%u] %s", $code, $message));
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* ClassicCoreMiddleware.php@hyperf-development-kit
*
@@ -10,13 +9,13 @@
namespace Singularity\HDK\Core\Middleware;
use Hyperf\Contract\Arrayable;
use Hyperf\Contract\Jsonable;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\CoreMiddleware;
use Hyperf\Utils\Codec\Json;
use Hyperf\Utils\Contracts\Arrayable;
use Hyperf\Utils\Contracts\Jsonable;
use Lmc\HttpConstants\Header;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -31,30 +30,31 @@ use Singularity\HDK\Core\Service\UtilsService;
*/
class ClassicCoreMiddleware extends CoreMiddleware
{
#[Inject]
/**
* @Inject()
* @var \Singularity\HDK\Core\Service\UtilsService
*/
private UtilsService $utilsService;
/**
* Transfer the non-standard response content to a standard response object.
* @template TKey of array-key
* @template TValue
* @param null|array<TKey, TValue>|Arrayable<TKey, TValue>|Jsonable|string $response
*
* @param null|array<string, mixed>|Arrayable<string, mixed>|Jsonable|string $response
*/
protected function transferToResponse($response, ServerRequestInterface $request): ResponseInterface
{
$code_name = config('common.response.code_name');
$message_name = config('common.response.message_name');
$data_name = config('common.response.data_name');
// 分页数据
if ($response instanceof LengthAwarePaginatorInterface) {
$paginator = $response;
$fact_response = $this->response()->withHeader('Per-Page', (string)$paginator->perPage())->withHeader(
'Total',
(string)$paginator->total()
)->withHeader('Current-Page', (string)$paginator->currentPage())->withHeader(
'Total-Pages',
(string)$paginator->hasPages()
);
$fact_response = $this->response()->withHeader('Per-Page', (string)$paginator->perPage())
->withHeader('Total', (string)$paginator->total())
->withHeader('Current-Page', (string)$paginator->currentPage())
->withHeader('Total-Pages', (string)$paginator->hasPages());
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $paginator->nextPageUrl(), 'next');
$fact_response = $this->utilsService->extendLinkToHeader(
$fact_response,
@@ -67,10 +67,11 @@ class ClassicCoreMiddleware extends CoreMiddleware
$paginator->previousPageUrl(),
'prev'
);
return $fact_response->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(
Json::encode(
[
return $fact_response
->withAddedHeader(Header::CONTENT_TYPE, 'application/json')
->withBody(
new SwooleStream(
Json::encode([
$code_name => 200,
$message_name => 'ok',
$data_name => $response->items(),
@@ -80,28 +81,39 @@ class ClassicCoreMiddleware extends CoreMiddleware
'perPage' => $paginator->perPage(),
'total' => $paginator->total(),
],
]
])
)
)
);
}
// 普通数组
if (is_array($response) || $response instanceof Arrayable) {
$response = [$code_name => 200, $message_name => 'ok', $data_name => $response];
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(Json::encode($response))
);
);
}
// 可 Json 化的数据结构
if ($response instanceof Jsonable) {
$response = [$code_name => 200, $message_name => 'ok', $data_name => Json::decode((string)$response)];
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'application/json')->withBody(
new SwooleStream(Json::encode($response))
);
if ($response instanceof Jsonable && $response instanceof Arrayable) {
$response = [
$code_name => 200,
$message_name => 'ok',
$data_name => $response->toArray(),
];
return $this->response()
->withAddedHeader(Header::CONTENT_TYPE, 'application/json')
->withBody(new SwooleStream(Json::encode($response)));
}
// 普通数组
if (is_array($response) || $response instanceof Arrayable) {
$response = [
$code_name => 200,
$message_name => 'ok',
$data_name => $response,
];
return $this->response()
->withAddedHeader(Header::CONTENT_TYPE, 'application/json')
->withBody(new SwooleStream(Json::encode($response)));
}
// 其他默认按字符串处理
return $this->response()->withAddedHeader(Header::CONTENT_TYPE, 'text/plain')->withBody(
new SwooleStream((string)$response)
);
return $this->response()
->withAddedHeader(Header::CONTENT_TYPE, 'text/plain')
->withBody(
new SwooleStream((string)$response)
);
}
}

View File

@@ -15,16 +15,20 @@ use Singularity\HDK\Core\Service\ExtendService;
class ExtendsMiddleware implements MiddlewareInterface
{
/**
* @Inject
* @var \Singularity\HDK\Core\Service\ExtendService
*/
#[Inject]
private ExtendService $service;
/**
* @inheritDoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$this->service->parse($request);
return $handler->handle($request);
}
}

View File

@@ -22,10 +22,11 @@ use Psr\Http\Server\RequestHandlerInterface;
class InternationalizationMiddleware implements MiddlewareInterface
{
/**
* @Inject
* @var TranslatorInterface
*/
#[Inject]
private TranslatorInterface $translator;
/**
* @inheritDoc
*/
@@ -34,15 +35,23 @@ class InternationalizationMiddleware implements MiddlewareInterface
$response = Context::get(ResponseInterface::class);
$language = $request->getHeaderLine('Accept-Language');
if (!empty($language)) {
$language = match (strtolower($language)) {
'en', 'en_us', 'en-us', 'en-uk', 'en_uk' => 'en',
'zh', 'zh_cn', 'zh-cn' => 'zh_CN',
default => config('translation.locale'),
};
switch (true) {
case in_array(strtolower($language), ['en', 'en_us', 'en-us', 'en-uk', 'en_uk']):
$language = 'en';
break;
case in_array(strtolower($language), ['zh', 'zh_cn', 'zh-cn']):
$language = 'zh_CN';
break;
default:
$language = config('translation.locale') ;
break;
}
$this->translator->setLocale($language);
$response = $response->withAddedHeader('Content-Language', strtr($this->translator->getLocale(), '_', '-'));
Context::set(ResponseInterface::class, $response);
}
return $handler->handle($request);
}
}

View File

@@ -1,65 +0,0 @@
<?php
/**
* RestfulCoreMiddleware.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/14
*/
namespace Singularity\HDK\Core\Middleware;
use Hyperf\Contract\Arrayable;
use Hyperf\Contract\Jsonable;
use Hyperf\Contract\LengthAwarePaginatorInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\CoreMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Singularity\HDK\Core\Service\UtilsService;
/**
* Singularity\HDK\Core\Middleware\RestfulCoreMiddleware@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/1/14
*/
class RestfulCoreMiddleware extends CoreMiddleware
{
#[Inject]
private UtilsService $utilsService;
/**
* @template TKey of array-key
* @template TValue
* @param null|array<TKey, TValue>|Arrayable<TKey, TValue>|Jsonable|string $response
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
protected function transferToResponse($response, ServerRequestInterface $request): ResponseInterface
{
// 分页数据
if ($response instanceof LengthAwarePaginatorInterface) {
$fact_response = $this->response()
->withHeader('Per-Page', (string)$response->perPage())
->withHeader('Total', (string)$response->total())
->withHeader('Current-Page', (string)$response->currentPage())
->withHeader('Total-Pages', (string)$response->hasPages());
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $response->nextPageUrl(), 'next');
$fact_response = $this->utilsService->extendLinkToHeader(
$fact_response,
$response->url($response->lastPage()),
'last'
);
$fact_response = $this->utilsService->extendLinkToHeader($fact_response, $response->url(1), 'first');
return $this->utilsService->extendLinkToHeader(
$fact_response,
$response->previousPageUrl(),
'prev'
);
}
return parent::transferToResponse($response, $request);
}
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* SessionMiddleware.php@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/8
*/
namespace Singularity\HDK\Core\Middleware;
use Carbon\Carbon;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\SessionInterface;
use Hyperf\HttpMessage\Cookie\Cookie;
use Hyperf\HttpMessage\Server\Response;
use Hyperf\Session\SessionManager;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
* Singularity\HDK\Auth\Middleware\SessionMiddleware@HDK-Core
*
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
* Powered by PhpStorm
* Created on 2023/3/8
*/
class SessionMiddleware implements MiddlewareInterface
{
private SessionManager $sessionManager;
private ConfigInterface $config;
public function __construct(SessionManager $sessionManager, ConfigInterface $config)
{
$this->config = $config;
$this->sessionManager = $sessionManager;
}
/**
* Process an incoming server request.
* Processes an incoming server request in order to produce a response.
* If unable to produce the response itself, it may delegate to the provided
* request handler to do so.
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->isSessionAvailable()) {
return $handler->handle($request);
}
$session = $this->sessionManager->start($request);
try {
$response = $handler->handle($request);
} finally {
$this->storeCurrentUrl($request, $session);
$session = $this->sessionManager->getSession();
/*
* 现在的机制,但凡写入 Redis 的数据,
* 过期时间就是固定的 gc_maxlifetime
*
* 而 RedisHandler 只会被依赖注入时加载一次,
* 后续无法临时修改 gc_maxlifetime
*
* 又因为现在没有其他情况用到 session
* 只有登录之后才会记下用户信息
*
* 所以一个妥协的方案,就是只有登录了再写入 Redis
*
* (除非重写整套 RedisHandler/RedisHandlerFactory/...
*/
if (!$this->auth($session)) {
$this->sessionManager->end($session);
}
}
return $this->addCookieToResponse($request, $response, $session);
}
private function isSessionAvailable(): bool
{
return $this->config->has('session.handler');
}
/**
* Store the current URL for the request if necessary.
*/
private function storeCurrentUrl(RequestInterface $request, SessionInterface $session)
{
if ($request->getMethod() === 'GET') {
$session->setPreviousUrl($this->fullUrl($request));
}
}
private function auth(SessionInterface $session): bool
{
return $session->has('userInfo');
}
/**
* Add the session cookie to the response·.
*/
private function addCookieToResponse(
ServerRequestInterface $request,
ResponseInterface $response,
SessionInterface $session
): ResponseInterface {
$cookie = new Cookie(
$session->getName(),
$session->getId(),
$this->getCookieExpirationDate($session),
$this->config->get('session.options.path', '/'),
$this->config->get('session.options.domain', $request->getUri()->getHost()),
$this->config->get(
'session.options.secure',
strtolower($request->getUri()->getScheme()) === 'https'
),
true,
$this->config->get('session.options.samesite', Cookie::SAMESITE_LAX)
);
if (!method_exists($response, 'withCookie')) {
return $response->withHeader('Set-Cookie', (string)$cookie);
}
/* @var Response $response */
return $response->withCookie($cookie);
}
/**
* Get the full URL for the request.
*/
private function fullUrl(RequestInterface $request): string
{
$uri = $request->getUri();
$query = $uri->getQuery();
$question = $uri->getHost() . $uri->getPath() == '/' ? '/?' : '?';
return $query ? $this->url($request) . $question . $query : $this->url($request);
}
/**
* Get the session lifetime in seconds.
*/
private function getCookieExpirationDate(SessionInterface $session): int
{
// if ($this->config->get('session.options.expire_on_close')) {
if (!$this->auth($session)) {
$expirationDate = 0;
} else {
$expireSeconds = $this->config->get('session.options.cookie_lifetime', 5 * 60 * 60);
$expirationDate = Carbon::now()->addSeconds($expireSeconds)->getTimestamp();
}
return $expirationDate;
}
/**
* Get the URL (no query string) for the request.
*/
private function url(RequestInterface $request): string
{
return rtrim(preg_replace('/\?.*/', '', (string)$request->getUri()));
}
}

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Singularity\HDK\Core\Service;
use JetBrains\PhpStorm\Deprecated;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;
@@ -43,6 +42,18 @@ class EmailService
);
}
public static function make(
?string $dsn = null,
?string $mailSenderName = null,
?string $mailSender = null
): EmailService {
return new static(
$dsn,
$mailSenderName,
$mailSender,
);
}
/**
* 发送邮件
*
@@ -54,8 +65,9 @@ class EmailService
* @throws TransportExceptionInterface
* @see EmailService::sendText
* @see EmailService::sendHtml
* @deprecated
* @noinspection PhpUnused
*/
#[Deprecated]
public function sendCode(
string $target,
string $subject,
@@ -66,23 +78,26 @@ class EmailService
/**
* @param string|array<string> $target
* @param string $subject
* @param string $text
* @param string $subject
* @param string $text
* @param array<string> $cc
* @param array $bcc
*
* @return bool
* @throws TransportExceptionInterface
*/
public function sendText(
string|array $target,
$target,
string $subject,
string $text,
array $cc = []
array $cc = [],
array $bcc = []
): bool {
$email = (new Email())
->from(Address::create($this->from))
->to(...(is_array($target) ? $target : [$target]))
->cc(...$cc)
->bcc(...$bcc)
->subject($subject)
->text($text);
@@ -95,23 +110,26 @@ class EmailService
* 以 HTML 格式发送邮件
*
* @param string|array<string> $target
* @param string $subject
* @param string $html
* @param string $subject
* @param string $html
* @param array<string> $cc
* @param array $bcc
*
* @return bool
* @throws TransportExceptionInterface
*/
public function sendHtml(
string|array $target,
$target,
string $subject,
string $html,
array $cc = []
array $cc = [],
array $bcc = []
): bool {
$email = (new Email())
->from(Address::create($this->from))
->to(...(is_array($target) ? $target : [$target]))
->cc(...$cc)
->bcc(...$bcc)
->subject($subject)
->html($html);

View File

@@ -10,23 +10,31 @@ use Psr\Http\Message\ServerRequestInterface;
*/
class ExtendService
{
/** @var UtilsService $utils */
private UtilsService $utils;
public function __construct(UtilsService $utils)
{
$this->utils = $utils;
}
/**
* @param ServerRequestInterface|null $request
* @param array<string, string>|null $params
* @param array<string, string>|string|null $params
* @return string[]
*/
public function parse(
?ServerRequestInterface $request,
?array $params = null
$params = null
): array {
$params ??= $request->getQueryParams();
$extends = $params['extends'] ?? null;
$params = $params ?? (isset($request) ? $request->getQueryParams() : null);
$extends = empty($params['extends']) ? [] : $params['extends'];
if (!empty($extends)) {
$extends = explode(',', $extends);
return Context::set(self::class, array_map('trim', $extends));
$extends = array_map('trim', $extends);
}
return [];
Context::set(self::class, $extends);
return $extends;
}
/**
@@ -37,6 +45,12 @@ class ExtendService
return Context::get(self::class) ?? [];
}
public function hasExtends(): bool
{
$extends = Context::get(self::class);
return is_array($extends) && count($extends) > 0;
}
/**
* 判断是否传入了此扩展
*
@@ -44,8 +58,8 @@ class ExtendService
*
* @return bool
*/
public function hasExtends(string $field): bool
public function hasExtend(string $field): bool
{
return Context::has(self::class) && isset(array_flip(Context::get(self::class))[$field]);
return $this->utils->inArray($field, Context::get(self::class));
}
}

View File

@@ -13,6 +13,12 @@ use Hyperf\Utils\Codec\Json;
class OssService
{
public ?string $accessKeyId = null;
public ?string $accessKeySecret = null;
public ?string $host = null;
public ?string $callbackUrl = null;
public ?int $expiration = 30;
/**
* @param string|null $accessKeyId 不传则取配置文件
* @param string|null $accessKeySecret 不传则取配置文件
@@ -20,16 +26,17 @@ class OssService
* @param int|null $expiration 过期时间默认30s
*/
public function __construct(
public ?string $accessKeyId = null,
public ?string $accessKeySecret = null,
public ?string $host = null,
public ?string $callbackUrl = null,
public ?int $expiration = 30
?string $accessKeyId = null,
?string $accessKeySecret = null,
?string $host = null,
?string $callbackUrl = null,
?int $expiration = 30
) {
$this->accessKeyId ??= config('common.third_party.storage.oss.access_key_id');
$this->accessKeySecret ??= config('common.third_party.storage.oss.access_key_secret');
$this->host ??= config('common.third_party.storage.oss.oss_host');
$this->callbackUrl ??= config('common.third_party.storage.oss.oss_callback') ?? null;
$this->expiration = $expiration;
$this->callbackUrl = $callbackUrl?? config('oss_callback');
$this->host = $host?? config('common.third_party.storage.oss.oss_host');
$this->accessKeySecret = $accessKeySecret ?? config('common.third_party.storage.oss.access_key_secret');
$this->accessKeyId = $accessKeyId ?? config('common.third_party.storage.oss.access_key_id');
}
/**
@@ -84,7 +91,7 @@ class OssService
$string_to_sign = $base64_policy;
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->accessKeySecret, true));
$callback = isset($this->callbackUrl) ? Json::encode([
$callback = Json::encode([
'callbackUrl' => $this->callbackUrl,
'callbackBody' => $isImage ? <<<'callbackBody'
{
@@ -115,20 +122,17 @@ callbackBody : <<<'callbackBody'
}
callbackBody,
'callbackBodyType' => 'application/json',
]) : null;
]);
$result = [
return [
'accessid' => $this->accessKeyId,
'host' => $this->host,
'policy' => $base64_policy,
'signature' => $signature,
'expire' => $expire_time,
'dir' => $dir,
'callback' => base64_encode($callback),
];
if (isset($callback)) {
$result += ['callback' => base64_encode($callback),];
}
return $result;
}
/**

View File

@@ -10,6 +10,7 @@ use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\Utils\Codec\Json;
use Singularity\HDK\Core\Constants\CommonErrorCode;
use Singularity\HDK\Core\Exceptions\ValidateException;
use UnexpectedValueException;
/**
* 短信服务
@@ -17,9 +18,9 @@ use Singularity\HDK\Core\Exceptions\ValidateException;
class SmsService
{
/**
* @var \AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi
* @var Dysmsapi
*/
private Dysmsapi $client;
private $client;
public function __construct()
{
@@ -29,7 +30,7 @@ class SmsService
/**
* 初始化实例
*
* @return \AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi
* @return Dysmsapi
*/
private function createClient(): Dysmsapi
{
@@ -44,22 +45,23 @@ class SmsService
/**
* 国内短信
*
* @param string $phone 接收短信的手机号码
* @param string|null $templateCode 短信模板CODE
* @param array<string, string>|null $templateParam 短信模板变量对应的实际值(不支持空数组)
* @param string|null $signName 短信签名名称
* @param string $phone 接收短信的手机号码
* @param string|null $templateCode 短信模板CODE
* @param array<string, string>|null $templateParam 短信模板变量对应的实际值(不支持空数组)
* @param string|null $signName 短信签名名称
*
* @return bool
* @link https://help.aliyun.com/document_detail/419273.htm
* @noinspection PhpUnused
*/
public function sendSmsCountryside(
string $phone,
?string $templateCode = null,
?array $templateParam = null,
?string $signName = null,
?string $signName = null
): bool {
if (is_array($templateParam) && count($templateParam) <= 0) {
throw new \UnexpectedValueException('不支持空数组,请用 null 代替或不传', CommonErrorCode::FORMATTER_ERROR);
throw new UnexpectedValueException('不支持空数组,请用 null 代替或不传', CommonErrorCode::FORMATTER_ERROR);
}
$phone_number = strtr($phone, ['#' => '']);
@@ -89,12 +91,20 @@ class SmsService
// 频率太高
if ($code === 'isv.BUSINESS_LIMIT_CONTROL') {
$error_code = match (mb_substr($message, 2, 2)) {
'天级' => CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_DAY,
'小时' => CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_HOUR,
'分钟' => CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_MINUTE,
default => null
};
switch (mb_substr($message, 2, 2)) {
case '天级':
$error_code = CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_DAY;
break;
case '小时':
$error_code = CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_HOUR;
break;
case '分钟':
$error_code = CommonErrorCode::REQUEST_PARAMS_ERROR_CODE_MAX_TIMES_MINUTE;
break;
default:
$error_code = null;
break;
}
if (isset($error_code)) {
throw new ValidateException($error_code);

View File

@@ -8,8 +8,6 @@ use Closure;
use Exception;
use Generator;
use Hyperf\HttpServer\Contract\RequestInterface;
use JetBrains\PhpStorm\NoReturn;
use JetBrains\PhpStorm\Pure;
use Psr\Http\Message\ResponseInterface;
/**
@@ -101,7 +99,6 @@ class UtilsService
* @param bool $anchorQuery 开启后query 参数将被尽量组合到锚点中,以支持 spa 路由
* @return string
*/
#[Pure]
public function buildUrl(string $url, array $moreQueries = [], bool $anchorQuery = false): string
{
if (count($moreQueries) === 0) {
@@ -236,7 +233,6 @@ class UtilsService
*
* @return void
*/
#[NoReturn]
public function unlimitedSubCategoriesQuicklyWithLevel(
array &$list,
int $level = 1,
@@ -275,9 +271,8 @@ class UtilsService
*
* @return array{'size': float, 'unit': string}
*/
#[Pure]
public function convertStorageSize(
float|int|string $size,
$size,
bool $unitToUpper = false,
int $precision = 2
): array {
@@ -305,9 +300,8 @@ class UtilsService
*
* @return array{'duration': float, 'unit': string}
*/
#[Pure]
public function convertDuration(
int|float $duration,
$duration,
bool $unitToUpper = false,
bool $format = true,
int $precision = 3
@@ -328,4 +322,16 @@ class UtilsService
'unit' => $unitToUpper ? strtoupper($unit[$i]) : $unit[$i],
];
}
/**
* 更快的判断数组元素方法
* @param string|int|float $needle
* @param array<int, mixed> $haystack
* @return bool
*/
public function inArray($needle, array $haystack): bool
{
$list = array_flip($haystack);
return isset($list[$needle]);
}
}

View File

@@ -12,25 +12,27 @@ namespace Singularity\HDK\Test\Core\Service;
use Singularity\HDK\Core\Service\EmailService;
use Symfony\Component\Mailer\Exception\TransportException;
$dsn = 'smtp://account@luxcreo.ai:Qfsd8866@smtp.qiye.aliyun.com:465';
$dsn = 'smtp://account@luxcreo.com:Qfsd8866@smtp-mail.outlook.com:587';
$mail_sender_name = 'LuxCreo';
$mail_sender = 'account@luxcreo.ai';
$mail_sender = 'account@luxcreo.com';
$email = new EmailService($dsn, $mail_sender_name, $mail_sender);
it('assertions that send HTML is available', function () use ($email) {
$result = $email->sendHtml(
'dongyun.li@luxcreo.ai',
'HDK Unit Test HTML',
<<<HTML
$target = 'dongyun.li@luxcreo.ai';
it('assertions that send HTML is available', function () use ($email, $target) {
$result = $email->sendHtml(
$target,
'HDK Unit Test HTML',
<<<HTML
<h1>Hello, World!</h1>
HTML
);
expect($result)->toBeTrue();
);
expect($result)->toBeTrue();
});
it('assertions that send Text is available', function () use ($email) {
it('assertions that send Text is available', function () use ($email, $target) {
$result = $email->sendText(
'dongyun.li@luxcreo.ai',
$target,
'HDK Unit Test Text',
<<<Text
<h1>Hello, World!</h1>
@@ -41,24 +43,26 @@ Text
it('assertions Error Receiver can be catch', function () use ($email) {
try {
$email->sendHtml(
$result = $email->sendHtml(
'unknown@luxcreo.ai',
'HDK Unit Test',
<<<HTML
<h1>Hello, World!</h1>
HTML
);
expect($result)->toBeTrue();
} catch (TransportException $t) {
expect($t->getCode())->toBe(554);
}
try {
$email->sendText(
$result = $email->sendText(
'unknown@luxcreo.ai',
'HDK Unit Test',
<<<Text
<h1>Hello, World!</h1>
Text
);
expect($result)->toBeTrue();
} catch (TransportException $t) {
expect($t->getCode())->toBe(554);
}

View File

@@ -10,23 +10,44 @@
namespace Singularity\HDK\Test\Core\Unit;
use Singularity\HDK\Core\Service\ExtendService;
use Singularity\HDK\Core\Service\UtilsService;
$service = new ExtendService();
/** @var ExtendService $service */
$service = make(ExtendService::class, ['utils' => new UtilsService()]);
it('asserts no parameters can be parsed.', function (ExtendService $service, $params) {
$service->parse(
null,
$params
);
$result = $service->getExtends();
expect($result)->toBeArray()->toHaveCount(0)->toBe([]);
})->with([
[$service, null],
[$service, ''],
[$service, []],
[$service, ['extends' => '']],
[$service, ['extends' => null]],
]);
it('asserts query parameters can be parsed.', function () use ($service) {
$result = $service->parse(
null,
params: [
[
'id' => 5,
'extends' => 'a,b',
]
);
expect($result)->toBeArray()->toHaveCount(2)->toBe(['a', 'b']);
});
it('asserts has extends', function () use ($service) {
expect($service->hasExtends('a'))->toBeTrue()
->and($service->hasExtends('b'))->toBeTrue()
->and($service->hasExtends('c'))->toBeFalse();
expect($service->hasExtends())->toBeTrue();
});
it('asserts has specify extend', function () use ($service) {
expect($service->hasExtend('a'))->toBeTrue()
->and($service->hasExtend('b'))->toBeTrue()
->and($service->hasExtend('c'))->toBeFalse();
})->depends('it asserts query parameters can be parsed.');
it('asserts parsed extends', function () use ($service) {

View File

@@ -10,10 +10,11 @@
use Hyperf\Utils\Codec\Json;
use Singularity\HDK\Core\Service\OssService;
use function Spatie\PestPluginTestTime\testTime;
// use function Spatie\PestPluginTestTime\testTime;
it('asserts oss policy can be generated', function ($setDir, $isImage, $maxSize) {
testTime()->freeze();
$time = time();
// testTime()->freeze();
$oss = new OssService(
'{MOCK_ACCESS_KEY_ID}',
'{MOCK_ACCESS_KEY_SECRET}',
@@ -40,8 +41,8 @@ it('asserts oss policy can be generated', function ($setDir, $isImage, $maxSize)
->and($host)->toBe('{MOCK_HOST}')
->and($dir)->toBe($setDir)
->and($setDir)->toBe($policy['conditions'][1][2])
->and($expire)->toBe(testTime()->addSeconds(30)->unix())
->and($policy['expiration'])->toBe(testTime()->setMilli(0)->toIso8601ZuluString('m'))
->and($expire)->toBe($time + 30)
// ->and($policy['expiration'])->toBe(testTime()->setMilli(0)->toIso8601ZuluString('m'))
->and($maxSize)->toBe($policy['conditions'][0][2])
->and($callback['callbackUrl'])->toBe('{MOCK_CALLBACK_URL}')
->and($signature)->toBe($sign);

View File

@@ -29,7 +29,7 @@ test('断言验证码可以正常生成指定长度', function (int $length) use
})->with($length_data)->group('pure', 'utils');
test('断言可以根据参数构建 URL', function (string $url, array $params, bool $anchorQuery, string $expect) use ($utils) {
$url = $utils->buildUrl(url: $url, moreQueries: $params, anchorQuery: $anchorQuery);
$url = $utils->buildUrl($url, $params, $anchorQuery);
expect($url)->toBe($expect);
})->with([
['baidu.com/list', ['a' => 'b'], false, 'baidu.com/list?a=b'],
@@ -58,5 +58,24 @@ test('断言可以根据参数构建 URL', function (string $url, array $params,
['c' => 'd'],
true,
'http://username:password@127.0.0.1/git/resp?id=1#/page?a=b&c=d',
]
],
])->group('pure', 'utils');
test('断言可以判断是否是数组中的元素', function (
$needle,
array $haystack,
bool $expect,
bool $exceptions = false
) use ($utils) {
try {
expect($utils->inArray($needle, $haystack))->toBe($expect);
} catch (Throwable $e) {
if ($exceptions) {
expect(true)->toBeTrue();
}
}
})->with([
[1, ['1', 2, 3], true, false],
['1', ['1', 2, 3], true, false],
[[1, 2, 3], ['1', 2, 3], false, true],
])->group('pure', 'utils');