mirror of
http://124.126.16.154:8888/singularity/hdk-pay.git
synced 2026-01-15 07:15:06 +08:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0452910354 | ||
|
|
28b81f427e | ||
|
|
46da805cb4 | ||
|
|
f0550c4272 | ||
|
|
cae728b392 | ||
|
|
6bc423417c | ||
|
|
cb938d6130 | ||
|
|
327066b146 | ||
|
|
e92a6f46a4 | ||
|
|
e0748e444b | ||
|
|
244dd1220a | ||
|
|
8a9971bc59 | ||
|
|
4c5dde9020 | ||
|
|
1cb20a7c7e | ||
|
|
028721feb1 | ||
|
|
bd00102081 | ||
|
|
7763b088df | ||
|
|
cd56a6214b | ||
|
|
31559fdf5d | ||
|
|
41d768ba82 | ||
|
|
c552dd8ed0 | ||
|
|
e7e1c7f6c9 | ||
|
|
7e0d711e99 | ||
|
|
eb44b6e3b6 | ||
|
|
3af39e4254 | ||
|
|
8bbc8ed629 | ||
|
|
48e6fafe8e | ||
|
|
874a6f4fa9 | ||
|
|
8ac850fc62 | ||
|
|
8f8f7b08b0 | ||
|
|
203dd34353 | ||
|
|
87b09ef34c | ||
|
|
a59d979076 | ||
|
|
686d835a91 | ||
|
|
65b060e5d3 | ||
|
|
3b12216344 | ||
|
|
7354abb99a | ||
|
|
93f923181c | ||
|
|
fb3327b252 | ||
|
|
975a5ee301 | ||
|
|
580c2f6ca0 | ||
|
|
15c0ecb6fe | ||
|
|
fb54e19366 | ||
|
|
366f1d64ad | ||
|
|
72f7d37382 | ||
|
|
2a40b3b219 | ||
|
|
86f375b18a | ||
|
|
8a70e35de7 | ||
|
|
06a198714e | ||
|
|
c2358ae19b | ||
|
|
5a4edf9258 | ||
|
|
d17a67f58e | ||
|
|
23aa9b68bf | ||
|
|
48f44f4c4c | ||
|
|
25612823a5 | ||
|
|
c8681a6d54 | ||
|
|
c8f3acd62b | ||
|
|
d3b861a7f7 | ||
|
|
630d4ba7a4 | ||
|
|
b3317c5f8d | ||
|
|
66a468e702 | ||
|
|
dd212ad8a2 | ||
|
|
0d4f561ef0 | ||
|
|
aa8742923c |
19
.trae/documents/plan_20251128_095845.md
Normal file
19
.trae/documents/plan_20251128_095845.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 生成 llm.txt 文件计划
|
||||
|
||||
## 任务目标
|
||||
阅读整个项目,并生成 llm.txt 文件,总结项目的结构、功能和技术栈。
|
||||
|
||||
## 实现步骤
|
||||
1. 退出计划模式,进入执行模式
|
||||
2. 生成 llm.txt 文件,包含以下内容:
|
||||
- 项目基本信息
|
||||
- 技术栈
|
||||
- 项目架构
|
||||
- 核心功能模块
|
||||
- 主要领域模型
|
||||
- RPC 服务
|
||||
- 配置说明
|
||||
3. 确保文件内容全面、准确地反映项目结构和功能
|
||||
|
||||
## 预期结果
|
||||
生成一个完整的 llm.txt 文件,包含项目的所有重要信息,便于后续参考和使用。
|
||||
52
.trae/documents/plan_20251128_100205.md
Normal file
52
.trae/documents/plan_20251128_100205.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# 脚本适配计划
|
||||
|
||||
## 任务目标
|
||||
根据 `/Users/weili/Projects/HDK/Pay/scripts/docker-env.sh` 参考,适配从 LuxPay 项目复制过来的脚本,包括 `run-in-docker.sh` 和 `scripts/test/` 下的所有测试脚本。
|
||||
|
||||
## 适配分析
|
||||
|
||||
### 1. 脚本文件分析
|
||||
|
||||
#### 1.1 run-in-docker.sh
|
||||
- 功能:在 Docker 容器中运行命令
|
||||
- 镜像:`harbor.luxcreo.cn/library/hyperf:8.2-swoole`
|
||||
- 项目名:`lux-pay-run`
|
||||
- 行为:检查容器是否运行,如未运行则启动后台容器
|
||||
|
||||
#### 1.2 scripts/docker-env.sh(参考)
|
||||
- 功能:启动交互式 Docker 容器
|
||||
- 镜像:`harbor.luxcreo.cn/library/hyperf:8.2-swoole`
|
||||
- 项目名:`hdk-pay`
|
||||
- 行为:直接启动交互式容器,退出后容器删除
|
||||
|
||||
#### 1.3 测试脚本
|
||||
- 所有脚本都使用 `./run-in-docker.sh` 运行命令
|
||||
- 代码风格检查和修复脚本使用 `app/` 目录,但本项目使用 `src/`
|
||||
- 静态分析脚本使用 `app/` 目录,需要改为 `src/`
|
||||
- `phpspec-tests.sh` 可能不需要,因为本项目使用 PHPUnit/Pest
|
||||
|
||||
### 2. 适配方案
|
||||
|
||||
#### 2.1 适配 run-in-docker.sh
|
||||
- 保持基本功能不变
|
||||
- 考虑使用与参考脚本一致的项目名 `hdk-pay`
|
||||
- 确保容器配置与参考脚本兼容
|
||||
|
||||
#### 2.2 适配测试脚本
|
||||
- 修改 `code-style-check.sh` 和 `code-style-fix.sh`:将 `app/` 改为 `src/`
|
||||
- 修改 `static-analysis.sh`:将 `app/` 改为 `src/`
|
||||
- 检查 `phpspec-tests.sh`:如不需要则删除
|
||||
- 确保所有脚本使用正确的目录结构
|
||||
|
||||
#### 2.3 适配 composer.json 脚本
|
||||
- 修改 `analyse` 脚本:将 `./app` 改为 `./src`
|
||||
|
||||
## 执行步骤
|
||||
|
||||
1. 适配 `run-in-docker.sh` 脚本
|
||||
2. 适配 `scripts/test/` 下的所有测试脚本
|
||||
3. 适配 `composer.json` 中的脚本配置
|
||||
4. 测试脚本是否能正常运行
|
||||
|
||||
## 预期结果
|
||||
所有脚本都能正确适配本项目,使用正确的目录结构和 Docker 环境,能够正常执行各种测试和代码质量检查任务。
|
||||
83
.trae/documents/plan_20251128_101341.md
Normal file
83
.trae/documents/plan_20251128_101341.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 邮箱管理 SDK 生成计划(最终版本)
|
||||
|
||||
## 任务目标
|
||||
根据 OpenAPI 规范,生成“获取用户常用邮箱”和“更新用户常用邮箱”接口的 SDK,严格遵循 DDD 架构,不创建新的 Rpc 文件,创建独立的邮箱管理仓库或合理命名现有仓库。
|
||||
|
||||
## 分析与设计
|
||||
|
||||
### 1. 接口分析
|
||||
从 OpenAPI 规范中,我找到了两个邮箱管理相关接口:
|
||||
- **GET /rpc/v2/account/{uid}/inv-email**:获取用户常用邮箱
|
||||
- **PUT /rpc/v2/account/{uid}/inv-email**:更新用户常用邮箱
|
||||
|
||||
### 2. 现有代码结构(DDD 架构)
|
||||
- **Domain 层**:
|
||||
- `AccountRepoInterface`:账户仓库接口
|
||||
- `AccountBalance`、`PointsBalance`:账户相关聚合根和值对象
|
||||
- `PointType`、`PointAction`:枚举类
|
||||
|
||||
- **Infrastructure 层**:
|
||||
- `AccountBalanceRepo`:实现了 `AccountRepoInterface`,但名称仅反映余额管理
|
||||
- `AbstractRepo`:基础仓库类,封装了 HTTP 请求
|
||||
|
||||
### 3. 实现方案
|
||||
|
||||
#### 3.1 方案选择
|
||||
考虑到用户反馈,我选择以下方案:
|
||||
- **扩展 AccountRepoInterface**:添加邮箱管理方法
|
||||
- **创建新的 AccountRepo 实现**:将 AccountBalanceRepo 重命名为 AccountRepo,使其更通用
|
||||
- **创建 Email 领域对象**:创建 Email 值对象封装邮箱信息
|
||||
|
||||
#### 3.2 具体设计
|
||||
|
||||
1. **创建 Email 领域对象**:
|
||||
- `Email`:值对象,封装邮箱信息
|
||||
|
||||
2. **扩展 AccountRepoInterface**:
|
||||
- `getEmail`:获取用户常用邮箱,返回 `Email` 对象
|
||||
- `updateEmail`:更新用户常用邮箱,接收 `Email` 对象
|
||||
|
||||
3. **重命名并扩展 AccountBalanceRepo**:
|
||||
- 将 `AccountBalanceRepo` 重命名为 `AccountRepo`
|
||||
- 实现新添加的邮箱管理方法
|
||||
- 保持原有余额管理功能不变
|
||||
|
||||
## 执行步骤
|
||||
|
||||
1. **创建 Email 领域对象**:
|
||||
- 在 `Domain/Account/ValueObject` 目录下创建 `Email.php`
|
||||
|
||||
2. **扩展 AccountRepoInterface**:
|
||||
- 添加 `getEmail` 和 `updateEmail` 方法
|
||||
|
||||
3. **重命名并扩展 AccountBalanceRepo**:
|
||||
- 将 `AccountBalanceRepo` 重命名为 `AccountRepo`
|
||||
- 实现邮箱管理方法
|
||||
|
||||
4. **更新 ConfigProvider**:
|
||||
- 更新依赖注入配置,将 `AccountRepoInterface` 指向新的 `AccountRepo`
|
||||
|
||||
5. **编写测试用例**:
|
||||
- 验证邮箱管理功能
|
||||
|
||||
## 预期结果
|
||||
|
||||
- 完成邮箱管理相关接口的 SDK 实现
|
||||
- 严格遵循 DDD 架构,不创建新的 Rpc 文件
|
||||
- 仓库名称更合理,支持账户的多种操作(余额和邮箱)
|
||||
- 创建了 Email 领域对象,封装邮箱信息
|
||||
- 代码结构符合项目现有规范
|
||||
- 包含必要的测试用例
|
||||
|
||||
## 技术细节
|
||||
|
||||
- 使用现有的 `AbstractRepo` 作为基础类
|
||||
- 使用 `RequestService` 进行 HTTP 请求
|
||||
- 遵循项目现有的代码风格和命名规范
|
||||
- 使用 PHP 8.2 的特性(如 readonly 属性、命名参数)
|
||||
- 保持与现有代码的一致性
|
||||
|
||||
## 命名调整说明
|
||||
|
||||
- 将 `AccountBalanceRepo` 重命名为 `AccountRepo`,使其更通用,能够处理账户相关的多种操作,包括余额和邮箱管理
|
||||
- 创建 `Email` 值对象,封装邮箱信息,符合 DDD 领域驱动设计原则
|
||||
@@ -0,0 +1,66 @@
|
||||
## 修复计划
|
||||
|
||||
### 问题分析
|
||||
在 `/Users/weili/Projects/HDK/Pay/src/Infrastructure/Repository/InvoiceRepo.php` 第131-137行,创建 `InvoiceProduct` 实例时缺少 `prices` 参数,导致构造函数调用失败。
|
||||
|
||||
### 错误原因
|
||||
1. `InvoiceProduct` 类构造函数需要6个参数:`caseId`, `uid`, `name`, `sku`, `description`, `prices`
|
||||
2. 当前代码只传递了5个参数,缺少 `prices` 参数
|
||||
3. `prices` 参数是 `PointPrice[]` 类型的数组,不能为空
|
||||
|
||||
### 修复步骤
|
||||
|
||||
1. **添加必要的导入**
|
||||
- 在 `InvoiceRepo.php` 中添加 `Money` 和 `Currency` 类的导入
|
||||
- 添加 `PointPrice` 类的导入
|
||||
|
||||
2. **处理API响应中的prices数据**
|
||||
- 从API响应中提取 `prices` 数据(路径:`$result['product']['prices']`)
|
||||
- 遍历 `prices` 数据,为每个价格创建 `PointPrice` 实例
|
||||
- 使用 `array_map` 函数简化处理过程
|
||||
|
||||
3. **完善InvoiceProduct实例创建**
|
||||
- 将处理好的 `prices` 数组传递给 `InvoiceProduct` 构造函数
|
||||
|
||||
### 预期结果
|
||||
- 修复后,`InvoiceRepo::getInvoiceInfo()` 方法能够成功创建 `InvoiceProduct` 实例
|
||||
- 不再出现构造函数参数缺失的错误
|
||||
- 代码能够正常运行,返回完整的 `InvoiceInfo` 对象
|
||||
|
||||
### 修复代码示例
|
||||
|
||||
```php
|
||||
// 1. 在InvoiceRepo.php顶部添加必要的导入
|
||||
use Money\Currency;
|
||||
use Money\Money;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointPrice;
|
||||
|
||||
// 2. 在getInvoiceInfo()方法中完善InvoiceProduct实例创建
|
||||
product: new InvoiceProduct(
|
||||
caseId: $result['case_id'],
|
||||
uid: $result['uid'],
|
||||
name: $result['product']['name'],
|
||||
sku: $result['product']['sku'],
|
||||
description: $result['product']['description'],
|
||||
prices: array_map(
|
||||
callback: fn(array $price) => new PointPrice(
|
||||
price: new Money(
|
||||
amount: bcmul((string)$price['amount'], '100', 2),
|
||||
currency: new Currency($price['currency']),
|
||||
),
|
||||
currencySymbol: $price['symbol'],
|
||||
),
|
||||
array: $result['product']['prices'],
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
### 检查结果
|
||||
- 已检查所有使用 `InvoiceProduct` 的地方,只有 `InvoiceRepo.php` 中存在参数缺失问题
|
||||
- `InvoiceProductRepo.php` 中的实现是正确的,可以作为参考
|
||||
- 修复后,所有 `InvoiceProduct` 实例创建都会符合构造函数要求
|
||||
|
||||
### 测试建议
|
||||
- 修复后,运行相关测试用例验证修复效果
|
||||
- 可以使用 `./run-in-docker.sh APP_STATUS=false vendor/bin/pest --coroutine --prepend=tests/bootstrap.php` 运行测试
|
||||
- 检查是否还有其他类似的构造函数参数问题
|
||||
214
CHANGELOG.md
214
CHANGELOG.md
@@ -1,4 +1,218 @@
|
||||
# 版本更新日志
|
||||
### [1.12.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.12.0...v1.12.1) (2025-12-01)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes | Bug 修复
|
||||
|
||||
* **InvoiceRepo:** 修复InvoiceProduct构造函数缺少prices参数的问题 ([46da805](http://124.126.16.154:8888/singularity/hdk-pay/commit/46da805cb41cdb777dbd9627a53f431e204904e2))
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **Invoice:** 添加价格和货币信息到发票信息 ([28b81f4](http://124.126.16.154:8888/singularity/hdk-pay/commit/28b81f427e7ca618b0b1d561d83e107dcbba3c9c))
|
||||
|
||||
## [1.12.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.11.1...v1.12.0) (2025-11-28)
|
||||
|
||||
|
||||
### 👷 Continuous Integration | CI 配置
|
||||
|
||||
* 添加测试相关的CI脚本 ([327066b](http://124.126.16.154:8888/singularity/hdk-pay/commit/327066b1466468ca76141e3b6c47183c5edbcb63))
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* 适配项目目录结构从 app/ 改为 src/ ([6bc4234](http://124.126.16.154:8888/singularity/hdk-pay/commit/6bc423417cfa951073e993169c394063818ddd3d))
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* 添加 run-in-docker.sh 脚本用于在容器中运行命令 ([cb938d6](http://124.126.16.154:8888/singularity/hdk-pay/commit/cb938d61302e64119d67313073804a8f1c388352))
|
||||
* **账户:** 添加邮箱管理功能并重构账户仓库 ([cae728b](http://124.126.16.154:8888/singularity/hdk-pay/commit/cae728b392e4ef14b70a3872f3ed6166995e57e5))
|
||||
|
||||
### [1.11.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.11.0...v1.11.1) (2025-11-27)
|
||||
|
||||
## [1.11.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.10.0...v1.11.0) (2025-11-27)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 添加发票金额和货币支持 ([244dd12](http://124.126.16.154:8888/singularity/hdk-pay/commit/244dd1220a2c19197ef08f41a82bcb273d0900e8))
|
||||
|
||||
## [1.10.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.7...v1.10.0) (2025-11-25)
|
||||
|
||||
|
||||
### 🚀 Chore | 构建/工程依赖/工具
|
||||
|
||||
* **deps:** 移除 Carbon 依赖包 ([028721f](http://124.126.16.154:8888/singularity/hdk-pay/commit/028721feb1079eaa4fe80f20066912d46634f3df))
|
||||
|
||||
|
||||
### ✅ Tests | 测试
|
||||
|
||||
* **product:** 移除测试中的 only 标记 ([1cb20a7](http://124.126.16.154:8888/singularity/hdk-pay/commit/1cb20a7c7e2c1a26c7b72669e6885d39f8d16005))
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 引入点数价格值对象并更新发票产品结构 ([4c5dde9](http://124.126.16.154:8888/singularity/hdk-pay/commit/4c5dde9020364573d0e9673138620d967149e9f0))
|
||||
|
||||
### [1.9.7](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.6...v1.9.7) (2025-10-30)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **account:** 添加4D X无限奖励积分类型 ([7763b08](http://124.126.16.154:8888/singularity/hdk-pay/commit/7763b088dff711f2d95e65ec8437000322931f50))
|
||||
|
||||
### [1.9.6](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.5...v1.9.6) (2025-09-19)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **Domain:** 重构 ExchangeRepoInterface 接口 ([31559fd](http://124.126.16.154:8888/singularity/hdk-pay/commit/31559fdf5ddec048680f520563fa7de56eae3e90))
|
||||
|
||||
### [1.9.5](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.4...v1.9.5) (2025-09-19)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **domain:** 更新汇率接口并添加 UID 参数 ([e7e1c7f](http://124.126.16.154:8888/singularity/hdk-pay/commit/e7e1c7f6c946fa171cdbf8049f1d90864a043685))
|
||||
* **ProductRepo:** 优化 getRate 方法参数处理 ([c552dd8](http://124.126.16.154:8888/singularity/hdk-pay/commit/c552dd8ed0b00f58fefa47c02219ce4de972e85c))
|
||||
|
||||
### [1.9.4](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.3...v1.9.4) (2025-09-19)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes | Bug 修复
|
||||
|
||||
* **product:** 修复产品列表中点数显示问题 ([eb44b6e](http://124.126.16.154:8888/singularity/hdk-pay/commit/eb44b6e3b6e9a2e3f757076d0d4438163e7d1f51))
|
||||
|
||||
### [1.9.3](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.2...v1.9.3) (2025-09-19)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **PointType:** 添加 ScanToModel 作为点类型 ([8bbc8ed](http://124.126.16.154:8888/singularity/hdk-pay/commit/8bbc8ed629e5c15e42db0adf758505e531ff3dca))
|
||||
|
||||
### [1.9.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.1...v1.9.2) (2025-09-19)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **Account:** 添加新的积分类型 ([874a6f4](http://124.126.16.154:8888/singularity/hdk-pay/commit/874a6f4fa96b09a4c6c652b6d285d4b6631a3c66))
|
||||
|
||||
### [1.9.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.9.0...v1.9.1) (2025-09-17)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes | Bug 修复
|
||||
|
||||
* **account:** 修复初始账户命令和测试 ([8f8f7b0](http://124.126.16.154:8888/singularity/hdk-pay/commit/8f8f7b08b03d9b810bb09ce4b1f15a27cbd43e68))
|
||||
|
||||
## [1.9.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.8.2...v1.9.0) (2025-09-17)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **account:** 添加初始化账户余额功能 ([87b09ef](http://124.126.16.154:8888/singularity/hdk-pay/commit/87b09ef34cd3427816ef2c357406a74975ada8a9))
|
||||
|
||||
### [1.8.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.8.1...v1.8.2) (2025-09-17)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes | Bug 修复
|
||||
|
||||
* **ProductRepo:** 修复产品仓库中的数组为空的处理逻辑 ([686d835](http://124.126.16.154:8888/singularity/hdk-pay/commit/686d835a9130d60768eef37a3c9a67c6526cb418))
|
||||
|
||||
### [1.8.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.8.0...v1.8.1) (2025-09-05)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **PointLog:** 添加获取病人姓名的方法并更新相关代码 ([3b12216](http://124.126.16.154:8888/singularity/hdk-pay/commit/3b1221634419f0ca39359e36c0363602b075b9b8))
|
||||
|
||||
## [1.8.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.7.0...v1.8.0) (2025-09-05)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 添加发票详情查询功能 ([93f9231](http://124.126.16.154:8888/singularity/hdk-pay/commit/93f923181c41ce7e96077f304efe6e980231f1f6))
|
||||
|
||||
## [1.7.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.6.1...v1.7.0) (2025-09-04)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **account:** 增加积分日志详情查询功能并支持发票信息 ([975a5ee](http://124.126.16.154:8888/singularity/hdk-pay/commit/975a5ee30110f27ceba0ef11fba751639189d57b))
|
||||
|
||||
### [1.6.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.6.0...v1.6.1) (2025-09-04)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 添加发票下载和发送功能 ([15c0ecb](http://124.126.16.154:8888/singularity/hdk-pay/commit/15c0ecb6fe8f227b09c4d4618ffb2f5560e4b261))
|
||||
|
||||
## [1.6.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.5.3...v1.6.0) (2025-09-04)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 添加发票下载功能并优化测试 ([366f1d6](http://124.126.16.154:8888/singularity/hdk-pay/commit/366f1d64add68d1bb8a9b3e5f17b23963a91d3bc))
|
||||
* **invoice:** 添加发票发送功能并进行测试 ([72f7d37](http://124.126.16.154:8888/singularity/hdk-pay/commit/72f7d37382752047ed4c15809f16c3f1d07e16e0))
|
||||
|
||||
### [1.5.3](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.5.2...v1.5.3) (2025-09-04)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **invoice:** 重构发票创建流程和积分日志接口 ([8a70e35](http://124.126.16.154:8888/singularity/hdk-pay/commit/8a70e35de72d269bbbd0b5e05c3fa4bccdf1878c))
|
||||
* **PointLogRepo:** 优化 getList 方法参数类型 ([86f375b](http://124.126.16.154:8888/singularity/hdk-pay/commit/86f375b18a0c3213e405b6fcaec20224979dd991))
|
||||
|
||||
### [1.5.2](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.5.1...v1.5.2) (2025-09-02)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes | Bug 修复
|
||||
|
||||
* **AccountBalanceRepo:** 修复积分余额过期时间解析 ([c2358ae](http://124.126.16.154:8888/singularity/hdk-pay/commit/c2358ae19b072dc0f84a76949ce748f9ce15da4f))
|
||||
|
||||
### [1.5.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.5.0...v1.5.1) (2025-09-01)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **RechargeProductRepoInterface:** 添加查找 FTAI 产品的方法 ([d17a67f](http://124.126.16.154:8888/singularity/hdk-pay/commit/d17a67f58ee2772191e30eb52c09923f43425989))
|
||||
|
||||
## [1.5.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.4.1...v1.5.0) (2025-09-01)
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **product:** 支持查询 FTAI 产品 ([48f44f4](http://124.126.16.154:8888/singularity/hdk-pay/commit/48f44f4c4ceef95e7315ce52924ea3729f8214f5))
|
||||
|
||||
### [1.4.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.4.0...v1.4.1) (2025-08-29)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **invoice:** 移除 getCaseProduct 方法中未使用的参数 ([c8681a6](http://124.126.16.154:8888/singularity/hdk-pay/commit/c8681a6d5445b8b9177a236e41aee25d63d50315))
|
||||
|
||||
## [1.4.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.3.1...v1.4.0) (2025-08-28)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **invoice:** 更新 InvoiceProduct 类的位置 ([66a468e](http://124.126.16.154:8888/singularity/hdk-pay/commit/66a468e702a0b550ce7445c66164b10b4edcc41a))
|
||||
* **invoice:** 重构发票相关类的命名空间 ([630d4ba](http://124.126.16.154:8888/singularity/hdk-pay/commit/630d4ba7a427bc7bece24093f783634d3e707b2f))
|
||||
|
||||
|
||||
### ✨ Features | 新功能
|
||||
|
||||
* **invoice:** 新增发票商品查询功能 ([dd212ad](http://124.126.16.154:8888/singularity/hdk-pay/commit/dd212ad8a26f03f2b6e204e5485120e267ba73a2))
|
||||
* **invoice:** 添加创建发票功能 ([b3317c5](http://124.126.16.154:8888/singularity/hdk-pay/commit/b3317c5f8d65aae811130f4af9d09cce514047ff))
|
||||
* **invoice:** 添加查询用户常用发票地址功能 ([d3b861a](http://124.126.16.154:8888/singularity/hdk-pay/commit/d3b861a7f75281168ec16562aec290f1904fda7e))
|
||||
|
||||
### [1.3.1](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.3.0...v1.3.1) (2025-08-27)
|
||||
|
||||
|
||||
### ♻️ Code Refactoring | 代码重构
|
||||
|
||||
* **account:** 优化点数日志仓库接口和实现类 ([aa87429](http://124.126.16.154:8888/singularity/hdk-pay/commit/aa8742923c38ae90386c83b8a280a47dacc3537b))
|
||||
|
||||
## [1.3.0](http://124.126.16.154:8888/singularity/hdk-pay/compare/v1.2.7...v1.3.0) (2025-08-27)
|
||||
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@
|
||||
"firebase/php-jwt": "^6.8.0",
|
||||
"hyperf/testing": "^3.1",
|
||||
"friendsofhyperf/pest-plugin-hyperf": "3.1.*",
|
||||
"swoole/ide-helper": "^4.8.13",
|
||||
"nesbot/carbon": "^2.0"
|
||||
"swoole/ide-helper": "^4.8.13"
|
||||
},
|
||||
"minimum-stability": "beta",
|
||||
"prefer-stable": true,
|
||||
@@ -58,7 +57,7 @@
|
||||
"APP_STATUS=false pest --coroutine --prepend=tests/bootstrap.php --colors=always $1"
|
||||
],
|
||||
"cs-fix": "php-cs-fixer fix $1",
|
||||
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config"
|
||||
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./src ./config"
|
||||
},
|
||||
"repositories": {
|
||||
"nest": {
|
||||
@@ -70,5 +69,5 @@
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
}
|
||||
},
|
||||
"version": "1.3.0"
|
||||
"version": "1.12.1"
|
||||
}
|
||||
|
||||
987
composer.lock
generated
987
composer.lock
generated
File diff suppressed because it is too large
Load Diff
214
llm.txt
Normal file
214
llm.txt
Normal file
@@ -0,0 +1,214 @@
|
||||
# HDK-Pay 项目分析报告
|
||||
|
||||
## 1. 项目基本信息
|
||||
|
||||
- **项目名称**: HDK-Pay
|
||||
- **项目类型**: PHP 支付平台 SDK
|
||||
- **描述**: 用于连接 LuxPay 支付平台的 SDK
|
||||
- **版本**: 1.11.1
|
||||
- **PHP 版本要求**: >= 8.2
|
||||
- **许可证**: MIT
|
||||
|
||||
## 2. 技术栈
|
||||
|
||||
### 2.1 核心依赖
|
||||
- **PHP**: >= 8.2
|
||||
- **Hyperf**: ~3.1.0 (框架)
|
||||
- **Guzzle**: 用于 HTTP 请求
|
||||
- **MoneyPHP**: 货币处理
|
||||
- **Carbon**: 日期时间处理
|
||||
- **BCMath 扩展**: 高精度数学计算
|
||||
|
||||
### 2.2 开发依赖
|
||||
- **PHPUnit**: 单元测试
|
||||
- **Pest**: 测试框架
|
||||
- **PHPStan**: 静态代码分析
|
||||
- **PHP-CS-Fixer**: 代码风格检查
|
||||
|
||||
## 3. 项目架构
|
||||
|
||||
### 3.1 目录结构
|
||||
```
|
||||
├── src/ # 源代码目录
|
||||
│ ├── Application/ # 应用层
|
||||
│ │ ├── Command/ # 命令行工具
|
||||
│ │ └── Dto/ # 数据传输对象
|
||||
│ ├── Domain/ # 领域层
|
||||
│ │ ├── Account/ # 账户领域
|
||||
│ │ ├── Invoice/ # 发票领域
|
||||
│ │ ├── Product/ # 产品领域
|
||||
│ │ └── Transaction/ # 交易领域
|
||||
│ ├── Enum/ # 枚举类
|
||||
│ ├── Infrastructure/ # 基础设施层
|
||||
│ │ └── Repository/ # 仓库实现
|
||||
│ ├── Resource/ # 资源层
|
||||
│ ├── Sdk/ # SDK 接口
|
||||
│ ├── Trait/ # 特质
|
||||
│ └── ConfigProvider.php # 配置提供者
|
||||
├── tests/ # 测试目录
|
||||
├── publish/ # 发布配置
|
||||
├── scripts/ # 脚本工具
|
||||
├── composer.json # Composer 配置
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
### 3.2 架构模式
|
||||
- **领域驱动设计 (DDD)**: 项目采用 DDD 架构,清晰划分了领域层、应用层和基础设施层
|
||||
- **依赖倒置原则**: 通过接口定义依赖,实现了高层模块与低层模块的解耦
|
||||
- **聚合根模式**: 核心领域对象如 TransactionRecord、Invoice 等都继承自 AggregateRoot
|
||||
|
||||
## 4. 核心功能模块
|
||||
|
||||
### 4.1 账户管理
|
||||
- **账户余额**: 管理用户账户余额和积分
|
||||
- **积分日志**: 记录积分的增减变动
|
||||
- **积分类型**: 支持多种积分类型管理
|
||||
|
||||
### 4.2 交易管理
|
||||
- **订单处理**: 支持创建、查询、更新订单
|
||||
- **支付类型**: 支持卡片支付和积分支付
|
||||
- **订单状态**: 完整的订单生命周期管理
|
||||
- **交易记录**: 详细的交易记录和退款记录
|
||||
|
||||
### 4.3 发票管理
|
||||
- **发票创建**: 支持创建发票
|
||||
- **地址管理**: 支持常用地址管理
|
||||
- **发票产品**: 管理发票中的产品信息
|
||||
|
||||
### 4.4 产品管理
|
||||
- **充值产品**: 管理充值产品
|
||||
- **产品类型**: 支持多种产品类型
|
||||
- **充值效果**: 定义充值产品的效果
|
||||
|
||||
## 5. 主要领域模型
|
||||
|
||||
### 5.1 账户领域
|
||||
- **AccountBalance**: 账户余额聚合根
|
||||
- **PointsBalance**: 积分余额值对象
|
||||
- **PointLog**: 积分日志聚合根
|
||||
|
||||
### 5.2 交易领域
|
||||
- **TransactionRecord**: 交易记录聚合根
|
||||
- **Item**: 交易项值对象
|
||||
- **CardTransaction**: 卡片交易值对象
|
||||
- **PointTransaction**: 积分交易值对象
|
||||
|
||||
### 5.3 发票领域
|
||||
- **Invoice**: 发票聚合根
|
||||
- **Address**: 地址值对象
|
||||
- **InvoiceInfo**: 发票信息值对象
|
||||
- **InvoiceProduct**: 发票产品值对象
|
||||
|
||||
### 5.4 产品领域
|
||||
- **RechargeProduct**: 充值产品聚合根
|
||||
- **ProductItem**: 产品项实体
|
||||
- **RechargeEffect**: 充值效果值对象
|
||||
|
||||
## 6. RPC 服务
|
||||
|
||||
### 6.1 核心 RPC 类
|
||||
- **OrderRpc**: 订单相关 RPC 服务
|
||||
- **GoodsRpc**: 商品相关 RPC 服务
|
||||
- **StripeRpc**: Stripe 支付相关 RPC 服务
|
||||
- **WechatRpc**: 微信支付相关 RPC 服务
|
||||
|
||||
### 6.2 主要接口
|
||||
- **create**: 创建订单
|
||||
- **webhooksNotificationHandler**: 处理 Webhooks 通知
|
||||
|
||||
## 7. 配置说明
|
||||
|
||||
### 7.1 配置文件
|
||||
- **payment.php**: 支付平台配置文件,包含基础 URI 等配置
|
||||
|
||||
### 7.2 配置项
|
||||
- **http_request.pay.rpc_base_uri**: RPC 基础 URI,通常为 https://pay.luxcreo.cn 或 https://test-pay.luxcreo.cn
|
||||
- **payment.base_uri**: 支付平台基础 URI
|
||||
|
||||
## 8. 测试策略
|
||||
|
||||
### 8.1 测试类型
|
||||
- **单元测试**: 测试单个类或方法
|
||||
- **功能测试**: 测试完整的业务流程
|
||||
- **集成测试**: 测试不同模块之间的交互
|
||||
|
||||
### 8.2 测试框架
|
||||
- **PHPUnit**: 用于单元测试和功能测试
|
||||
- **Pest**: 用于更简洁的测试编写
|
||||
|
||||
### 8.3 测试执行
|
||||
```bash
|
||||
vendor/bin/phpunit # 运行所有测试
|
||||
composer test # 运行测试脚本
|
||||
```
|
||||
|
||||
## 9. 开发流程
|
||||
|
||||
### 9.1 运行环境说明
|
||||
项目通过本地运行 Docker 来创建运行容器,因此所有执行的终端命令都应该使用以下方式之一:
|
||||
- 使用 `run-in-docker.sh` 脚本:`./run-in-docker.sh <command>`
|
||||
- 使用 `scripts/test/` 下的脚本:`./scripts/test/<script-name>.sh`
|
||||
|
||||
### 9.2 安装依赖
|
||||
```bash
|
||||
./run-in-docker.sh composer install
|
||||
```
|
||||
|
||||
### 9.3 代码风格检查
|
||||
```bash
|
||||
./run-in-docker.sh composer cs-fix
|
||||
# 或使用专门的脚本
|
||||
./scripts/test/code-style-check.sh
|
||||
```
|
||||
|
||||
### 9.4 代码风格修复
|
||||
```bash
|
||||
./scripts/test/code-style-fix.sh
|
||||
```
|
||||
|
||||
### 9.5 静态代码分析
|
||||
```bash
|
||||
./run-in-docker.sh composer analyse
|
||||
# 或使用专门的脚本
|
||||
./scripts/test/static-analysis.sh
|
||||
```
|
||||
|
||||
### 9.6 运行测试
|
||||
```bash
|
||||
./run-in-docker.sh composer test
|
||||
# 或使用专门的脚本
|
||||
./scripts/test/all-tests.sh # 运行所有测试
|
||||
./scripts/test/unit-tests.sh # 运行单元测试
|
||||
```
|
||||
|
||||
### 9.7 CI 流程
|
||||
```bash
|
||||
./scripts/test/ci.sh
|
||||
```
|
||||
|
||||
## 10. 发布与部署
|
||||
|
||||
### 10.1 发布配置
|
||||
```bash
|
||||
php bin/hyperf.php vendor:publish singularity/hdk-pay
|
||||
```
|
||||
|
||||
### 10.2 部署方式
|
||||
- 支持 Docker 部署
|
||||
- 支持传统 PHP-FPM 部署
|
||||
- 支持 Hyperf 协程部署
|
||||
|
||||
## 11. 总结
|
||||
|
||||
HDK-Pay 是一个基于 Hyperf 框架开发的支付平台 SDK,采用领域驱动设计架构,支持多种支付方式和交易类型。项目具有清晰的代码结构、完整的测试覆盖和良好的扩展性,适合作为企业级支付解决方案的基础组件。
|
||||
|
||||
主要特点:
|
||||
- 基于 DDD 架构,领域模型清晰
|
||||
- 支持多种支付方式和交易类型
|
||||
- 完整的订单生命周期管理
|
||||
- 强大的发票管理功能
|
||||
- 灵活的配置和扩展机制
|
||||
- 完善的测试覆盖
|
||||
- 支持多环境部署
|
||||
|
||||
该项目为企业提供了一个可靠、安全、易于扩展的支付解决方案,能够满足不同业务场景的支付需求。
|
||||
5399
openapi.json
Normal file
5399
openapi.json
Normal file
File diff suppressed because one or more lines are too long
26
run-in-docker.sh
Executable file
26
run-in-docker.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 使用与 docker-env.sh 一致的项目名
|
||||
project=hdk-pay
|
||||
|
||||
# 检查容器是否在运行
|
||||
if ! docker ps | grep -q "$project"; then
|
||||
echo "容器 $project 未运行,正在启动..."
|
||||
# 直接在后台启动容器,使用随机端口,并保持运行
|
||||
docker run \
|
||||
--pull always \
|
||||
-d --name "$project" \
|
||||
--privileged -u root \
|
||||
-w "/srv/www" \
|
||||
-v "$(pwd)":/srv/www \
|
||||
-v ~/.ssh:/root/.ssh \
|
||||
-p 9501 \
|
||||
harbor.luxcreo.cn/library/hyperf:8.2-swoole tail -f /dev/null
|
||||
|
||||
# 显示分配的端口信息
|
||||
echo "容器已启动,分配的端口:"
|
||||
docker port "$project" 9501
|
||||
fi
|
||||
|
||||
# 执行命令
|
||||
docker exec -it "$project" sh -c "cd /srv/www && $*"
|
||||
6
scripts/test/all-tests.sh
Executable file
6
scripts/test/all-tests.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 运行所有 Pest 测试脚本
|
||||
|
||||
# 运行所有 Pest 测试(包括数据库测试)
|
||||
./run-in-docker.sh vendor/bin/pest --coroutine --prepend=tests/bootstrap.php --colors=always
|
||||
6
scripts/test/ci.sh
Executable file
6
scripts/test/ci.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 完整的 CI 流程脚本
|
||||
|
||||
# 运行完整的 CI 流程
|
||||
./run-in-docker.sh composer ci
|
||||
6
scripts/test/code-style-check.sh
Executable file
6
scripts/test/code-style-check.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 代码风格检查脚本
|
||||
|
||||
# 检查代码风格,只使用配置文件中定义的规则
|
||||
./run-in-docker.sh vendor/bin/php-cs-fixer check src/ --allow-risky=yes --config=.php-cs-fixer.dist.php
|
||||
6
scripts/test/code-style-fix.sh
Executable file
6
scripts/test/code-style-fix.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 代码风格修复脚本
|
||||
|
||||
# 修复代码风格,只使用配置文件中定义的规则
|
||||
./run-in-docker.sh vendor/bin/php-cs-fixer fix src/ tests/ --allow-risky=yes --config=.php-cs-fixer.dist.php
|
||||
6
scripts/test/phpspec-tests.sh
Executable file
6
scripts/test/phpspec-tests.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Phpspec BDD 测试脚本
|
||||
|
||||
# 运行所有 Phpspec 测试
|
||||
./run-in-docker.sh vendor/bin/phpspec run
|
||||
6
scripts/test/static-analysis.sh
Executable file
6
scripts/test/static-analysis.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 代码静态分析脚本
|
||||
|
||||
# 运行完整的代码静态分析
|
||||
./run-in-docker.sh vendor/bin/phpstan analyse src/ tests/
|
||||
6
scripts/test/unit-tests.sh
Executable file
6
scripts/test/unit-tests.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 非数据库依赖的单元测试脚本
|
||||
|
||||
# 运行非数据库依赖的单元测试
|
||||
./run-in-docker.sh vendor/bin/pest tests/Unit/ --exclude-group database --coroutine --prepend=tests/bootstrap.php --colors=always
|
||||
31
src/Application/Command/CreateInvoiceCmd.php
Normal file
31
src/Application/Command/CreateInvoiceCmd.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CreateInvoiceCmd.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Application\Command;
|
||||
|
||||
use Money\Money;
|
||||
|
||||
final readonly class CreateInvoiceCmd
|
||||
{
|
||||
public function __construct(
|
||||
public string $caseId,
|
||||
public bool $setFreqInvAddr,
|
||||
public string $receiver,
|
||||
public string $patientName,
|
||||
public string $address,
|
||||
public string $city,
|
||||
public string $state,
|
||||
public string $country,
|
||||
public string $zipCode,
|
||||
public Money $price,
|
||||
public string $currencySymbol,
|
||||
) {}
|
||||
}
|
||||
40
src/Application/Command/InitialAccountCmd.php
Normal file
40
src/Application/Command/InitialAccountCmd.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InitialAccountCmd.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/17
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Application\Command;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
|
||||
final class InitialAccountCmd
|
||||
{
|
||||
public array $pointsBalances;
|
||||
|
||||
public function __construct(
|
||||
public readonly string $uid,
|
||||
) {}
|
||||
|
||||
public function addPointsBalance(
|
||||
PointType $type,
|
||||
float $basic = 0.0,
|
||||
float $bonus = 0.0,
|
||||
?Carbon $expiredAt = null,
|
||||
?string $version = null,
|
||||
): array {
|
||||
return $this->pointsBalances[] = [
|
||||
'type' => $type->value,
|
||||
'basic' => $basic,
|
||||
'bonus' => $bonus,
|
||||
'expired_at' => $expiredAt->toDateTimeString(),
|
||||
'version' => $version,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -43,28 +43,38 @@ final class RechargeProductsDto extends AbstractDto
|
||||
$effect = $product->effect;
|
||||
$price = $product->unitPrice;
|
||||
|
||||
return [
|
||||
$result = [
|
||||
'id' => $product->id,
|
||||
'name' => $product->description,
|
||||
'currency' => $price->getCurrency()->getCode(),
|
||||
'price' => (float)$price->getAmount(),
|
||||
'total_points' => $effect->getPointTotal(),
|
||||
'bonus_rate_pct' => (float)(bcmul(
|
||||
num1: $effect->getPointTotal() == 0
|
||||
? '0'
|
||||
: bcdiv(
|
||||
(string)$effect->pointBonus,
|
||||
(string)$effect->getPointTotal(),
|
||||
3,
|
||||
),
|
||||
num2: '100',
|
||||
scale: 0,
|
||||
)),
|
||||
'point' => [
|
||||
'total' => $effect->getPointTotal(),
|
||||
'number' => $effect->pointBasic,
|
||||
'bonus' => $effect->pointBonus,
|
||||
],
|
||||
'price' => (float)bcdiv($price->getAmount(), '100', 2),
|
||||
];
|
||||
|
||||
$is_ftai = empty($effect);
|
||||
if (!$is_ftai) {
|
||||
$result += [
|
||||
'total_points' => $effect->getPointTotal(),
|
||||
'bonus_rate_pct' => round(
|
||||
(float)(bcmul(
|
||||
num1: $effect->getPointTotal() == 0
|
||||
? '0'
|
||||
: bcdiv(
|
||||
(string)$effect->pointBonus,
|
||||
(string)$effect->getPointTotal(),
|
||||
3,
|
||||
),
|
||||
num2: '100',
|
||||
scale: 3,
|
||||
)),
|
||||
),
|
||||
'point' => [
|
||||
'total' => $effect->getPointTotal(),
|
||||
'number' => $effect->pointBasic,
|
||||
'bonus' => $effect->pointBonus,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,16 @@ use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Hyperf\Framework\Logger\StdoutLogger;
|
||||
use Singularity\HDK\Pay\Domain\Account\Repository\AccountRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Account\Repository\PointLogRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\FrequentAddressRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceProductRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Product\Repository\ExchangeRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Product\Repository\RechargeProductRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Transaction\Repository\OrderRepoInterface;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\FrequentAddressRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceProductRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\OrderRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\PointLogRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\ProductRepo;
|
||||
@@ -33,11 +39,21 @@ class ConfigProvider
|
||||
StdoutLoggerInterface::class => StdoutLogger::class,
|
||||
|
||||
// Repo
|
||||
AccountRepoInterface::class => AccountBalanceRepo::class,
|
||||
// account
|
||||
AccountRepoInterface::class => AccountRepo::class,
|
||||
PointLogRepoInterface::class => PointLogRepo::class,
|
||||
|
||||
// product
|
||||
ExchangeRepoInterface::class => ProductRepo::class,
|
||||
RechargeProductRepoInterface::class => ProductRepo::class,
|
||||
|
||||
// transaction
|
||||
OrderRepoInterface::class => OrderRepo::class,
|
||||
PointLogRepoInterface::class => PointLogRepo::class,
|
||||
|
||||
// invoice
|
||||
InvoiceProductRepoInterface::class => InvoiceProductRepo::class,
|
||||
InvoiceRepoInterface::class => InvoiceRepo::class,
|
||||
FrequentAddressRepoInterface::class => FrequentAddressRepo::class,
|
||||
],
|
||||
// 合并到 config/autoload/annotations.php 文件
|
||||
'annotations' => [
|
||||
@@ -62,4 +78,4 @@ class ConfigProvider
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\ValueObject\InvoiceRecord;
|
||||
use Singularity\HDK\Pay\Domain\AggregateRoot;
|
||||
|
||||
final class PointLog extends AggregateRoot
|
||||
@@ -24,9 +25,17 @@ final class PointLog extends AggregateRoot
|
||||
private readonly string $source,
|
||||
private readonly string $operator,
|
||||
private readonly string $description,
|
||||
private readonly string $patientName,
|
||||
private readonly Carbon $date,
|
||||
private readonly bool $invoiceable,
|
||||
private readonly ?InvoiceRecord $invoiceRecord,
|
||||
) {}
|
||||
|
||||
public function getPatientName(): string
|
||||
{
|
||||
return $this->patientName;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
@@ -67,4 +76,19 @@ final class PointLog extends AggregateRoot
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getInvoiceNo(): ?string
|
||||
{
|
||||
return $this->invoiceRecord?->invoiceNo;
|
||||
}
|
||||
|
||||
public function getInvoiceReceiver(): ?string
|
||||
{
|
||||
return $this->invoiceRecord?->receiver;
|
||||
}
|
||||
|
||||
public function getInvoiceable(): bool
|
||||
{
|
||||
return $this->invoiceable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceRecord.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/4
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\ValueObject;
|
||||
|
||||
final class InvoiceRecord
|
||||
{
|
||||
public function __construct(
|
||||
public string $invoiceNo,
|
||||
public string $receiver,
|
||||
) {}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ enum PointType: string
|
||||
case Aligner4D = 'aligner-4d';
|
||||
case NightguardAi = 'nightguard-ai';
|
||||
|
||||
case ScanToModel = 'scan-to-model';
|
||||
|
||||
case FourDXUnlimitedBonusCredits = 'aligner-4d-u';
|
||||
|
||||
public static function values(): array
|
||||
{
|
||||
return [
|
||||
@@ -30,6 +34,8 @@ enum PointType: string
|
||||
self::FtaiRetainer->value,
|
||||
self::Aligner4D->value,
|
||||
self::NightguardAi->value,
|
||||
self::ScanToModel->value,
|
||||
self::FourDXUnlimitedBonusCredits->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,52 @@
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/17
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Account\Repository;
|
||||
|
||||
use Singularity\HDK\Pay\Application\Command\InitialAccountCmd;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Domain\Account\ValueObject\Email;
|
||||
|
||||
interface AccountRepoInterface
|
||||
{
|
||||
/**
|
||||
* @param string $uid
|
||||
* @return AccountBalance
|
||||
*/
|
||||
public function getAccount(string $uid): AccountBalance;
|
||||
|
||||
/**
|
||||
* @param string $uid
|
||||
* @param PointType $pointType
|
||||
* @return PointsBalance
|
||||
*/
|
||||
public function getPointBalance(string $uid, PointType $pointType): PointsBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InitialAccountCmd $cmd
|
||||
* @return void
|
||||
*/
|
||||
public function initial(InitialAccountCmd $cmd): void;
|
||||
|
||||
/**
|
||||
* 获取用户常用邮箱
|
||||
*
|
||||
* @param string $uid
|
||||
* @return Email
|
||||
*/
|
||||
public function getEmail(string $uid): Email;
|
||||
|
||||
/**
|
||||
* 更新用户常用邮箱
|
||||
*
|
||||
* @param string $uid
|
||||
* @param Email $email
|
||||
* @return void
|
||||
*/
|
||||
public function updateEmail(string $uid, Email $email): void;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,10 @@ interface PointLogRepoInterface
|
||||
* @return PointLog[]
|
||||
*/
|
||||
public function getList(string $uid): array;
|
||||
|
||||
/**
|
||||
* @param string $caseId
|
||||
* @return PointLog
|
||||
*/
|
||||
public function getDetail(string $caseId): PointLog;
|
||||
}
|
||||
38
src/Domain/Account/ValueObject/Email.php
Normal file
38
src/Domain/Account/ValueObject/Email.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Email.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/11/28
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Account\ValueObject;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\ValueObject;
|
||||
|
||||
final class Email extends ValueObject
|
||||
{
|
||||
private string $email;
|
||||
|
||||
public function __construct(string $email)
|
||||
{
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new \InvalidArgumentException('Invalid email address: ' . $email);
|
||||
}
|
||||
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
}
|
||||
56
src/Domain/Invoice/Aggregate/Address/FrequentAddress.php
Normal file
56
src/Domain/Invoice/Aggregate/Address/FrequentAddress.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryFreqAddrDto.php@LuxPay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\AggregateRoot;
|
||||
|
||||
final class FrequentAddress extends AggregateRoot
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $uid,
|
||||
private readonly string $address,
|
||||
private readonly string $city,
|
||||
private readonly string $state,
|
||||
private readonly string $country,
|
||||
private readonly string $zipCode,
|
||||
) {}
|
||||
|
||||
public function getUid(): string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function getAddress(): string
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function getCity(): string
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function getCountry(): string
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
public function getZipCode(): string
|
||||
{
|
||||
return $this->zipCode;
|
||||
}
|
||||
}
|
||||
53
src/Domain/Invoice/Aggregate/Invoice/Address.php
Normal file
53
src/Domain/Invoice/Aggregate/Invoice/Address.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Address.php@LuxPay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
|
||||
|
||||
final readonly class Address {
|
||||
public function __construct(
|
||||
private string $patientName,
|
||||
private string $address,
|
||||
private string $city,
|
||||
private string $state,
|
||||
private string $country,
|
||||
private string $zipCode,
|
||||
) {}
|
||||
|
||||
public function getPatientName(): string
|
||||
{
|
||||
return $this->patientName;
|
||||
}
|
||||
|
||||
public function getAddress(): string
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function getCity(): string
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function getCountry(): string
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
public function getZipCode(): string
|
||||
{
|
||||
return $this->zipCode;
|
||||
}
|
||||
}
|
||||
99
src/Domain/Invoice/Aggregate/Invoice/Invoice.php
Normal file
99
src/Domain/Invoice/Aggregate/Invoice/Invoice.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceDto.php@LuxPay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
|
||||
|
||||
use Money\Money;
|
||||
use Singularity\HDK\Pay\Domain\AggregateRoot;
|
||||
|
||||
final class Invoice extends AggregateRoot
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $invoiceNo,
|
||||
private readonly string $uid,
|
||||
private readonly string $caseId,
|
||||
private readonly Address $address,
|
||||
private readonly bool $setFreqInvAddr,
|
||||
private readonly string $receiver,
|
||||
private readonly Money $price,
|
||||
private readonly string $currencySymbol,
|
||||
) {}
|
||||
|
||||
public function getAmount(): float
|
||||
{
|
||||
return (float)bcdiv($this->price->getAmount(), '100', 2);
|
||||
}
|
||||
|
||||
public function getCurrencyCode(): string
|
||||
{
|
||||
return $this->price->getCurrency()->getCode();
|
||||
}
|
||||
|
||||
public function getCurrencySymbol(): string
|
||||
{
|
||||
return $this->currencySymbol;
|
||||
}
|
||||
|
||||
public function getInvoiceNo(): string
|
||||
{
|
||||
return $this->invoiceNo;
|
||||
}
|
||||
|
||||
public function getUid(): string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function getCaseId(): string
|
||||
{
|
||||
return $this->caseId;
|
||||
}
|
||||
|
||||
public function isSetFreqInvAddr(): bool
|
||||
{
|
||||
return $this->setFreqInvAddr;
|
||||
}
|
||||
|
||||
public function getReceiver(): string
|
||||
{
|
||||
return $this->receiver;
|
||||
}
|
||||
|
||||
public function getPatientName(): string
|
||||
{
|
||||
return $this->address->getPatientName();
|
||||
}
|
||||
|
||||
public function getAddress(): string
|
||||
{
|
||||
return $this->address->getAddress();
|
||||
}
|
||||
|
||||
public function getCity(): string
|
||||
{
|
||||
return $this->address->getCity();
|
||||
}
|
||||
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->address->getState();
|
||||
}
|
||||
|
||||
public function getCountry(): string
|
||||
{
|
||||
return $this->address->getCountry();
|
||||
}
|
||||
|
||||
public function getZipCode(): string
|
||||
{
|
||||
return $this->address->getZipCode();
|
||||
}
|
||||
}
|
||||
100
src/Domain/Invoice/Aggregate/Invoice/InvoiceInfo.php
Normal file
100
src/Domain/Invoice/Aggregate/Invoice/InvoiceInfo.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceInfo.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/5
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Money\Money;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointBalance;
|
||||
|
||||
final readonly class InvoiceInfo
|
||||
{
|
||||
public function __construct(
|
||||
private string $invoiceNo,
|
||||
private string $uid,
|
||||
private string $caseId,
|
||||
private Address $address,
|
||||
private InvoiceProduct $product,
|
||||
private PointBalance $balance,
|
||||
private string $receiver,
|
||||
private Carbon $invoiceAt,
|
||||
private Carbon $designedAt,
|
||||
private string $patientName,
|
||||
private Money $price,
|
||||
private string $currencySymbol,
|
||||
) {}
|
||||
|
||||
public function getAmount(): float
|
||||
{
|
||||
return (float)bcdiv($this->price->getAmount(), '100', 2);
|
||||
}
|
||||
|
||||
public function getCurrencyCode(): string
|
||||
{
|
||||
return $this->price->getCurrency()->getCode();
|
||||
}
|
||||
|
||||
public function getCurrencySymbol(): string
|
||||
{
|
||||
return $this->currencySymbol;
|
||||
}
|
||||
|
||||
public function getInvoiceNo(): string
|
||||
{
|
||||
return $this->invoiceNo;
|
||||
}
|
||||
|
||||
public function getUid(): string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function getCaseId(): string
|
||||
{
|
||||
return $this->caseId;
|
||||
}
|
||||
|
||||
public function getAddress(): Address
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function getProduct(): InvoiceProduct
|
||||
{
|
||||
return $this->product;
|
||||
}
|
||||
|
||||
public function getBalance(): PointBalance
|
||||
{
|
||||
return $this->balance;
|
||||
}
|
||||
|
||||
public function getReceiver(): string
|
||||
{
|
||||
return $this->receiver;
|
||||
}
|
||||
|
||||
public function getInvoiceAt(): Carbon
|
||||
{
|
||||
return $this->invoiceAt;
|
||||
}
|
||||
|
||||
public function getDesignedAt(): Carbon
|
||||
{
|
||||
return $this->designedAt;
|
||||
}
|
||||
|
||||
public function getPatientName(): string
|
||||
{
|
||||
return $this->patientName;
|
||||
}
|
||||
}
|
||||
33
src/Domain/Invoice/Aggregate/Invoice/InvoiceProduct.php
Normal file
33
src/Domain/Invoice/Aggregate/Invoice/InvoiceProduct.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceProduct.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointPrice;
|
||||
|
||||
final readonly class InvoiceProduct {
|
||||
/**
|
||||
* @param string $caseId
|
||||
* @param string $uid
|
||||
* @param string $name
|
||||
* @param string $sku
|
||||
* @param string $description
|
||||
* @param PointPrice[] $prices
|
||||
*/
|
||||
public function __construct(
|
||||
public string $caseId,
|
||||
public string $uid,
|
||||
public string $name,
|
||||
public string $sku,
|
||||
public string $description,
|
||||
public array $prices,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PointBalance.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/5
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject;
|
||||
|
||||
final readonly class PointBalance {
|
||||
public function __construct(
|
||||
public float $total,
|
||||
public float $cost,
|
||||
public float $remain,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PointPrice.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/11/25
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject;
|
||||
|
||||
use Money\Money;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
|
||||
final readonly class PointPrice
|
||||
{
|
||||
public function __construct(
|
||||
public Money $price,
|
||||
public string $currencySymbol,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* FrequentAddressRepoInterface.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
|
||||
|
||||
interface FrequentAddressRepoInterface
|
||||
{
|
||||
/**
|
||||
* @param string $uid
|
||||
* @return FrequentAddress
|
||||
*/
|
||||
public function findByUser(string $uid): FrequentAddress;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* InvoiceProductRepoInterface.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
|
||||
|
||||
interface InvoiceProductRepoInterface
|
||||
{
|
||||
/**
|
||||
* @param string $caseId
|
||||
* @return InvoiceProduct
|
||||
*/
|
||||
public function getCaseProduct(string $caseId): InvoiceProduct;
|
||||
}
|
||||
46
src/Domain/Invoice/Repository/InvoiceRepoInterface.php
Normal file
46
src/Domain/Invoice/Repository/InvoiceRepoInterface.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* InvoiceRepoInterface.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Invoice\Repository;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceInfo;
|
||||
|
||||
interface InvoiceRepoInterface
|
||||
{
|
||||
/**
|
||||
* @param CreateInvoiceCmd $cmd
|
||||
* @return Invoice
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function create(CreateInvoiceCmd $cmd): Invoice;
|
||||
|
||||
/**
|
||||
* @param string $invoiceNo
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function download(string $invoiceNo): ResponseInterface;
|
||||
|
||||
/**
|
||||
* @param string $invoiceNo
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function send(string $invoiceNo): void;
|
||||
|
||||
/**
|
||||
* @param string $invoiceNo
|
||||
* @return InvoiceInfo
|
||||
*/
|
||||
public function findOne(string $invoiceNo): InvoiceInfo;
|
||||
}
|
||||
@@ -22,6 +22,6 @@ final class ProductItem
|
||||
public string $description,
|
||||
public Money $unitPrice,
|
||||
public ProductType $productType,
|
||||
public RechargeEffect $effect,
|
||||
public ?RechargeEffect $effect = null,
|
||||
) {}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ interface ExchangeRepoInterface
|
||||
/**
|
||||
* @param PointType $source
|
||||
* @param PointType $target
|
||||
* @param string|null $uid
|
||||
* @return float
|
||||
*/
|
||||
public function getRate(PointType $source, PointType $target): float;
|
||||
public function getRate(PointType $source, PointType $target, ?string $uid = null): float;
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain\Product\Repository;
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
|
||||
|
||||
interface RechargeProductRepoInterface
|
||||
@@ -23,4 +24,11 @@ interface RechargeProductRepoInterface
|
||||
* @return RechargeProduct
|
||||
*/
|
||||
public function findEmaProduct(string $uid): RechargeProduct;
|
||||
|
||||
/**
|
||||
* @param string $uid
|
||||
* @param PointType $pointType
|
||||
* @return RechargeProduct
|
||||
*/
|
||||
public function findFtaiProduct(string $uid, PointType $pointType): RechargeProduct;
|
||||
}
|
||||
@@ -7,10 +7,11 @@
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/12
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Domain;
|
||||
|
||||
use Swoole\ArrayObject;
|
||||
|
||||
abstract class ValueObject extends ArrayObject {}
|
||||
abstract class ValueObject extends ArrayObject {}
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AccountBalanceRpc.php@Pay
|
||||
* AccountRepo.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/17
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Infrastructure\Repository;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Codec\Json;
|
||||
use Singularity\HDK\Pay\Application\Command\InitialAccountCmd;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Domain\Account\Repository\AccountRepoInterface;
|
||||
use Singularity\HDK\Pay\Domain\Account\ValueObject\Email;
|
||||
|
||||
final class AccountBalanceRepo extends AbstractRepo implements AccountRepoInterface
|
||||
final class AccountRepo extends AbstractRepo implements AccountRepoInterface
|
||||
{
|
||||
public function getAccount(string $uid): AccountBalance
|
||||
{
|
||||
@@ -35,13 +39,13 @@ final class AccountBalanceRepo extends AbstractRepo implements AccountRepoInterf
|
||||
uid: $result['uid'],
|
||||
pointsBalances: array_map(
|
||||
callback: fn($pointBalance)
|
||||
=> new PointsBalance(
|
||||
=> new PointsBalance(
|
||||
type: PointType::from($pointBalance['name']),
|
||||
total: $pointBalance['total'],
|
||||
cost: $pointBalance['cost'],
|
||||
amount: $pointBalance['amount'],
|
||||
version: $pointBalance['version'],
|
||||
expiredAt: $pointBalance['expired_at'],
|
||||
expiredAt: isset($pointBalance['expired_at']) ? new Carbon($pointBalance['expired_at']) : null,
|
||||
),
|
||||
array: $result['point_balance'],
|
||||
),
|
||||
@@ -63,7 +67,42 @@ final class AccountBalanceRepo extends AbstractRepo implements AccountRepoInterf
|
||||
cost: $result['cost'],
|
||||
amount: $result['amount'],
|
||||
version: $result['version'],
|
||||
expiredAt: $result['expired_at'],
|
||||
expiredAt: isset($result['expired_at']) ? new Carbon($result['expired_at']) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function initial(InitialAccountCmd $cmd): void
|
||||
{
|
||||
$uid = $cmd->uid;
|
||||
|
||||
$this->requestService->requestPost(
|
||||
url: "/rpc/v2/account/$uid/balance",
|
||||
data: $cmd->pointsBalances
|
||||
);
|
||||
}
|
||||
|
||||
public function getEmail(string $uid): Email
|
||||
{
|
||||
$response = $this->requestService->requestGet(
|
||||
url: "/rpc/v2/account/$uid/inv-email"
|
||||
);
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new Email($result['email']);
|
||||
}
|
||||
|
||||
public function updateEmail(string $uid, Email $email): void
|
||||
{
|
||||
$this->requestService->requestPut(
|
||||
url: "/rpc/v2/account/$uid/inv-email",
|
||||
data: [
|
||||
'email' => $email->getValue()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
41
src/Infrastructure/Repository/FrequentAddressRepo.php
Normal file
41
src/Infrastructure/Repository/FrequentAddressRepo.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* FrequentAddressRepo.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Infrastructure\Repository;
|
||||
|
||||
use Hyperf\Codec\Json;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\FrequentAddressRepoInterface;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AbstractRepo;
|
||||
|
||||
final class FrequentAddressRepo extends AbstractRepo implements FrequentAddressRepoInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function findByUser(string $uid): FrequentAddress
|
||||
{
|
||||
$response = $this->requestService->requestGet("/rpc/v2/account/$uid/inv-addr");
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new FrequentAddress(
|
||||
uid: $result['uid'],
|
||||
address: $result['address'],
|
||||
city: $result['city'],
|
||||
state: $result['state'],
|
||||
country: $result['country'],
|
||||
zipCode: $result['zip']
|
||||
);
|
||||
}
|
||||
}
|
||||
51
src/Infrastructure/Repository/InvoiceProductRepo.php
Normal file
51
src/Infrastructure/Repository/InvoiceProductRepo.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceProductRepo.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Infrastructure\Repository;
|
||||
|
||||
use Hyperf\Codec\Json;
|
||||
use Money\Currency;
|
||||
use Money\Money;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointPrice;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceProductRepoInterface;
|
||||
|
||||
final class InvoiceProductRepo extends AbstractRepo implements InvoiceProductRepoInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getCaseProduct(string $caseId): InvoiceProduct
|
||||
{
|
||||
$response = $this->requestService->requestGet("/rpc/v2/invoice/$caseId/product");
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new InvoiceProduct(
|
||||
caseId: $result['case_id'],
|
||||
uid: $result['uid'],
|
||||
name: $result['name'],
|
||||
sku: $result['sku'],
|
||||
description: $result['description'],
|
||||
prices: array_map(
|
||||
callback: fn(array $price) => new PointPrice(
|
||||
price: new Money(
|
||||
amount: bcmul((string)$price['amount'], '100', 2),
|
||||
currency: new Currency($price['currency']),
|
||||
),
|
||||
currencySymbol: $price['symbol'],
|
||||
),
|
||||
array: $result['prices'],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
157
src/Infrastructure/Repository/InvoiceRepo.php
Normal file
157
src/Infrastructure/Repository/InvoiceRepo.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InvoiceRepo.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Pay\Infrastructure\Repository;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Codec\Json;
|
||||
use Money\Currency;
|
||||
use Money\Money;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Singularity\HDK\Core\Exceptions\ValidateException;
|
||||
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Address;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceInfo;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointBalance;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointPrice;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Repository\InvoiceRepoInterface;
|
||||
|
||||
final class InvoiceRepo extends AbstractRepo implements InvoiceRepoInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function create(CreateInvoiceCmd $cmd): Invoice
|
||||
{
|
||||
$money = $cmd->price;
|
||||
$response = $this->requestService->requestPost(
|
||||
url: "/rpc/v2/account/logs/points/$cmd->caseId/invoices",
|
||||
data: [
|
||||
'set_freq_addr' => $cmd->setFreqInvAddr,
|
||||
'receiver' => $cmd->receiver,
|
||||
'patient_name' => $cmd->patientName,
|
||||
'address' => $cmd->address,
|
||||
'city' => $cmd->city,
|
||||
'state' => $cmd->state,
|
||||
'country' => $cmd->country,
|
||||
'zip' => $cmd->zipCode,
|
||||
'price' => [
|
||||
'amount' => (float)bcmul($money->getAmount(), '100', 2),
|
||||
'currency' => [
|
||||
'code' => $money->getCurrency()->getCode(),
|
||||
'symbol' => $cmd->currencySymbol,
|
||||
],
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
$price = $result['price'];
|
||||
return new Invoice(
|
||||
invoiceNo: $result['invoice_no'],
|
||||
uid: $result['uid'],
|
||||
caseId: $result['case_id'],
|
||||
address: new Address(
|
||||
patientName: $result['patient_name'],
|
||||
address: $result['address'],
|
||||
city: $result['city'],
|
||||
state: $result['state'],
|
||||
country: $result['country'],
|
||||
zipCode: $result['zip'],
|
||||
),
|
||||
setFreqInvAddr: $result['set_freq_addr'],
|
||||
receiver: $result['receiver'],
|
||||
price: new Money(
|
||||
bcmul((string)$price['amount'], '100', 2),
|
||||
new Currency($price['code']),
|
||||
),
|
||||
currencySymbol: $price['symbol'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function send(string $invoiceNo): void
|
||||
{
|
||||
if (empty($invoiceNo)) {
|
||||
throw new ValidateException(message: 'invoice no is required.');
|
||||
}
|
||||
$this->requestService->requestGet(url: "/rpc/v2/invoice/invoices/$invoiceNo/email");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function download(string $invoiceNo): ResponseInterface
|
||||
{
|
||||
if (empty($invoiceNo)) {
|
||||
throw new ValidateException(message: 'invoice no is required.');
|
||||
}
|
||||
|
||||
return $this->requestService->requestGet(url: "/rpc/v2/invoice/invoices/$invoiceNo/pdf");
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function findOne(string $invoiceNo): InvoiceInfo
|
||||
{
|
||||
if (empty($invoiceNo)) {
|
||||
throw new ValidateException(message: 'invoice no is required.');
|
||||
}
|
||||
|
||||
$response = $this->requestService->requestGet(url: "/rpc/v2/invoice/invoices/$invoiceNo");
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new InvoiceInfo(
|
||||
invoiceNo: $result['invoice_no'],
|
||||
uid: $result['uid'],
|
||||
caseId: $result['case_id'],
|
||||
address: new Address(
|
||||
patientName: $result['patient_name'],
|
||||
address: $result['address']['address'],
|
||||
city: $result['address']['city'],
|
||||
state: $result['address']['state'],
|
||||
country: $result['address']['country'],
|
||||
zipCode: $result['address']['zip_code'],
|
||||
),
|
||||
product: new InvoiceProduct(
|
||||
caseId: $result['case_id'],
|
||||
uid: $result['uid'],
|
||||
name: $result['product']['name'],
|
||||
sku: $result['product']['sku'],
|
||||
description: $result['product']['description'],
|
||||
prices: [],
|
||||
),
|
||||
balance: new PointBalance(
|
||||
total: $result['balance']['total'],
|
||||
cost: $result['balance']['cost'],
|
||||
remain: $result['balance']['remain'],
|
||||
),
|
||||
receiver: $result['receiver'],
|
||||
invoiceAt: new Carbon($result['invoice_at']),
|
||||
designedAt: new Carbon($result['designed_at']),
|
||||
patientName: $result['patient_name'],
|
||||
price: new Money(
|
||||
bcmul((string)$result['price']['amount'], '100', 2),
|
||||
new Currency($result['price']['currency_code']),
|
||||
),
|
||||
currencySymbol: $result['price']['currency_symbol'],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,25 @@ declare(strict_types=1);
|
||||
namespace Singularity\HDK\Pay\Infrastructure\Repository;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Hyperf\Codec\Json;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\PointLog;
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\PointLog\ValueObject\InvoiceRecord;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Domain\Account\Repository\PointLogRepoInterface;
|
||||
|
||||
final class PointLogRepo extends AbstractRepo implements PointLogRepoInterface
|
||||
{
|
||||
public function getList(string $uid): array
|
||||
/**
|
||||
* @param string $uid
|
||||
* @param PointType|null $type
|
||||
* @return array{}|PointLog[]
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getList(string $uid, ?PointType $type = null): array
|
||||
{
|
||||
$response = $this->requestService->requestGet(url: "/rpc/v2/account/$uid/logs/points");
|
||||
$type = isset($type) ? $type->value : 'all';
|
||||
$response = $this->requestService->requestGet(url: "/rpc/v2/account/$uid/balance/$type/logs");
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
@@ -35,8 +44,46 @@ final class PointLogRepo extends AbstractRepo implements PointLogRepoInterface
|
||||
source: $item['source'],
|
||||
operator: $item['operator'],
|
||||
description: $item['description'],
|
||||
patientName: $item['patient_name'],
|
||||
date: new Carbon($item['date']),
|
||||
invoiceable: $item['invoiceable'],
|
||||
invoiceRecord: isset($item['invoice_record'])
|
||||
? new InvoiceRecord(
|
||||
invoiceNo: $item['invoice_record']['invoice_no'],
|
||||
receiver: $item['invoice_record']['receiver'],
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getDetail(string $caseId): PointLog
|
||||
{
|
||||
$response = $this->requestService->requestGet(url: "/rpc/v2/account/logs/points/$caseId");
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new PointLog(
|
||||
id: $result['id'],
|
||||
caseId: $result['case_id'],
|
||||
uid: $result['uid'],
|
||||
credits: $result['credits'],
|
||||
source: $result['source'],
|
||||
operator: $result['operator'],
|
||||
description: $result['description'],
|
||||
patientName: $result['patient_name'],
|
||||
date: new Carbon($result['date']),
|
||||
invoiceable: $result['invoiceable'],
|
||||
invoiceRecord: isset($result['invoice_record'])
|
||||
? new InvoiceRecord(
|
||||
$result['invoice_record']['invoice_no'],
|
||||
$result['invoice_record']['receiver'],
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
id: $result['one_time']['id'],
|
||||
description: $result['one_time']['name'],
|
||||
unitPrice: new Money(
|
||||
amount: $result['one_time']['price'],
|
||||
amount: bcmul((string)$result['one_time']['price'], '100'),
|
||||
currency: new Currency($result['one_time']['currency']),
|
||||
),
|
||||
productType: ProductType::oneTime,
|
||||
@@ -55,20 +55,82 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
pointBonus: $result['one_time']['point']['bonus'],
|
||||
),
|
||||
),
|
||||
packages: array_map(fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: $item['price'],
|
||||
currency: new Currency($item['currency']),
|
||||
packages: array_map(
|
||||
fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: bcmul((string)$item['price'], '100'),
|
||||
currency: new Currency($item['currency']),
|
||||
),
|
||||
productType: ProductType::pack,
|
||||
effect: new RechargeEffect(
|
||||
pointType: PointType::LuxPoint,
|
||||
pointBasic: $item['point']['number'],
|
||||
pointBonus: $item['point']['bonus'],
|
||||
),
|
||||
),
|
||||
productType: ProductType::pack,
|
||||
effect: new RechargeEffect(
|
||||
pointType: PointType::LuxPoint,
|
||||
pointBasic: $item['point']['number'],
|
||||
pointBonus: $item['point']['bonus'],
|
||||
$result['package'] ?? [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function findFtaiProduct(string $uid, PointType $pointType): RechargeProduct
|
||||
{
|
||||
$response = $this->requestService->requestGet(
|
||||
url: '/rpc/v2/products/ftai',
|
||||
params: ['uid' => $uid, 'type' => $pointType->value],
|
||||
);
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$result = Json::decode($content);
|
||||
|
||||
return new RechargeProduct(
|
||||
oneTime: isset($result['one_time'])
|
||||
? new ProductItem(
|
||||
id: $result['one_time']['id'],
|
||||
description: $result['one_time']['name'],
|
||||
unitPrice: new Money(
|
||||
amount: bcmul((string)$result['one_time']['price'], '100'),
|
||||
currency: new Currency($result['one_time']['currency']),
|
||||
),
|
||||
productType: ProductType::oneTime,
|
||||
effect: new RechargeEffect(
|
||||
pointType: $pointType,
|
||||
pointBasic: $result['one_time']['point']['number'],
|
||||
pointBonus: $result['one_time']['point']['bonus'],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
plans: array_map(
|
||||
fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: bcmul((string)$item['price'], '100'),
|
||||
currency: new Currency($item['currency']),
|
||||
),
|
||||
productType: ProductType::plan,
|
||||
),
|
||||
), $result['package']),
|
||||
$result['plan'] ?? [],
|
||||
),
|
||||
packages: array_map(
|
||||
fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: bcmul((string)$item['price'], '100'),
|
||||
currency: new Currency($item['currency']),
|
||||
),
|
||||
productType: ProductType::plan,
|
||||
effect: new RechargeEffect(
|
||||
pointType: $pointType,
|
||||
pointBasic: $item['point']['number'],
|
||||
pointBonus: $item['point']['bonus'],
|
||||
),
|
||||
),
|
||||
$result['package'] ?? [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,7 +153,7 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
id: $result['one_time']['id'],
|
||||
description: $result['one_time']['name'],
|
||||
unitPrice: new Money(
|
||||
amount: $result['one_time']['price'],
|
||||
amount: bcmul((string)$result['one_time']['price'], '100'),
|
||||
currency: new Currency($result['one_time']['currency']),
|
||||
),
|
||||
productType: ProductType::oneTime,
|
||||
@@ -107,7 +169,7 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
id: $result['renew']['id'],
|
||||
description: $result['renew']['name'],
|
||||
unitPrice: new Money(
|
||||
amount: $result['renew']['price'],
|
||||
amount: bcmul((string)$result['renew']['price'], '100'),
|
||||
currency: new Currency($result['renew']['currency']),
|
||||
),
|
||||
productType: ProductType::renew,
|
||||
@@ -118,20 +180,23 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
),
|
||||
)
|
||||
: null,
|
||||
plans: array_map(fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: $item['price'],
|
||||
currency: new Currency($item['currency']),
|
||||
plans: array_map(
|
||||
fn(array $item) => new ProductItem(
|
||||
id: $item['id'],
|
||||
description: $item['name'],
|
||||
unitPrice: new Money(
|
||||
amount: bcmul((string)$item['price'], '100'),
|
||||
currency: new Currency($item['currency']),
|
||||
),
|
||||
productType: ProductType::plan,
|
||||
effect: new RechargeEffect(
|
||||
pointType: PointType::EMA,
|
||||
pointBasic: $item['point']['number'],
|
||||
pointBonus: $item['point']['bonus'],
|
||||
),
|
||||
),
|
||||
productType: ProductType::plan,
|
||||
effect: new RechargeEffect(
|
||||
pointType: PointType::LuxPoint,
|
||||
pointBasic: $item['point']['number'],
|
||||
pointBonus: $item['point']['bonus'],
|
||||
),
|
||||
), $result['plan']),
|
||||
$result['plan'] ?? [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,10 +206,11 @@ final class ProductRepo extends AbstractRepo implements RechargeProductRepoInter
|
||||
* @return float
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getRate(PointType $source, PointType $target): float
|
||||
public function getRate(PointType $source, PointType $target, ?string $uid = null): float
|
||||
{
|
||||
$response = $this->requestService->requestGet(
|
||||
url: "/rpc/v2/products/$target->value/exchange-rate/$source->value",
|
||||
params: array_filter(['uid' => $uid]),
|
||||
);
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
|
||||
53
tests/Feature/Account/InitialAccountBalanceTest.php
Normal file
53
tests/Feature/Account/InitialAccountBalanceTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* InitialAccountBalanceTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/17
|
||||
*/
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Singularity\HDK\Pay\Application\Command\InitialAccountCmd;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should initial account balance', function () {
|
||||
$uid = uniqid('TDD');
|
||||
$data = [
|
||||
[
|
||||
'type' => 'aligner',
|
||||
'basic' => 0,
|
||||
'bonus' => 40,
|
||||
'expired_at' => Carbon::now()->addYear(),
|
||||
'version' => 'trial',
|
||||
],
|
||||
[
|
||||
'type' => 'ema',
|
||||
'basic' => 0,
|
||||
'bonus' => 1,
|
||||
'expired_at' => Carbon::now()->addYear(),
|
||||
'version' => 'Trial',
|
||||
],
|
||||
];
|
||||
|
||||
$cmd = new InitialAccountCmd($uid);
|
||||
foreach ($data as $point_balance) {
|
||||
$cmd->addPointsBalance(
|
||||
type: PointType::from($point_balance['type']),
|
||||
basic: $point_balance['basic'],
|
||||
bonus: $point_balance['bonus'],
|
||||
expiredAt:$point_balance['expired_at'],
|
||||
version: $point_balance['version'],
|
||||
);
|
||||
}
|
||||
|
||||
$repo = make(AccountRepo::class);
|
||||
$repo->initial($cmd);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@@ -9,13 +9,13 @@
|
||||
*/
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\AccountBalance;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should be able to query account information', function () {
|
||||
$uid = '123456';
|
||||
$repo = make(AccountBalanceRepo::class);
|
||||
$repo = make(AccountRepo::class);
|
||||
$result = $repo->getAccount($uid);
|
||||
expect($result)
|
||||
->toBeInstanceOf(AccountBalance::class)
|
||||
|
||||
35
tests/Feature/Account/QueryEmailTest.php
Normal file
35
tests/Feature/Account/QueryEmailTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryEmailTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/11/28
|
||||
*/
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Account\ValueObject\Email;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should be able to get user email', function () {
|
||||
$uid = '123456';
|
||||
$repo = make(AccountRepo::class);
|
||||
$result = $repo->getEmail($uid);
|
||||
expect($result)
|
||||
->toBeInstanceOf(Email::class);
|
||||
});
|
||||
|
||||
it('should be able to update user email', function () {
|
||||
$uid = '123456';
|
||||
$newEmail = 'test@example.com';
|
||||
$repo = make(AccountRepo::class);
|
||||
|
||||
$email = new Email($newEmail);
|
||||
$repo->updateEmail($uid, $email);
|
||||
|
||||
// 验证更新后的邮箱
|
||||
$result = $repo->getEmail($uid);
|
||||
expect($result->getValue())->toBe($newEmail);
|
||||
});
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Account\Aggregate\Account\PointsBalance;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountBalanceRepo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\AccountRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should query point balance', function () {
|
||||
$uid = 'cn3221';
|
||||
$repo = make(AccountBalanceRepo::class);
|
||||
$repo = make(AccountRepo::class);
|
||||
$point_balance = $repo->getPointBalance($uid, PointType::EMA);
|
||||
expect($point_balance)
|
||||
->toBeInstanceOf(PointsBalance::class)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryPointLogListTest.php@Pay
|
||||
* QueryPointLogTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
@@ -23,3 +23,12 @@ it('should can query point log list', function () {
|
||||
->each
|
||||
->toBeInstanceOf(PointLog::class);
|
||||
});
|
||||
|
||||
it('should can query a point log', function () {
|
||||
$case_id = '68b6ac37a6440';
|
||||
|
||||
$repo = make(PointLogRepo::class);
|
||||
$log = $repo->getDetail($case_id);
|
||||
expect($log)
|
||||
->toBeInstanceOf(PointLog::class);
|
||||
});
|
||||
61
tests/Feature/Invoice/CreateInvoiceTest.php
Normal file
61
tests/Feature/Invoice/CreateInvoiceTest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CreateInvoiceTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
|
||||
use Money\Money;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Singularity\HDK\Pay\Application\Command\CreateInvoiceCmd;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\Invoice;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should can create invoice', function () {
|
||||
$repo = make(InvoiceRepo::class);
|
||||
|
||||
$invoice = $repo->create(
|
||||
new CreateInvoiceCmd(
|
||||
caseId: '68affb136c01d',
|
||||
setFreqInvAddr: true,
|
||||
receiver: "dongyun.li@luxcreo.ai",
|
||||
patientName: "Ms. Jennifer Durgan",
|
||||
address: "12345 Magnolia Boulevard Northeast,Apartment 5678, Suite 910",
|
||||
city: "Springfield",
|
||||
state: "Illinois",
|
||||
country: "United States",
|
||||
zipCode: "67890-1234",
|
||||
price: Money::EUR('19900'),
|
||||
currencySymbol: '€'
|
||||
),
|
||||
);
|
||||
|
||||
expect($invoice)
|
||||
->toBeInstanceOf(Invoice::class);
|
||||
});
|
||||
|
||||
it('should can send invoice email to receiver', function () {
|
||||
$repo = make(InvoiceRepo::class);
|
||||
|
||||
expect(
|
||||
(function () use ($repo) {
|
||||
$invoice_no = '517268';
|
||||
$repo->send($invoice_no);
|
||||
|
||||
return true;
|
||||
})(),
|
||||
)->not->toThrow(Throwable::class);
|
||||
});
|
||||
|
||||
it('should can download invoice pdf', function () {
|
||||
$repo = make(InvoiceRepo::class);
|
||||
|
||||
$invoice_no = '517268';
|
||||
$response = $repo->download($invoice_no);
|
||||
expect($response)->toBeInstanceOf(ResponseInterface::class);
|
||||
});
|
||||
29
tests/Feature/Invoice/QueryCaseInvoiceProductTest.php
Normal file
29
tests/Feature/Invoice/QueryCaseInvoiceProductTest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryCaseInvoiceProductTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/28
|
||||
*/
|
||||
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceProduct;
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\ValueObject\PointPrice;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceProductRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should can query case invoice product', function () {
|
||||
/** @var InvoiceProductRepo $invoiceProductRepo */
|
||||
$invoiceProductRepo = make(InvoiceProductRepo::class);
|
||||
|
||||
$caseId = '68affb136c01d';
|
||||
|
||||
$result = $invoiceProductRepo->getCaseProduct(caseId: $caseId);
|
||||
|
||||
expect($result)->toBeInstanceOf(InvoiceProduct::class)
|
||||
->prices->toBeArray()
|
||||
->each->toBeInstanceOf(PointPrice::class);
|
||||
});
|
||||
23
tests/Feature/Invoice/QueryFreqAddrTest.php
Normal file
23
tests/Feature/Invoice/QueryFreqAddrTest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryFreqAddrTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/8/29
|
||||
*/
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Address\FrequentAddress;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\FrequentAddressRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should can query frequent invoice address', function () {
|
||||
$uid = '61dbe752d4caa';
|
||||
|
||||
$addrRepo = make(FrequentAddressRepo::class);
|
||||
$freq_addr = $addrRepo->findByUser($uid);
|
||||
|
||||
expect($freq_addr)->toBeInstanceOf(FrequentAddress::class);
|
||||
});
|
||||
23
tests/Feature/Invoice/QueryInvoiceDetailTest.php
Normal file
23
tests/Feature/Invoice/QueryInvoiceDetailTest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* QueryInvoiceDetailTest.php@Pay
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2025/9/5
|
||||
*/
|
||||
|
||||
use Singularity\HDK\Pay\Domain\Invoice\Aggregate\Invoice\InvoiceInfo;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\InvoiceRepo;
|
||||
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
it('should can query invoice detail', function () {
|
||||
$repo = make(InvoiceRepo::class);
|
||||
|
||||
$invoiceNo = '517268';
|
||||
$result = $repo->findOne($invoiceNo);
|
||||
expect($result)
|
||||
->toBeInstanceOf(InvoiceInfo::class);
|
||||
});
|
||||
@@ -19,6 +19,20 @@ it('can query point rate', function () {
|
||||
$from = PointType::FtaiAligner;
|
||||
$to = PointType::LuxPoint;
|
||||
|
||||
$rate = $repo->getRate($from, $to);
|
||||
$uid = 'cn3321';
|
||||
|
||||
$rate = $repo->getRate($from, $to, $uid);
|
||||
expect($rate)->toBeFloat();
|
||||
});
|
||||
|
||||
it('can query point rate with uid', function () {
|
||||
$repo = make(ProductRepo::class);
|
||||
|
||||
$from = PointType::FtaiAligner;
|
||||
$to = PointType::LuxPoint;
|
||||
|
||||
$uid = 'cn3321';
|
||||
|
||||
$rate = $repo->getRate($from, $to, $uid);
|
||||
expect($rate)->toBeFloat();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
use Pest\Expectation;
|
||||
use Singularity\HDK\Pay\Domain\Account\Enum\PointType;
|
||||
use Singularity\HDK\Pay\Domain\Product\Aggregate\Entities\ProductItem;
|
||||
use Singularity\HDK\Pay\Domain\Product\Aggregate\RechargeProduct;
|
||||
use Singularity\HDK\Pay\Infrastructure\Repository\ProductRepo;
|
||||
@@ -44,3 +45,19 @@ it('should can query EMA products', function () {
|
||||
)
|
||||
->and($products->getPlans())->each->toBeInstanceOf(ProductItem::class);
|
||||
});
|
||||
|
||||
it('should can query FTAI products', function () {
|
||||
$repo = make(ProductRepo::class);
|
||||
|
||||
$uid = '61efabc21e5a2';
|
||||
$products = $repo->findFtaiProduct($uid, PointType::FtaiAligner);
|
||||
expect($products)
|
||||
->toBeInstanceOf(RechargeProduct::class)
|
||||
->when(
|
||||
$products->getOneTime() !== null,
|
||||
fn(Expectation $expectation) => $expectation
|
||||
->and($products->getOneTime())->toBeInstanceOf(ProductItem::class),
|
||||
)
|
||||
->and($products->getPackages())->each->toBeInstanceOf(ProductItem::class)
|
||||
->and($products->getPlans())->each->toBeInstanceOf(ProductItem::class);
|
||||
});
|
||||
|
||||
@@ -32,4 +32,4 @@ test(
|
||||
);
|
||||
expect($goods)->toBeInstanceOf(Goods::class);
|
||||
}
|
||||
);
|
||||
)->skip();
|
||||
|
||||
@@ -29,7 +29,7 @@ test('能够正常创建 Stripe 订单', function () {
|
||||
service: 1
|
||||
);
|
||||
expect($order)->toBeInstanceOf(Order::class);
|
||||
});
|
||||
})->skip();
|
||||
|
||||
test('能够正常获取 Stripe 配置信息', function () {
|
||||
/** @var StripeRpc $service */
|
||||
@@ -40,4 +40,4 @@ test('能够正常获取 Stripe 配置信息', function () {
|
||||
->toBeInstanceOf(StripeConfiguration::class)
|
||||
->toHaveKeys(['id', 'pk'])
|
||||
->and($configures->resolve())->toBeArray();
|
||||
});
|
||||
})->skip();
|
||||
|
||||
Reference in New Issue
Block a user