代码优化

This commit is contained in:
Billy 2025-10-20 15:51:06 +08:00
parent eee44f2816
commit 1b8d4538ae
10 changed files with 17 additions and 3317 deletions

View File

@ -88,29 +88,18 @@ class Ccbpayment extends Common
}
// 4. 生成支付串
// ⚠️ 注意: generatePaymentString()内部已经完成了以下操作:
// - 更新订单的ccb_pay_flow_id字段
// - 记录支付日志到ccb_payment_log表
// 控制器不应该重复操作,否则会导致数据重复写入!
$result = $this->paymentService->generatePaymentString($orderId);
if (!$result['status']) {
$this->error('支付串生成失败: ' . $result['message']);
}
// 5. 保存支付流水号到订单
$order->ccb_pay_flow_id = $result['data']['pay_flow_id']; // ✅ 使用真实的支付流水号
$order->save();
// 6. 记录支付日志
$this->savePaymentLog($order, $result['data']['payment_string'], $result['data']['pay_flow_id']);
// 7. 返回支付串
$this->success('支付串生成成功', [
'payment_string' => $result['data']['payment_string'],
'payment_url' => $result['data']['payment_url'],
'mac' => $result['data']['mac'],
'order_id' => $order->id,
'order_sn' => $order->order_sn,
'pay_flow_id' => $result['data']['pay_flow_id'], // ✅ 返回真实的支付流水号
'amount' => $result['data']['amount'],
]);
// 5. 直接返回支付串(不再重复保存数据库!)
$this->success('支付串生成成功', $result['data']);
} catch (Exception $e) {
Log::error('[建行支付] 生成支付串失败 order_id:' . ($orderId ?? 0) . ' error:' . $e->getMessage());

View File

@ -70,7 +70,6 @@ class CcbEncryption
{
$this->privateKey = $this->config['private_key'] ?? '';
$this->publicKey = $this->config['public_key'] ?? '';
$this->platformPublicKey = $this->config['platform_public_key'] ?? '';
if (empty($this->privateKey)) {
throw new Exception('服务方私钥未配置');

View File

@ -96,6 +96,9 @@ class CcbPaymentService
*/
public function generatePaymentString($orderId)
{
// ⚠️ 开启事务保护,确保数据一致性
Db::startTrans();
try {
// 获取订单信息
$order = Order::find($orderId);
@ -176,6 +179,9 @@ class CcbPaymentService
'pay_flow_id' => $payFlowId
]);
// ✅ 提交事务
Db::commit();
return [
'status' => true,
'message' => '支付串生成成功',
@ -185,11 +191,15 @@ class CcbPaymentService
'payment_url' => $paymentUrl,
'order_sn' => $order['order_sn'],
'pay_flow_id' => $payFlowId,
'amount' => number_format($order['pay_fee'], 2, '.', '')
'amount' => number_format($order['pay_fee'], 2, '.', ''),
'order_id' => $orderId // 补充order_id字段
]
];
} catch (\Exception $e) {
// ❌ 回滚事务
Db::rollback();
Log::error('建行支付串生成失败: ' . $e->getMessage());
return [
'status' => false,

View File

@ -1,500 +0,0 @@
# 建行生活前端测试指南
## 测试环境准备
### 1. 前端项目结构确认
确认以下文件已正确创建:
```
frontend/
├── sheep/platform/
│ ├── provider/ccblife/
│ │ ├── index.js ✅ 已创建
│ │ └── api.js ✅ 已创建
│ ├── index.js ✅ 已修改(添加建行支持)
│ └── pay.js ✅ 已修改(添加建行支付)
├── pages/ccblife/
│ └── index.vue ✅ 已创建
├── static/
│ └── ccb-test.html ✅ 已创建(测试页面)
└── pages.json ✅ 已修改(添加路由配置)
```
### 2. 检查文件完整性
```bash
cd /Users/billy/Code/fengketrade.com/frontend
# 检查建行模块文件
ls -l sheep/platform/provider/ccblife/
# 检查建行页面
ls -l pages/ccblife/
# 检查测试页面
ls -l static/ccb-test.html
```
## 测试方法
### 方法一:使用 HBuilderX推荐
这是 uni-app 项目的标准开发方式。
#### 步骤 1: 打开项目
1. 启动 HBuilderX
2. 文件 → 打开目录 → 选择 `frontend` 目录
3. 等待项目加载完成
#### 步骤 2: 运行到浏览器
1. 点击工具栏的"运行"按钮
2. 选择"运行到浏览器" → "Chrome"(或其他浏览器)
3. 等待编译完成,会自动打开浏览器
#### 步骤 3: 访问测试页面
在浏览器中访问:
```
http://localhost:8080/static/ccb-test.html
```
或访问建行专属页面:
```
http://localhost:8080/#/pages/ccblife/index
```
#### 步骤 4: 模拟建行环境
在 URL 后添加参数模拟建行环境:
```
http://localhost:8080/#/pages/index/index?from=ccblife&ccbParamSJ=test123
```
### 方法二:使用 Vite 开发服务器
如果项目配置了 Vite可以使用命令行运行。
#### 步骤 1: 安装依赖
```bash
cd frontend
npm install
```
#### 步骤 2: 启动开发服务器
```bash
# 如果有 dev:h5 脚本
npm run dev:h5
# 或者直接使用 vite
npx vite
```
#### 步骤 3: 访问测试页面
默认地址:`http://localhost:3000`(端口以实际为准)
### 方法三:使用测试服务器
将前端代码部署到测试服务器。
#### 步骤 1: 访问后端静态测试页面
```
http://fengketrade.test/ccblife-demo.html
```
这个页面已经在后端 public 目录创建,可以直接访问。
#### 步骤 2: 访问测试接口
```
http://fengketrade.test/addons/shopro/ccbtest
```
## 测试项目清单
### 1. 模块加载测试
#### 测试目标
验证建行模块是否正确导入和初始化。
#### 测试步骤
1. 打开浏览器开发者工具F12
2. 访问测试页面
3. 在 Console 中输入:
```javascript
// 检查 sheep 对象
console.log(window.$shop || window.sheep);
// 检查平台对象
console.log(sheep.$platform);
// 检查平台名称
console.log(sheep.$platform.name);
// 检查提供商
console.log(sheep.$platform.provider);
```
#### 预期结果
- 在非建行环境:`name` 应该是 `H5``WechatOfficialAccount`
- 在建行环境:`name` 应该是 `CcbLife``provider` 应该是 `ccb`
### 2. 环境检测测试
#### 测试目标
验证系统能否正确识别建行生活 App 环境。
#### 测试步骤
**情况 1普通浏览器**
```
访问: http://localhost:3000/
预期: 识别为 H5 或 WechatOfficialAccount 环境
```
**情况 2模拟建行环境URL 参数)**
```
访问: http://localhost:3000/?from=ccblife
预期: 识别为 CcbLife 环境
```
**情况 3模拟建行参数**
```
访问: http://localhost:3000/?ccbParamSJ=test123
预期: 识别为 CcbLife 环境
```
#### 验证方法
在 Console 中执行:
```javascript
// 方法1通过平台对象
console.log(sheep.$platform.provider === 'ccb' ? '建行环境' : '非建行环境');
// 方法2直接导入模块在实际项目中
// import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
// console.log(CcbLifePlatform.isInCcbApp);
```
### 3. 页面路由测试
#### 测试目标
验证建行专属页面是否可以正常访问。
#### 测试步骤
1. **访问首页**
```
http://localhost:3000/#/pages/index/index
```
2. **访问建行专属页面**
```
http://localhost:3000/#/pages/ccblife/index
```
3. **使用路由跳转**
在任意页面的 Console 中执行:
```javascript
uni.navigateTo({
url: '/pages/ccblife/index'
});
```
#### 预期结果
- 页面能正常加载
- 显示"建行生活"标题
- 导航栏背景色为建行红(#F51C13
### 4. API 接口测试
#### 测试目标
验证前端能否正确调用后端建行接口。
#### 测试步骤
在 Console 中执行:
```javascript
// 测试1URL跳转登录接口
fetch('/addons/shopro/ccblife/login?ccbParamSJ=test')
.then(res => res.json())
.then(data => console.log('登录接口:', data))
.catch(err => console.error('登录接口错误:', err));
// 测试2环境检查
fetch('/addons/shopro/ccbtest/checkConfig')
.then(res => res.json())
.then(data => console.log('配置检查:', data))
.catch(err => console.error('配置检查错误:', err));
// 测试3数据库检查
fetch('/addons/shopro/ccbtest/checkDatabase')
.then(res => res.json())
.then(data => console.log('数据库检查:', data))
.catch(err => console.error('数据库检查错误:', err));
```
#### 预期结果
- 接口返回 JSON 格式数据
- 不应该出现 404 或 500 错误
### 5. 支付功能测试
#### 测试目标
验证建行支付流程是否正常。
#### 前置条件
- 已登录用户
- 有可支付的订单
#### 测试步骤
1. **创建测试订单**(可选)
```
访问: http://fengketrade.test/#/pages/goods/index?id=1
添加商品到购物车 → 去结算 → 提交订单
```
2. **选择建行支付**
在支付页面,如果在建行环境中,应该看到"建行支付"选项。
3. **模拟支付调用**
在 Console 中执行:
```javascript
// 注意:这需要有实际的订单
sheep.$platform.pay('ccb', 'goods', 'ORDER202501170001');
```
#### 预期结果
- 非建行环境:提示"请在建行生活App内使用建行支付"
- 建行环境(模拟):尝试调用 JSBridge
### 6. 日志和错误测试
#### 测试目标
验证错误处理和日志记录是否正常。
#### 测试步骤
1. **查看 Console 日志**
启动应用后,在 Console 中应该看到:
```
[CcbLife] 初始化完成, 是否在建行App内: false
```
2. **触发错误**
在非建行环境调用建行功能:
```javascript
// 应该输出友好的错误提示
sheep.$platform.useProvider('ccb').getUserInfo()
.catch(err => console.log('预期的错误:', err));
```
#### 预期结果
- 有清晰的日志输出
- 错误信息友好可读
## 使用静态测试页面
访问 `http://fengketrade.test/static/ccb-test.html`(或前端开发服务器对应地址)
### 测试页面功能
1. **环境信息显示**
- 自动检测当前平台
- 显示是否在建行App内
- 显示 User-Agent 和 URL 参数
2. **模块加载测试**
- 点击"测试模块导入"按钮
- 检查建行模块文件是否存在
3. **环境检测测试**
- 点击"测试环境检测"按钮
- 查看多种检测方式的结果
4. **URL参数测试**
- 点击"测试URL参数解析"按钮
- 验证参数解析功能
### 模拟建行环境
在测试页面 URL 后添加参数:
```
?from=ccblife&ccbParamSJ=testdata123
```
完整示例:
```
http://localhost:3000/static/ccb-test.html?from=ccblife&ccbParamSJ=testdata123
```
## 常见问题排查
### 问题1页面打不开
**症状**:访问建行页面显示 404
**排查步骤**
1. 检查 `pages.json` 是否正确配置
```bash
grep -A 10 "ccblife" pages.json
```
2. 检查页面文件是否存在
```bash
ls -l pages/ccblife/index.vue
```
3. 重新编译项目(在 HBuilderX 中点击"停止" → "运行"
### 问题2模块导入失败
**症状**Console 显示 "Cannot find module" 错误
**排查步骤**
1. 检查文件路径
```bash
ls -l sheep/platform/provider/ccblife/
```
2. 检查文件内容是否有语法错误
```bash
node --check sheep/platform/provider/ccblife/index.js
```
3. 清除缓存重新编译
### 问题3平台识别错误
**症状**:在建行环境中仍识别为 H5
**排查步骤**
1. 检查 URL 参数
```javascript
console.log(window.location.search);
```
2. 检查 User-Agent
```javascript
console.log(navigator.userAgent);
```
3. 手动测试检测函数
```javascript
const urlParams = new URLSearchParams(window.location.search);
console.log('from参数:', urlParams.get('from'));
console.log('ccbParamSJ参数:', urlParams.get('ccbParamSJ'));
```
### 问题4API 请求失败
**症状**:接口返回 404 或跨域错误
**排查步骤**
1. 检查后端服务是否运行
```bash
curl http://fengketrade.test/addons/shopro/ccbtest
```
2. 检查 `.env` 配置
```bash
cat .env | grep SHOPRO_BASE_URL
```
3. 检查跨域配置(如果前后端分离)
## 真机测试
### iOS 设备测试
1. **使用 Safari 调试**
- Mac 上打开 Safari → 开发 → [设备名] → [页面]
- 可以查看 Console 和调试
2. **HBuilderX 真机运行**
- 连接 iOS 设备
- 运行 → 运行到手机或模拟器 → iOS
### Android 设备测试
1. **使用 Chrome 调试**
- 电脑 Chrome 访问 `chrome://inspect`
- 查看设备上的页面
2. **HBuilderX 真机运行**
- 连接 Android 设备(开启 USB 调试)
- 运行 → 运行到手机或模拟器 → Android
## 性能测试
### 加载时间
在 Console 中查看:
```javascript
// 查看性能数据
console.log(performance.timing);
// 计算加载时间
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
console.log('页面加载时间:', loadTime + 'ms');
```
### 内存使用
使用 Chrome DevTools
1. Performance → 录制
2. 操作页面
3. 停止录制
4. 查看内存使用情况
## 测试报告模板
```markdown
## 建行生活前端测试报告
**测试日期**: 2025-01-17
**测试环境**: [开发环境/测试环境]
**测试人员**: [姓名]
### 测试结果
| 测试项 | 状态 | 备注 |
|-------|------|------|
| 模块加载 | ✅ 通过 | 所有模块正常加载 |
| 环境检测 | ✅ 通过 | 正确识别建行环境 |
| 页面路由 | ✅ 通过 | 页面可以正常访问 |
| API接口 | ✅ 通过 | 接口返回正常 |
| 支付功能 | ⏳ 待测试 | 需要建行真机环境 |
### 发现的问题
1. [问题描述]
- **严重程度**: 高/中/低
- **复现步骤**: ...
- **预期行为**: ...
- **实际行为**: ...
### 建议
1. [建议内容]
```
---
*文档版本1.0.0*
*最后更新2025-01-17*
*作者Billy*

View File

@ -1,252 +0,0 @@
# 建行生活 H5 商城对接实施指南
## 项目概述
本文档描述了 Shopro 商城系统与建行生活 App 的完整对接方案实现。所有代码已根据您的实际数据库结构进行调整,确保与现有系统完美兼容。
## 已实现功能清单
### 1. 核心加密模块
- ✅ **RSA 加密解密** (`CcbRSA.php`):支持 1024 位 RSA117/128 字节分段处理
- ✅ **MD5 签名** (`CcbMD5.php`)API 消息签名和支付串签名
- ✅ **URL 参数解密** (`CcbUrlDecrypt.php`):双层 BASE64 + DES 解密
### 2. 业务服务类
- ✅ **HTTP 客户端** (`CcbHttpClient.php`):处理与建行 API 的通信
- ✅ **订单服务** (`CcbOrderService.php`):订单推送、查询、状态更新、退款
- ✅ **支付服务** (`CcbPaymentService.php`):支付串生成、回调处理、支付验证
### 3. 控制器接口
- ✅ **用户登录** (`Ccblife.php`):建行用户登录、自动登录、用户绑定
- ✅ **支付处理** (`Ccbpayment.php`):生成支付串、处理回调、推送订单
- ✅ **测试接口** (`Ccbtest.php`):全面的功能测试接口
### 4. 前端集成
- ✅ **JSBridge 库** (`ccblife-bridge.js`):封装建行原生交互功能
- ✅ **示例页面** (`ccblife-demo.html`):演示如何使用 JSBridge
## 数据库结构(已执行)
### 用户表改造
```sql
ALTER TABLE `fa_user`
ADD COLUMN `ccb_user_id` varchar(50) DEFAULT NULL COMMENT '建行用户ID';
```
### 订单表改造
```sql
ALTER TABLE `fa_shopro_order`
ADD COLUMN `ccb_user_id` varchar(30) DEFAULT NULL COMMENT '建行用户ID',
ADD COLUMN `ccb_pay_flow_id` varchar(50) DEFAULT NULL COMMENT '建行支付流水号',
ADD COLUMN `ccb_sync_status` tinyint(1) DEFAULT '0' COMMENT '建行同步状态',
ADD COLUMN `ccb_sync_time` int(11) DEFAULT NULL COMMENT '建行同步时间';
```
### 支付日志表
```sql
-- fa_ccb_payment_log 表用于记录支付流水
```
### 同步日志表
```sql
-- fa_ccb_sync_log 表用于记录与建行的同步日志
```
## 配置说明
配置文件位置:`/addons/shopro/config/ccblife.php`
关键配置项(从 .env 读取):
- `CCB_SERVICE_ID`: 服务方编号生产YS44000009001853
- `CCB_MERCHANT_ID`: 商户号
- `CCB_POS_ID`: POS 号
- `CCB_BRANCH_ID`: 分行号
- `CCB_PRIVATE_KEY`: RSA 私钥BASE64 格式)
- `CCB_PUBLIC_KEY`: RSA 公钥BASE64 格式)
## 接口调用流程
### 1. 用户登录流程
```mermaid
sequenceDiagram
participant U as 用户
participant CCB as 建行App
participant H5 as H5商城
participant API as 后端API
participant DB as 数据库
U->>CCB: 点击进入商城
CCB->>H5: 跳转URL(携带ccbParamSJ)
H5->>API: 调用 /ccblife/login
API->>API: 解密ccbParamSJ参数
API->>DB: 查询/创建用户
DB-->>API: 返回用户信息
API->>API: 生成Token
API-->>H5: 返回Token和用户信息
H5-->>U: 显示商城首页
```
### 2. 支付流程
```mermaid
sequenceDiagram
participant U as 用户
participant H5 as H5商城
participant API as 后端API
participant CCB as 建行App
participant CCBAPI as 建行API
U->>H5: 提交订单
H5->>API: 创建订单
API-->>H5: 返回订单信息
H5->>API: 请求支付串 /ccbpayment/createPayment
API->>API: 生成支付串和MAC签名
API-->>H5: 返回支付串
H5->>CCB: 调用JSBridge发起支付
CCB->>U: 显示收银台
U->>CCB: 确认支付
CCB->>CCBAPI: 处理支付
CCBAPI-->>CCB: 支付结果
CCB->>H5: 返回支付结果
H5->>API: 回调 /ccbpayment/callback
API->>CCBAPI: 验证支付结果
API->>API: 更新订单状态
API->>CCBAPI: 推送订单信息
API-->>H5: 返回最终结果
```
## 测试步骤
### 1. 环境检查
访问:`http://fengketrade.test/addons/shopro/ccbtest`
检查项:
- 配置检查:`/ccbtest/checkConfig`
- 数据库检查:`/ccbtest/checkDatabase`
### 2. 功能测试
#### 基础功能
- RSA 测试:`/ccbtest/testRsa`
- MD5 测试:`/ccbtest/testMd5`
- URL 解密:`/ccbtest/testUrlDecrypt`
#### 业务功能
- 用户登录:`POST /ccbtest/testUserLogin`
- 订单推送:`/ccbtest/testOrderPush?order_id=1`
- 支付串生成:`/ccbtest/testPaymentString?order_id=1`
### 3. 前端测试
1. 访问示例页面:`http://fengketrade.test/ccblife-demo.html`
2. 测试各项功能:
- 检测运行环境
- 获取用户信息
- 自动登录
- 模拟支付
## 前端集成示例
### 初始化 JSBridge
```javascript
// 在页面加载时初始化
CcbLifeBridge.init({
debug: true,
apiBaseUrl: '/addons/shopro'
});
```
### 获取用户信息
```javascript
CcbLifeBridge.getUserInfo(function(result) {
if (result.success) {
console.log('用户信息:', result.data);
// result.data 包含: ccb_user_id, mobile, nickname, avatar
}
});
```
### 调起支付
```javascript
// 先调用后端生成支付串
fetch('/addons/shopro/ccbpayment/createPayment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'token': localStorage.getItem('ccb_token')
},
body: JSON.stringify({ order_id: orderId })
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
// 调起建行支付
CcbLifeBridge.payment({
payment_string: data.data.payment_string
}, function(result) {
if (result.success) {
// 支付成功,通知后端
notifyPaymentSuccess(orderId);
}
});
}
});
```
## 注意事项
### 1. 安全要求
- 所有敏感配置必须存储在 `.env` 文件中
- RSA 密钥必须是 BASE64 格式(不含 PEM 头尾)
- 生产环境必须启用 HTTPS
### 2. 数据同步
- 订单状态变更必须同步到建行
- 使用 `fa_ccb_sync_log` 表记录所有同步操作
- 支持批量同步未同步的订单
### 3. 错误处理
- 所有 API 调用都有重试机制(默认 3 次)
- 失败的同步任务会记录到日志表
- 支付失败需要明确提示用户
### 4. 兼容性
- iOS通过 URL Scheme 调起支付comccbpay://
- Android通过 window.mbspay 对象调用
- H5在非建行环境降级处理
## 部署检查清单
- [ ] 确认 `.env` 配置正确
- [ ] 数据库表结构已更新
- [ ] RSA 密钥已配置
- [ ] HTTPS 证书已安装
- [ ] 前端 JS 文件已部署
- [ ] 测试接口访问正常
- [ ] 日志目录可写
## 常见问题
### Q: RSA 加密失败
A: 检查密钥格式是否为 BASE64不要包含 PEM 头尾标记
### Q: 用户登录失败
A: 确认 ccbParamSJ 参数正确,服务方编号与配置一致
### Q: 订单推送失败
A: 检查必需的 34 个字段是否都已填充,特别注意日期格式
### Q: 支付无响应
A: 确认在建行 App 内访问,检查 JSBridge 是否就绪
## 技术支持
如有问题,请检查:
1. 系统日志:`runtime/log/ccblife/`
2. 同步日志:`fa_ccb_sync_log`
3. 支付日志:`fa_ccb_payment_log`
---
*最后更新2025-01-17*
*作者Billy*

View File

@ -1,321 +0,0 @@
# 建行生活前端测试摘要
## 📋 测试环境配置完成
**日期**: 2025-01-17
**状态**: ✅ 就绪
## 📁 已创建/修改的文件
### 新建文件 (6个)
1. `/frontend/sheep/platform/provider/ccblife/index.js` (347行)
- 建行生活平台核心模块
- 环境检测、JSBridge、用户信息、支付功能
2. `/frontend/sheep/platform/provider/ccblife/api.js` (69行)
- 建行 API 接口封装
- 登录、支付相关接口
3. `/frontend/pages/ccblife/index.vue` (373行)
- 建行生活专属页面
- 用户信息、专属活动、专享商品展示
4. `/frontend/static/ccb-test.html` (620行)
- 前端集成测试页面
- 环境检测、模块加载、功能测试
5. `/doc/ccblife-uniapp-integration.md`
- uni-app 集成指南文档
6. `/doc/ccblife-frontend-testing-guide.md`
- 完整的测试指南文档
### 修改文件 (3个)
1. `/frontend/sheep/platform/index.js`
- ✅ 添加 ccblife 模块导入
- ✅ 添加建行环境检测
- ✅ 添加 ccb provider 支持
2. `/frontend/sheep/platform/pay.js`
- ✅ 添加建行支付方式
- ✅ 实现 ccbPay() 方法
- ✅ 集成支付流程
3. `/frontend/pages.json`
- ✅ 添加建行页面路由配置
## ✅ 文件验证结果
| 检查项 | 状态 | 说明 |
|--------|------|------|
| JavaScript 语法 | ✅ 通过 | 所有 JS 文件语法正确 |
| JSON 格式 | ✅ 通过 | pages.json 格式正确 |
| 文件完整性 | ✅ 通过 | 所有文件已创建 |
| 代码行数 | ✅ 正常 | 共 516 行核心代码 |
## 🚀 快速开始测试
### 方式 1: 使用测试页面(最简单)
```bash
# 1. 访问后端测试页面
http://fengketrade.test/ccblife-demo.html
# 2. 或访问前端测试页面(需启动前端服务)
http://localhost:3000/static/ccb-test.html
# 3. 模拟建行环境
添加 URL 参数: ?from=ccblife&ccbParamSJ=test
```
### 方式 2: 使用 HBuilderX推荐
```bash
# 1. 打开 HBuilderX
# 2. 文件 → 打开目录 → 选择 frontend
# 3. 运行 → 运行到浏览器 → Chrome
# 4. 访问: http://localhost:8080/#/pages/ccblife/index
```
### 方式 3: 使用命令行(开发者)
```bash
cd frontend
# 安装依赖(如果还没安装)
npm install
# 启动开发服务器(如果项目支持)
npm run dev:h5
# 或
npx vite
```
## 🧪 核心测试点
### 1. 环境检测测试 ⏳
**测试目标**: 验证系统能否正确识别建行生活 App
**测试方法**:
```javascript
// 在浏览器 Console 中执行
console.log(sheep.$platform);
// 预期输出: { name: 'CcbLife', provider: 'ccb', ... }
```
**测试场景**:
- ✅ 普通浏览器 → 应识别为 H5
- ✅ 带 from=ccblife 参数 → 应识别为 CcbLife
- ✅ 带 ccbParamSJ 参数 → 应识别为 CcbLife
### 2. 页面路由测试 ⏳
**测试目标**: 验证建行专属页面可以正常访问
**测试方法**:
```
访问: http://localhost:3000/#/pages/ccblife/index
```
**预期结果**:
- ✅ 页面正常加载
- ✅ 显示"建行生活"标题
- ✅ 导航栏背景色为建行红 (#F51C13)
### 3. 模块导入测试 ⏳
**测试目标**: 验证建行模块正确导入
**测试方法**:
```javascript
// 检查文件是否可访问
fetch('/sheep/platform/provider/ccblife/index.js')
.then(res => console.log('建行模块:', res.ok ? '✓ 存在' : '✗ 不存在'));
```
### 4. API 接口测试 ⏳
**测试目标**: 验证前端能调用后端建行接口
**测试方法**:
```bash
# 方法1: 使用 curl
curl http://fengketrade.test/addons/shopro/ccbtest
# 方法2: 浏览器访问
http://fengketrade.test/addons/shopro/ccbtest/checkConfig
```
**预期结果**:
- ✅ 返回 JSON 数据
- ✅ code 字段为 1成功
### 5. 支付流程测试 ⏳
**测试目标**: 验证建行支付调用流程
**前置条件**: 在建行环境中
**测试方法**:
```javascript
// 模拟支付调用
sheep.$platform.pay('ccb', 'goods', 'ORDER_TEST_001');
```
**预期结果**:
- ✅ 非建行环境: 提示"请在建行生活App内使用"
- ✅ 建行环境: 尝试调用 JSBridge
## 📊 测试覆盖范围
```
核心功能模块
├── 环境检测 ✅ 已实现 ⏳ 待测试
├── 用户信息获取 ✅ 已实现 ⏳ 待测试
├── 自动登录 ✅ 已实现 ⏳ 待测试
├── 支付集成 ✅ 已实现 ⏳ 待测试
├── JSBridge 调用 ✅ 已实现 ⏳ 待测试
└── 专属页面 ✅ 已实现 ⏳ 待测试
平台兼容
├── H5 环境 ✅ 已实现 ⏳ 待测试
├── iOS JSBridge ✅ 已实现 ⏳ 需真机
├── Android 集成 ✅ 已实现 ⏳ 需真机
└── 小程序 ⚠️ 不支持(符合预期)
API 接口
├── 用户登录 ✅ 已实现 ⏳ 待测试
├── 自动登录 ✅ 已实现 ⏳ 待测试
├── 支付串生成 ✅ 已实现 ⏳ 待测试
└── 支付回调 ✅ 已实现 ⏳ 待测试
```
## 🔍 已知限制
1. **真机测试依赖**
- JSBridge 功能需要在真实建行 App 中测试
- 支付功能需要建行生产环境
2. **小程序限制**
- 微信小程序不支持建行支付
- 系统会自动降级处理
3. **开发环境限制**
- 本地无法完整模拟建行 JSBridge
- 可以通过 URL 参数模拟环境判断
## 📝 下一步行动
### 立即可以测试(本地)
- [x] 文件语法检查
- [x] JSON 格式验证
- [ ] 启动开发服务器
- [ ] 访问测试页面
- [ ] 验证环境检测
- [ ] 验证页面路由
- [ ] 测试 API 接口调用
### 需要建行环境(真机)
- [ ] JSBridge 功能测试
- [ ] 获取建行用户信息
- [ ] 自动登录流程
- [ ] 支付功能完整流程
- [ ] URL 参数解密
### 建议测试顺序
1. **第一阶段: 本地基础测试**
```bash
# 1. 访问测试页面
http://localhost:3000/static/ccb-test.html
# 2. 点击"测试模块导入"
# 3. 点击"测试环境检测"
# 4. 点击"测试URL参数解析"
```
2. **第二阶段: 接口联调测试**
```bash
# 1. 测试配置检查
curl http://fengketrade.test/addons/shopro/ccbtest/checkConfig
# 2. 测试数据库检查
curl http://fengketrade.test/addons/shopro/ccbtest/checkDatabase
# 3. 测试 RSA 加密
curl http://fengketrade.test/addons/shopro/ccbtest/testRsa
```
3. **第三阶段: 真机环境测试**
- 在建行生活 App 内打开测试链接
- 验证 JSBridge 功能
- 测试完整的支付流程
## 📚 相关文档
- **集成指南**: `/doc/ccblife-uniapp-integration.md`
- **测试指南**: `/doc/ccblife-frontend-testing-guide.md`
- **实施指南**: `/doc/ccblife-implementation-guide.md`
- **技术方案**: `/fangan.md`
## 💡 测试技巧
### 快速验证模块加载
```javascript
// 在浏览器 Console 中粘贴执行
(() => {
console.group('🏦 建行生活模块验证');
// 1. 平台对象
console.log('平台对象:', window.sheep?.$platform || '未找到');
// 2. 平台名称
console.log('平台名称:', window.sheep?.$platform?.name || '未知');
// 3. 提供商
console.log('提供商:', window.sheep?.$platform?.provider || '未知');
// 4. 是否建行环境
const isCcb = window.sheep?.$platform?.provider === 'ccb';
console.log('建行环境:', isCcb ? '✓ 是' : '✗ 否');
console.groupEnd();
})();
```
### 模拟建行参数
```javascript
// 方式1: 修改 URL刷新后生效
const url = new URL(window.location);
url.searchParams.set('from', 'ccblife');
url.searchParams.set('ccbParamSJ', 'test123');
window.location.href = url.toString();
// 方式2: 直接访问
window.location.href = '?from=ccblife&ccbParamSJ=test123';
```
## ❓ 问题排查
遇到问题?查看**测试指南**的"常见问题排查"部分:
```
/doc/ccblife-frontend-testing-guide.md#常见问题排查
```
---
**测试状态图例**:
- ✅ 已完成
- ⏳ 待测试
- ❌ 失败
- ⚠️ 有限制
*摘要生成时间: 2025-01-17*
*作者: Billy*

View File

@ -1,363 +0,0 @@
# 建行生活 uni-app 前端集成指南
## 概述
本文档介绍了如何在 Shopro uni-app 前端项目中集成建行生活功能,包括用户登录、支付、专属页面等功能。
## 已实现的前端功能
### 1. 平台集成模块
**位置**: `/frontend/sheep/platform/provider/ccblife/`
- `index.js` - 建行生活平台核心模块
- 环境检测检测是否在建行App内
- JSBridge 初始化和管理
- 用户信息获取
- 自动登录
- 支付调起
- 原生方法调用
- `api.js` - API 接口封装
- URL 跳转登录
- 自动登录
- 支付串生成
- 支付回调
### 2. 支付集成
**位置**: `/frontend/sheep/platform/pay.js`
已在现有支付系统中添加了建行支付ccb支持
```javascript
// 支付方式现在支持
payment = ['wechat','alipay','ccb','money','offline']
```
### 3. 平台识别
**位置**: `/frontend/sheep/platform/index.js`
系统会自动识别建行生活环境:
- 平台名称:`CcbLife`
- 提供商:`ccb`
- 平台标识:`ccblife`
### 4. 建行专属页面
**位置**: `/frontend/pages/ccblife/index.vue`
建行生活专属页面,展示:
- 建行用户信息
- 专属优惠活动
- 专享商品
- 建行特色功能
## 使用指南
### 1. 检测建行环境
```javascript
import sheep from '@/sheep';
// 检查是否在建行App内
if (sheep.$platform.provider === 'ccb') {
console.log('在建行生活App内');
}
// 或直接使用平台对象
import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
if (CcbLifePlatform.isInCcbApp) {
console.log('在建行生活App内');
}
```
### 2. 获取建行用户信息
```javascript
import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
// 获取用户信息
CcbLifePlatform.getUserInfo().then(result => {
if (result.code === 0) {
console.log('用户信息:', result.data);
// data包含: ccb_user_id, mobile, nickname, avatar
}
}).catch(error => {
console.error('获取用户信息失败:', error);
});
```
### 3. 自动登录流程
平台会自动处理登录流程,无需手动调用:
1. 用户从建行App进入H5页面
2. 系统自动检测建行环境
3. 自动获取用户信息并登录
4. 保存Token到本地存储
手动触发登录:
```javascript
import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
// 手动触发自动登录
CcbLifePlatform.autoLogin();
```
### 4. 建行支付集成
#### 在订单支付页面
```javascript
// 支付方式选择
const payMethods = [
{
value: 'wechat',
name: '微信支付',
icon: 'wechat'
},
{
value: 'alipay',
name: '支付宝',
icon: 'alipay'
},
{
value: 'ccb',
name: '建行支付',
icon: 'ccb',
// 只在建行App内显示
show: sheep.$platform.provider === 'ccb'
}
];
```
#### 发起支付
```javascript
import sheep from '@/sheep';
// 调起支付
sheep.$platform.pay('ccb', 'goods', orderSN);
```
### 5. 监听事件
```javascript
// 监听登录成功事件
uni.$on('ccb:login:success', (data) => {
console.log('建行用户登录成功', data);
// 更新UI或执行其他操作
});
// 在页面销毁时移除监听
onUnmounted(() => {
uni.$off('ccb:login:success');
});
```
### 6. 条件编译
在需要区分平台的代码中使用条件编译:
```javascript
// #ifdef H5
import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
if (CcbLifePlatform.isInCcbApp) {
// 建行App内的特殊处理
}
// #endif
```
## 页面路由配置
`pages.json` 中添加建行相关页面:
```json
{
"pages": [
{
"path": "pages/ccblife/index",
"style": {
"navigationBarTitleText": "建行生活",
"navigationBarBackgroundColor": "#F51C13",
"navigationBarTextStyle": "white"
}
}
]
}
```
## 测试流程
### 1. 本地测试
```bash
# 进入前端目录
cd frontend
# 安装依赖
npm install
# 运行H5
npm run dev:h5
```
### 2. 模拟建行环境
在浏览器中添加URL参数模拟建行环境
```
http://localhost:3000/#/pages/index/index?from=ccblife&ccbParamSJ=xxx
```
### 3. 真机测试
1. 使用 HBuilderX 打包H5应用
2. 部署到测试服务器
3. 在建行生活App内访问测试地址
## 注意事项
### 1. 平台判断优先级
系统按以下优先级判断平台:
1. 建行生活 (`CcbLife`)
2. 微信公众号 (`WechatOfficialAccount`)
3. 普通H5 (`H5`)
### 2. JSBridge 兼容性
- iOS使用 WebViewJavascriptBridge
- Android使用 window.mbspay 对象
### 3. 支付流程
1. 用户选择建行支付
2. 调用后端生成支付串
3. 通过JSBridge调起建行收银台
4. 支付完成后回调通知后端
5. 更新订单状态
### 4. 错误处理
```javascript
try {
const result = await CcbLifePlatform.payment(options);
// 处理成功
} catch (error) {
if (error.code === -1) {
// 不在建行App内
uni.showToast({
title: '请在建行生活App内使用',
icon: 'none'
});
} else {
// 其他错误
console.error(error);
}
}
```
## 常见问题
### Q: 如何判断是否在建行App内
```javascript
import sheep from '@/sheep';
// 方式1通过平台对象
if (sheep.$platform.provider === 'ccb') {
// 在建行App内
}
// 方式2直接使用建行平台模块
import CcbLifePlatform from '@/sheep/platform/provider/ccblife/index';
if (CcbLifePlatform.isInCcbApp) {
// 在建行App内
}
```
### Q: 自动登录失败怎么办?
1. 检查是否在建行App内
2. 确认后端接口正常
3. 查看控制台错误信息
4. 清除本地缓存重试
```javascript
// 清除缓存
uni.removeStorageSync('token');
uni.removeStorageSync('userInfo');
// 重新触发登录
CcbLifePlatform.autoLogin();
```
### Q: 支付无响应?
1. 确认在建行App内
2. 检查JSBridge是否就绪
3. 验证支付串格式
4. 查看原生日志
```javascript
// 检查JSBridge状态
CcbLifePlatform.ready(() => {
console.log('JSBridge已就绪');
});
```
### Q: 如何调试?
在建行平台模块中开启调试模式:
```javascript
// frontend/sheep/platform/provider/ccblife/index.js
const config = {
debug: true // 开启调试
};
```
## 扩展开发
### 添加新的原生方法调用
```javascript
// 在 CcbLifePlatform 中添加新方法
async customMethod() {
return new Promise((resolve, reject) => {
this.callNative('customMethod', {
// 参数
}, (result) => {
if (result.success) {
resolve(result);
} else {
reject(result);
}
});
});
}
```
### 添加新的API接口
```javascript
// 在 api.js 中添加
newApi: (data) => {
return request({
url: '/ccblife/newApi',
method: 'POST',
data: data
});
}
```
---
*文档版本1.0.0*
*最后更新2025-01-17*
*作者Billy*

View File

@ -1,539 +0,0 @@
# 建行生活H5商城对接 - Bug修复总结
> 修复时间: 2025-01-18
> 审查人员: Billy (Claude Code)
---
## 📊 Bug统计
| 级别 | 数量 | 说明 |
|------|------|------|
| P0 - 致命 | 7 | 导致功能完全无法使用 |
| P1 - 严重 | 0 | 影响核心功能但有workaround |
| P2 - 一般 | 0 | 影响次要功能 |
| **总计** | **7** | - |
---
## 🔴 P0级Bug清单
### Bug #1: MD5签名大小写混用
**文件**: `CcbMD5.php`
**行号**: 未区分
**发现时间**: 初次代码审查
**问题描述**:
代码未区分API接口签名和支付串签名的MD5大小写要求全部使用大写。
**错误代码**:
```php
// 错误API和支付串都用大写
public static function sign($message, $privateKey)
{
return strtoupper(md5($message . $privateKey));
}
```
**根本原因**:
根据建行文档:
- **API接口签名**: MD5签名后转大写
- **支付串签名**: MD5签名后保持小写
**修复方案**:
```php
/**
* API接口签名使用大写MD5
*/
public static function signApiMessage($message, $privateKey)
{
return strtoupper(md5($message . $privateKey));
}
/**
* 支付串签名使用小写MD5
*/
public static function signPaymentString($params, $privateKey)
{
return md5($paymentString); // 保持小写
}
```
**影响范围**:
- ❌ 支付串签名验证失败
- ❌ 建行收银台无法打开
- ❌ 支付回调签名验证失败
**验证方法**:
```bash
# 测试支付串生成
php addons/shopro/test/ccblife_test.php
# 检查输出的MAC签名是否为小写32位字符串
```
---
### Bug #2: 支付串缺少必需参数
**文件**: `CcbPaymentService.php`
**行号**: 原66-86已修复
**发现时间**: 初次代码审查
**问题描述**:
支付串只包含10个参数缺少建行要求的必需参数。
**错误代码**:
```php
// 原代码只有10个参数
$paymentParams = [
'MERCHANTID' => $this->config['merchant_id'],
'POSID' => $this->config['pos_id'],
'ORDERID' => $payFlowId,
'PAYMENT' => $order['total_fee'],
// ... 只有10个参数
];
```
**根本原因**:
未按照建行支付串规范包含所有必需字段,特别是:
- CLIENTIP客户端IP
- PROINFO商品信息
- THIRDAPPINFO第三方应用信息
- USER_ORDERID商户订单号
- TIMEOUT超时时间
**修复方案**:
```php
// 修复后包含18个核心参数
$paymentParams = [
'MERCHANTID' => $this->config['merchant_id'],
'POSID' => $this->config['pos_id'],
'BRANCHID' => $this->config['branch_id'],
'ORDERID' => $payFlowId, // 支付流水号
'PAYMENT' => number_format($order['total_fee'], 2, '.', ''),
'CURCODE' => '01',
'TXCODE' => '520100',
'REMARK1' => '',
'REMARK2' => $this->config['service_id'],
'TYPE' => '1',
'GATEWAY' => '0',
'CLIENTIP' => request()->ip(),
'REGINFO' => '',
'PROINFO' => $this->buildProductInfo($order),
'REFERER' => '',
'THIRDAPPINFO' => 'comccbpay1234567890cloudmerchant',
'USER_ORDERID' => $order['order_sn'], // 商户订单号
'TIMEOUT' => date('YmdHis', strtotime('+30 minutes'))
];
```
**影响范围**:
- ❌ 支付串验证失败
- ❌ 建行收银台拒绝请求
- ❌ 无法完成支付
---
### Bug #3: 支付回调notify()方法空实现
**文件**: `Ccbpayment.php`
**行号**: 原217-230已修复
**发现时间**: 初次代码审查
**问题描述**:
异步通知方法只有TODO注释没有实际业务逻辑。
**错误代码**:
```php
public function notify()
{
// TODO: 处理建行异步通知
echo 'FAIL';
return;
}
```
**根本原因**:
开发时未实现异步通知处理逻辑。
**修复方案**:
```php
public function notify()
{
try {
// 1. 获取原始请求数据
$rawData = file_get_contents('php://input');
Log::info('[建行通知] 收到异步通知: ' . $rawData);
// 2. 解析POST参数
$params = $this->request->post();
if (empty($params) && $rawData) {
parse_str($rawData, $params);
}
// 3. 验证必需参数
if (empty($params['ORDERID'])) {
echo 'FAIL';
return;
}
// 4. 调用支付服务处理通知
$result = $this->paymentService->handleNotify($params);
// 5. 返回结果
echo $result; // 'SUCCESS' 或 'FAIL'
} catch (Exception $e) {
Log::error('[建行通知] 处理失败 error:' . $e->getMessage());
echo 'FAIL';
}
}
```
**影响范围**:
- ❌ 异步通知处理失败
- ❌ 订单状态无法自动更新
- ❌ 建行会持续重发通知
---
### Bug #4: 支付流水号与订单号混淆
**文件**:
- `CcbPaymentService.php` (多处)
- `Ccbpayment.php` (多处)
**行号**: 409, 98, 111, 187-212, 272-283
**发现时间**: 深度代码审查
**问题描述**:
代码将支付流水号pay_flow_id和订单号order_sn混用特别是在
1. 日志记录时用了 `order_sn` 代替 `pay_flow_id`
2. 返回数据时用了 `order_sn` 代替 `pay_flow_id`
3. 回调处理时用 `ORDERID` 查询 `order_sn` 字段
**错误代码**:
```php
// 错误1: 日志记录
Db::name('ccb_payment_log')->insert([
'pay_flow_id' => $order['order_sn'], // ❌ 应该用真实的pay_flow_id
]);
// 错误2: 返回数据
$this->success('支付串生成成功', [
'pay_flow_id' => $result['data']['order_sn'], // ❌ 应该用真实的pay_flow_id
]);
// 错误3: 回调处理
$orderSn = $params['ORDERID'] ?? ''; // ❌ ORDERID是支付流水号不是订单号
$order = Order::where('order_sn', $orderSn)->find(); // ❌ 永远查不到订单
```
**核心概念区分**:
```
支付流水号 (pay_flow_id):
- 格式: PAY + YmdHis + 6位随机数
- 示例: PAY20250118143000123456
- 用途: 建行支付唯一标识
- 对应建行字段: ORDERID
订单号 (order_sn):
- 格式: Shopro系统生成
- 示例: SO20250118001
- 用途: 商城内部订单标识
- 对应建行字段: USER_ORDERID
```
**修复方案**:
```php
// 修复1: 日志记录
'pay_flow_id' => $paymentData['pay_flow_id'] ?? '', // ✅ 使用真实的支付流水号
// 修复2: 返回数据
'pay_flow_id' => $result['data']['pay_flow_id'], // ✅ 返回真实的支付流水号
// 修复3: 回调处理
$payFlowId = $params['ORDERID'] ?? ''; // 支付流水号
$userOrderId = $params['USER_ORDERID'] ?? ''; // 商户订单号
// 优先使用USER_ORDERID查询
if (!empty($userOrderId)) {
$order = Order::where('order_sn', $userOrderId)->find();
} else {
$order = Order::where('ccb_pay_flow_id', $payFlowId)->find();
}
```
**影响范围**:
- ❌ 支付日志记录错误
- ❌ 前端无法获取正确的支付流水号
- ❌ **支付回调100%失败**(订单永远查不到)
- ❌ 异步通知100%失败
- ❌ 对账数据不准确
**严重程度**: 🔥🔥🔥 致命(会导致所有支付失败)
---
### Bug #5: 平台公钥字段不存在
**文件**: `CcbPaymentService.php`
**行号**: 原94-97已修复
**发现时间**: 用户反馈
**问题描述**:
代码尝试在支付串签名中追加 `PLATFORMPUB` 字段,但建行文档中没有这个参数。
**错误代码**:
```php
// 追加平台公钥(如果有)
if (!empty($this->config['platform_public_key'])) {
$signString .= '&PLATFORMPUB=' . $this->config['platform_public_key'];
}
$mac = md5($signString . $this->config['private_key']);
```
**根本原因**:
对建行签名规则理解错误,误以为需要平台公钥参与签名。
**正确规则**:
```
支付串签名 = MD5(参数字符串 + 服务方私钥)
不需要平台公钥!
```
**修复方案**:
```php
// ⚠️ 注意:建行支付串签名规则
// 签名 = MD5(参数字符串 + 服务方私钥)
// 不需要PLATFORMPUB字段直接使用私钥签名
$mac = md5($signString . $this->config['private_key']);
```
**影响范围**:
- ⚠️ 签名算法错误如果配置了platform_public_key
- ⚠️ 可能导致签名验证失败
---
### Bug #6: 订单状态更新字段错误
**文件**: `CcbPaymentService.php`
**行号**: 原363-374已修复
**发现时间**: 数据库Schema审查
**问题描述**:
更新订单支付状态时使用了错误的字段名和数据类型。
**错误代码**:
```php
Order::where('id', $order['id'])->update([
'status' => 'paid',
'pay_type' => 'ccb', // ❌ 枚举中没有'ccb'
'paytime' => time(), // ❌ 字段名错误应为paid_time
'transaction_id' => $params['ORDERID'] ?? '',
]);
```
**根本原因**:
1. Shopro订单表使用 `paid_time` 字段,不是 `paytime`
2. `paid_time` 是bigint(16)毫秒时间戳,不是秒级
3. `pay_type` 枚举值中没有 `'ccb'` 选项
**数据库Schema**:
```sql
-- Shopro订单表字段
`paid_time` bigint(16) NULL DEFAULT NULL COMMENT '支付成功时间', -- 毫秒时间戳
`pay_type` enum('wechat','alipay','money','score','offline') NULL DEFAULT NULL
```
**修复方案**:
```php
Order::where('id', $order['id'])->update([
'status' => 'paid',
'pay_type' => 'offline', // ✅ 建行支付归类为线下银行支付
'paid_time' => time() * 1000, // ✅ 毫秒时间戳
'transaction_id' => $params['ORDERID'] ?? '',
]);
```
**影响范围**:
- ❌ 订单状态更新SQL执行失败
- ❌ 支付时间无法记录
- ❌ 订单列表显示异常
---
### Bug #7: 订单字段映射错误
**文件**: `CcbOrderService.php`
**行号**: 原276-331已在之前修复
**发现时间**: 初次代码审查
**问题描述**:
订单同步到建行时使用了错误的Shopro字段名。
**错误代码**:
```php
return [
'PAY_AMT' => $order['pay_amount'], // ❌ Shopro没有这个字段
'PAY_TIME' => date('YmdHis', $order['paytime']), // ❌ 字段名错误
];
```
**正确字段映射**:
| Shopro字段 | 建行字段 | 说明 |
|------------|----------|------|
| total_fee | PAY_AMT | 实际支付金额 |
| paid_time | PAY_TIME | 支付时间需除以1000 |
| discount_fee | - | 优惠金额 |
| aftersale_status | REFUND_STATUS | 退款状态 |
**修复方案**:
```php
// 计算金额Shopro使用total_fee作为实际支付
$payAmount = number_format($order['total_fee'] ?? 0, 2, '.', '');
// 处理支付时间Shopro paid_time是毫秒时间戳
$payTime = '';
if (!empty($order['paid_time'])) {
$payTime = date('YmdHis', intval($order['paid_time'] / 1000)); // ✅ 除以1000转为秒
}
return [
'PAY_AMT' => $payAmount,
'PAY_TIME' => $payTime,
'REFUND_STATUS' => $this->mapRefundStatus($order['aftersale_status'] ?? 0),
];
```
**影响范围**:
- ❌ 订单同步到建行失败
- ❌ 建行后台订单数据错误
- ❌ 对账金额不一致
---
## 📈 修复效果
### 修复前
- ❌ 支付串签名错误
- ❌ 支付回调100%失败
- ❌ 异步通知无法处理
- ❌ 订单状态无法更新
- ❌ 订单同步失败
### 修复后
- ✅ 支付串生成正确
- ✅ 支付回调正确处理
- ✅ 异步通知正确处理
- ✅ 订单状态正确更新
- ✅ 订单同步正确执行
- ✅ 所有自动化测试通过
---
## 🧪 验证方法
### 1. 运行自动化测试
```bash
cd /Users/billy/Code/fengketrade.com
php addons/shopro/test/ccblife_test.php
```
**预期结果**:
```
========================================
总计: 10 项测试
通过: 10 项
失败: 0 项
========================================
🎉 所有测试通过!系统运行正常。
```
### 2. 手动验证关键点
**验证支付流水号格式**:
```sql
SELECT
order_sn,
ccb_pay_flow_id,
LENGTH(ccb_pay_flow_id) as length,
SUBSTRING(ccb_pay_flow_id, 1, 3) as prefix
FROM fa_shopro_order
WHERE ccb_pay_flow_id IS NOT NULL;
-- 预期:
-- length = 23
-- prefix = 'PAY'
```
**验证订单状态更新**:
```sql
SELECT
order_sn,
status,
pay_type,
paid_time,
FROM_UNIXTIME(paid_time/1000) as paid_datetime,
transaction_id
FROM fa_shopro_order
WHERE status = 'paid' AND pay_type = 'offline';
-- 预期:
-- status = 'paid'
-- pay_type = 'offline'
-- paid_time > 0 (毫秒时间戳)
-- transaction_id = ccb_pay_flow_id
```
**验证支付日志**:
```sql
SELECT
order_sn,
pay_flow_id,
LENGTH(pay_flow_id) as length,
status,
amount
FROM fa_ccb_payment_log
ORDER BY id DESC
LIMIT 10;
-- 预期:
-- pay_flow_id 不等于 order_sn
-- length = 23
-- pay_flow_id 格式: PAY开头
```
---
## 📝 总结
本次代码审查发现并修复了**7个P0级致命bug**这些bug会导致
1. **支付功能完全无法使用**Bug #2, #4
2. **订单状态无法更新**Bug #6, #7
3. **回调和通知100%失败**Bug #3, #4
4. **签名验证失败**Bug #1, #5
修复后,系统核心逻辑已完全正确,可以进行模拟测试。
**下一步**:
1. ✅ 运行自动化测试脚本验证修复
2. ⏸️ 等待建行提供测试环境配置
3. 🔄 进行真实环境联调测试
---
## 附录:修改文件清单
| 文件 | 修改行数 | 修改类型 | 影响 |
|------|----------|----------|------|
| CcbMD5.php | ~50 | 重构 | 区分API和支付串签名 |
| CcbPaymentService.php | ~200 | 重大修复 | 修复支付串生成、回调处理、状态更新 |
| CcbOrderService.php | ~50 | 修复 | 修复字段映射 |
| Ccbpayment.php | ~100 | 补充实现 | 实现notify()方法 |
| CcbPaymentLog.php | +150 | 新建 | 创建支付日志模型 |
| CcbSyncLog.php | +150 | 新建 | 创建同步日志模型 |
| ccblife_test.php | +600 | 新建 | 创建自动化测试脚本 |
| ccblife_test_guide.md | +800 | 新建 | 创建测试指南文档 |
**总修改**: ~2100行代码
---
**文档结束**

View File

@ -1,744 +0,0 @@
# 建行生活H5商城对接 - 测试指南
> 文档版本: v1.0
> 更新时间: 2025-01-18
> 作者: Billy
---
## 📋 目录
1. [测试概述](#测试概述)
2. [测试环境准备](#测试环境准备)
3. [自动化测试流程](#自动化测试流程)
4. [手动测试流程](#手动测试流程)
5. [常见问题排查](#常见问题排查)
6. [测试检查清单](#测试检查清单)
---
## 测试概述
本测试方案分为两个阶段:
### 🤖 阶段一:模拟测试(当前可用)
**目的**: 在没有建行加密数据的情况下,验证系统核心逻辑是否正确
**范围**:
- ✅ 环境配置检查
- ✅ 数据库表结构验证
- ✅ 用户和订单创建
- ✅ 支付串生成逻辑
- ✅ 支付流水号规则
- ✅ 订单状态更新逻辑
- ✅ 字段映射正确性
**不包含**:
- ❌ 真实RSA加密/解密
- ❌ 真实建行API调用
- ❌ 建行回调签名验证
- ❌ JSBridge通信
### 🌐 阶段二:真实环境测试(需建行配置)
**前提**: 已获得建行提供的测试/生产环境配置
**范围**:
- ✅ 完整RSA加密流程
- ✅ 建行API接口调用
- ✅ 订单同步到建行
- ✅ 建行收银台支付
- ✅ 支付回调验证
- ✅ 异步通知验证
---
## 测试环境准备
### 1. 系统要求
```bash
# 检查PHP版本需要 >= 7.0
php -v
# 检查必需扩展
php -m | grep -E 'openssl|pdo|json|mbstring'
# 检查数据库连接
mysql -h <host> -u <user> -p<password> -e "SELECT VERSION();"
```
### 2. 配置文件检查
确保已创建配置文件:`/application/extra/ccblife.php`
```php
<?php
return [
// 基础配置
'merchant_id' => '***', // 商户代码
'pos_id' => '***', // 柜台代码
'branch_id' => '***', // 分行代码
'service_id' => '***', // 服务方编号
// 密钥配置
'private_key' => '***', // 服务方私钥1024位
'merchant_public_key' => '***', // 商户公钥1024位
// API地址测试环境
'api_url' => 'http://test.ccb.com/api',
'cashier_url' => 'comccbpay://pay',
// 回调地址
'callback_url' => 'https://your-domain.com/addons/shopro/ccbpayment/callback',
'notify_url' => 'https://your-domain.com/addons/shopro/ccbpayment/notify',
];
```
### 3. 数据库表检查
确保已执行以下SQL如未执行
```sql
-- 订单表添加建行支付流水号字段
ALTER TABLE `fa_shopro_order`
ADD COLUMN `ccb_pay_flow_id` VARCHAR(50) NULL DEFAULT NULL COMMENT '建行支付流水号'
AFTER `order_sn`;
-- 用户表添加建行用户ID字段
ALTER TABLE `fa_user`
ADD COLUMN `ccb_user_id` VARCHAR(50) NULL DEFAULT NULL COMMENT '建行用户ID'
AFTER `mobile`;
-- 支付日志表已在install.sql中
-- fa_ccb_payment_log
-- 同步日志表已在install.sql中
-- fa_ccb_sync_log
```
---
## 自动化测试流程
### 运行自动化测试脚本
```bash
cd /Users/billy/Code/fengketrade.com
# 运行完整测试套件
php addons/shopro/test/ccblife_test.php
```
### 预期输出示例
```
========================================
建行生活H5商城对接 - 自动化测试
========================================
测试时间: 2025-01-18 14:30:00
========================================
【环境检查】
✓ PHP版本检查 (7.4.33 >= 7.0)
✓ 检查OpenSSL扩展
✓ 检查PDO扩展
✓ 检查JSON扩展
✓ 检查文件: CcbPaymentService.php
✓ 检查文件: CcbOrderService.php
✓ 检查文件: CcbRSA.php
✓ 检查文件: CcbMD5.php
✓ 检查文件: CcbEncryption.php
【配置文件检查】
✓ 配置文件存在
✓ 商户ID配置
✓ POS ID配置
✓ 分行代码配置
✓ 服务方编号配置
✓ 服务方私钥配置
✓ 商户公钥配置
【数据库表检查】
✓ 检查表: fa_ccb_payment_log
✓ 检查表: fa_ccb_sync_log
✓ 检查表: fa_shopro_order
✓ 检查表: fa_user
✓ 检查订单表ccb_pay_flow_id字段
【创建测试用户】
✓ 用户创建成功 (ID: 123)
【创建测试订单】
✓ 订单创建成功 (ID: 456, SN: SO20250118143000001)
【支付串生成测试】
✓ 支付串生成状态
✓ 支付串不为空
✓ 支付流水号不为空
✓ MAC签名不为空
✓ 支付流水号长度正确 (23位)
✓ 支付流水号前缀正确 (PAY)
✓ 订单表支付流水号已更新
✓ 支付日志已记录
支付串长度: 512 字节
支付流水号: PAY20250118143000123456
MAC签名: a1b2c3d4e5f6...
【支付回调测试】
✓ 回调处理成功
✓ 回调消息正确
✓ 订单状态已更新为已支付
✓ 支付方式正确 (offline代表建行)
✓ 支付时间已记录
✓ 交易单号正确
订单状态: paid
支付时间: 2025-01-18 14:30:05
【异步通知测试】
通知处理结果: fail
✓ 通知处理逻辑测试完成
【订单同步测试】
✓ 订单字段 ORD_NUM 存在
✓ 订单字段 PAY_AMT 存在
✓ 订单字段 ORD_TIME 存在
✓ 订单字段 PAY_TIME 存在
✓ 订单字段 ORD_STATUS 存在
✓ 订单字段 REFUND_STATUS 存在
✓ 订单号正确
✓ 支付金额正确
订单号: SO20250118143000001
支付金额: 100.00
订单状态: 02
【清理测试数据】
✓ 删除测试订单 (ID: 456)
✓ 删除支付日志
✓ 删除测试用户 (ID: 123)
========================================
测试报告
========================================
✓ 通过 环境检查
所有环境检查通过
✓ 通过 配置文件检查
所有配置项完整
✓ 通过 数据库表检查
所有表结构完整
✓ 通过 创建测试用户
用户ID: 123
✓ 通过 创建测试订单
订单ID: 456
✓ 通过 支付串生成测试
支付流水号: PAY20250118143000123456
✓ 通过 支付回调测试
支付回调处理成功
✓ 通过 异步通知测试
通知处理逻辑测试完成
✓ 通过 订单同步测试
订单数据构建正确
✓ 通过 清理测试数据
所有测试数据已清理
========================================
总计: 10 项测试
通过: 10 项
失败: 0 项
耗时: 1.23 秒
========================================
🎉 所有测试通过!系统运行正常。
```
---
## 手动测试流程
### 测试1: 前端环境检测
**目的**: 验证建行生活APP环境识别
**步骤**:
1. 在建行生活APP内打开H5商城链接待建行提供测试链接
2. 打开浏览器控制台
3. 查看输出:
```javascript
// 预期输出
[CcbLife] 初始化完成, 是否在建行App内: true
[CcbLife] JSBridge 已就绪
```
**验证点**:
- `isInCcbApp` 应为 `true`
- `isReady` 应为 `true`
- User-Agent 包含 'ccblife' 或 'ccb'
---
### 测试2: 用户自动登录
**目的**: 验证建行用户自动登录流程
**前提**:
- 测试1通过
- 已在建行APP内打开H5商城
**步骤**:
1. 清除商城登录态:`localStorage.clear()`
2. 刷新页面
3. 观察控制台输出
**预期输出**:
```
[CcbLife] 自动登录成功
```
**验证点**:
- `localStorage.getItem('token')` 不为空
- `localStorage.getItem('userInfo')` 包含用户信息
- 数据库 `fa_user` 表中 `ccb_user_id` 已绑定
---
### 测试3: 创建订单
**目的**: 验证订单创建流程
**步骤**:
1. 选择商品加入购物车
2. 进入结算页
3. 填写收货地址
4. 提交订单
**验证点**:
- 订单创建成功返回订单ID和订单号
- 数据库 `fa_shopro_order` 表中订单状态为 `unpaid`
- `ccb_pay_flow_id` 字段为空
---
### 测试4: 生成支付串
**目的**: 验证支付串生成逻辑
**接口**: `POST /addons/shopro/ccbpayment/pay`
**请求参数**:
```json
{
"order_id": 123
}
```
**预期响应**:
```json
{
"code": 1,
"msg": "支付串生成成功",
"data": {
"payment_string": "MERCHANTID=...&POSID=...&MAC=...",
"payment_url": "comccbpay://pay?MERCHANTID=...&MAC=...",
"mac": "a1b2c3d4e5f6...",
"order_id": 123,
"order_sn": "SO20250118001",
"pay_flow_id": "PAY20250118143000123456",
"amount": "100.00"
}
}
```
**验证点**:
1. `payment_string` 包含所有必需参数
2. `pay_flow_id` 格式正确PAY + 14位时间戳 + 6位随机数
3. `mac` 签名不为空
4. 数据库订单表 `ccb_pay_flow_id` 已更新
5. `fa_ccb_payment_log` 表中已记录支付请求
**关键参数检查**:
```javascript
// 从 payment_string 中提取参数
const params = new URLSearchParams(payment_string);
console.log('ORDERID:', params.get('ORDERID')); // 应等于 pay_flow_id
console.log('USER_ORDERID:', params.get('USER_ORDERID')); // 应等于 order_sn
console.log('PAYMENT:', params.get('PAYMENT')); // 应等于订单实际支付金额
console.log('MAC:', params.get('MAC')); // MD5签名
console.log('PLATFORMID:', params.get('PLATFORMID')); // 服务方编号
```
---
### 测试5: 调起支付(需建行环境)
**目的**: 验证JSBridge调起建行收银台
**前提**:
- 测试1-4全部通过
- 已在建行APP内
**步骤**:
1. 点击"去支付"按钮
2. 前端调用 `CcbLifePlatform.payment()`
**前端代码示例**:
```javascript
// sheep/platform/provider/ccblife/index.js
const paymentResult = await CcbLifePlatform.payment({
payment_string: '支付串内容'
});
console.log('支付结果:', paymentResult);
```
**iOS预期行为**:
- 跳转到 `comccbpay://pay?支付参数`
- 打开建行收银台
**Android预期行为**:
- 调用 `window.mbspay.payment()`
- 打开建行收银台
**验证点**:
- 收银台显示正确的订单金额
- 收银台显示正确的商品信息
---
### 测试6: 支付回调(需建行环境)
**目的**: 验证支付完成后的同步回调
**触发方式**:
- 在建行收银台完成支付(或取消支付)
- 建行会重定向到 `callback_url`
**回调URL示例**:
```
https://your-domain.com/addons/shopro/ccbpayment/callback?ccbParamSJ=加密参数
```
**预期流程**:
1. 解密 `ccbParamSJ` 参数
2. 获取支付结果参数
3. 根据 `SUCCESS=Y/N` 判断支付成功或失败
4. 更新订单状态
**支付成功验证点**:
- 订单状态 → `paid`
- 支付时间 `paid_time` 已记录(毫秒时间戳)
- 支付方式 `pay_type``offline`
- 交易单号 `transaction_id` → 支付流水号
- 页面跳转到订单详情或支付成功页
**支付失败验证点**:
- 订单状态保持 `unpaid`
- 页面显示失败原因
---
### 测试7: 异步通知(需建行环境)
**目的**: 验证建行异步通知处理
**触发方式**:
- 支付成功后建行会POST请求到 `notify_url`
**接口**: `POST /addons/shopro/ccbpayment/notify`
**建行会发送的参数**(示例):
```
POST数据:
ORDERID=PAY20250118143000123456
&USER_ORDERID=SO20250118001
&POSID=100001
&PAYMENT=100.00
&SUCCESS=Y
&SIGN=签名值
```
**预期处理逻辑**:
1. 验证签名 `SIGN`
2. 验证 `POSID` 是否匹配
3. 根据 `USER_ORDERID``ccb_pay_flow_id` 查询订单
4. 更新订单状态
5. 返回 `SUCCESS``FAIL`
**验证点**:
- 响应体必须返回字符串 `SUCCESS``FAIL`
- 订单状态正确更新
- 日志 `runtime/log/` 中记录通知详情
**测试异步通知的方法**模拟建行POST请求:
```bash
curl -X POST 'https://your-domain.com/addons/shopro/ccbpayment/notify' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'ORDERID=PAY20250118143000123456&USER_ORDERID=SO20250118001&POSID=100001&PAYMENT=100.00&SUCCESS=Y&SIGN=mock_sign'
```
---
### 测试8: 订单同步到建行(需建行环境)
**目的**: 验证支付成功后订单推送到建行
**触发时机**:
- 支付回调成功后自动触发
- 或手动调用同步接口
**手动触发方式**:
```php
$orderService = new \addons\shopro\library\ccblife\CcbOrderService();
$result = $orderService->pushOrder($orderId);
var_dump($result);
```
**验证点**:
1. API请求成功HTTP 200
2. 返回 `CLD_HEAD.ERR_CODE === '0'`
3. `fa_ccb_sync_log` 表中记录同步成功
4. 订单同步状态更新
**失败处理**:
- 如果同步失败,日志中记录错误原因
- 支持重试机制(通过定时任务)
---
## 常见问题排查
### 问题1: 支付串生成失败
**错误信息**: "订单状态不正确"
**原因**: 订单已支付或已关闭
**解决**:
```sql
-- 检查订单状态
SELECT id, order_sn, status, pay_status FROM fa_shopro_order WHERE id = <order_id>;
-- 如需重测,重置订单状态
UPDATE fa_shopro_order SET status = 'unpaid', pay_status = 'unpaid' WHERE id = <order_id>;
```
---
### 问题2: 支付串签名错误
**错误信息**: "签名验证失败"
**排查步骤**:
1. 检查配置文件中私钥是否正确
2. 检查参数是否按ASCII排序
3. 检查MD5签名是否为小写
**验证签名算法**:
```php
// 测试代码
$params = [/* 支付参数 */];
ksort($params);
$signString = http_build_query($params);
$mac = md5($signString . config('ccblife.private_key'));
echo "签名字符串: {$signString}\n";
echo "MAC签名: {$mac}\n";
```
---
### 问题3: 订单查询失败
**错误信息**: "订单不存在"
**原因**:
- 使用了错误的订单号字段
- ORDERID支付流水号和 USER_ORDERID商户订单号混淆
**排查**:
```sql
-- 检查订单数据
SELECT
id,
order_sn, -- 商户订单号
ccb_pay_flow_id, -- 建行支付流水号
status
FROM fa_shopro_order
WHERE order_sn = 'SO20250118001' -- 使用商户订单号查询
OR ccb_pay_flow_id = 'PAY20250118143000123456'; -- 使用支付流水号查询
```
---
### 问题4: 订单状态未更新
**可能原因**:
1. 字段名错误(`paytime` vs `paid_time`
2. 时间戳单位错误(秒 vs 毫秒)
3. `pay_type` 枚举值不存在
**验证**:
```sql
-- 检查订单表字段
SHOW COLUMNS FROM fa_shopro_order LIKE 'paid_time';
-- 检查pay_type枚举值
SHOW COLUMNS FROM fa_shopro_order LIKE 'pay_type';
-- 查看订单更新时间
SELECT
id,
paid_time, -- 应为毫秒时间戳
FROM_UNIXTIME(paid_time/1000) as paid_datetime,
pay_type, -- 应为 'offline'
status
FROM fa_shopro_order
WHERE id = <order_id>;
```
---
### 问题5: RSA加密失败
**错误信息**: "RSA加密失败" 或 "key size too small"
**原因**:
- 密钥格式不正确
- 密钥长度不是1024位
- 数据块大小超过117字节
**验证密钥**:
```bash
# 检查私钥
echo "<私钥内容>" | openssl rsa -text -noout
# 检查公钥
echo "<公钥内容>" | openssl rsa -pubin -text -noout
# 查看密钥长度
# 应显示: Private-Key: (1024 bit)
```
**修复**:
1. 确保密钥包含完整的 `-----BEGIN``-----END` 标记
2. 数据分块处理每块不超过117字节
3. 使用正确的填充模式 `OPENSSL_PKCS1_PADDING`
---
## 测试检查清单
### ✅ 阶段一:模拟测试(可立即执行)
- [ ] 运行自动化测试脚本,所有测试通过
- [ ] 配置文件已正确配置
- [ ] 数据库表结构完整
- [ ] 订单表 `ccb_pay_flow_id` 字段存在
- [ ] 用户表 `ccb_user_id` 字段存在
- [ ] 支付日志表 `fa_ccb_payment_log` 存在
- [ ] 同步日志表 `fa_ccb_sync_log` 存在
- [ ] 支付串生成逻辑正确
- [ ] 支付流水号格式正确PAY+14位+6位
- [ ] 订单字段映射正确paid_time、total_fee等
### ✅ 阶段二:真实环境测试(需建行配置)
- [ ] 已获得建行测试环境配置
- [ ] RSA密钥对配置正确1024位
- [ ] API地址配置正确
- [ ] 回调地址已配置且可访问
- [ ] 在建行APP内能正确识别环境
- [ ] JSBridge初始化成功
- [ ] 用户自动登录成功
- [ ] ccb_user_id正确绑定
- [ ] 支付串加密正确ENCPUB字段
- [ ] 建行收银台能正确打开
- [ ] 支付回调能正确接收和处理
- [ ] 异步通知能正确接收和处理
- [ ] 签名验证通过
- [ ] 订单状态正确更新
- [ ] 订单同步到建行成功
- [ ] 重复通知幂等性处理正确
- [ ] 小额支付测试通过0.01元)
- [ ] 正常金额支付测试通过
- [ ] 支付取消测试通过
- [ ] 支付超时测试通过
---
## 附录
### A. 支付流水号规则
```
格式: PAY + YmdHis(14位) + 随机数(6位)
示例: PAY20250118143000123456
长度: 23位固定长度
组成:
- PAY: 固定前缀(3位)
- 20250118143000: 时间戳 yyMMddHHmmss (14位)
- 123456: 随机数 (6位)
```
### B. 重要字段映射表
| Shopro字段 | 建行字段 | 说明 | 示例 |
|------------|----------|------|------|
| order_sn | USER_ORDERID | 商户订单号 | SO20250118001 |
| ccb_pay_flow_id | ORDERID | 建行支付流水号 | PAY20250118143000123456 |
| total_fee | PAYMENT / PAY_AMT | 实际支付金额 | 100.00 |
| discount_fee | - | 优惠金额 | 5.00 |
| paid_time | PAY_TIME | 支付时间(毫秒) | 1737183000000 |
| createtime | ORD_TIME | 订单创建时间(秒) | 20250118143000 |
| status | ORD_STATUS | 订单状态 | paid → 02 |
| pay_type | - | 支付方式 | offline代表建行 |
| transaction_id | ORDERID | 交易单号 | PAY20250118143000123456 |
### C. 订单状态映射
| Shopro状态 | 建行状态码 | 说明 |
|-----------|-----------|------|
| unpaid | 01 | 未支付 |
| paid | 02 | 已支付 |
| closed | 03 | 已关闭 |
| cancelled | 04 | 已取消 |
### D. 日志文件位置
```
建行支付日志:
- runtime/log/202501/18.log
关键日志标识:
- [建行支付] 支付串生成
- [建行支付] 生成支付串失败
- [建行回调] 收到同步回调
- [建行通知] 收到异步通知
- [建行订单同步] 推送订单
```
---
## 总结
本测试指南提供了完整的测试方案,分为两个阶段:
1. **模拟测试阶段**(当前可用)
- 运行自动化测试脚本即可验证核心逻辑
- 无需建行真实配置
- 适合开发阶段自测
2. **真实环境测试阶段**(需建行配置)
- 需要建行提供测试环境
- 包含完整的支付流程
- 适合联调和上线前测试
**下一步行动**:
1. ✅ 立即运行自动化测试脚本
2. ⏸️ 等待建行提供测试环境配置
3. 🔄 获得配置后执行阶段二测试
**如有问题,请查看"常见问题排查"章节或联系开发人员。**

View File

@ -1,579 +0,0 @@
# Shopro 前端项目部署指南
## 概述
本文档描述如何将 Shopro uni-app 前端项目打包并部署到生产环境 `https://app.fengketrade.com`
## 环境配置
### 生产环境配置已完成 ✅
`.env` 文件已配置:
```env
# 正式环境接口域名
SHOPRO_BASE_URL = https://app.fengketrade.com
# 开发环境接口域名
SHOPRO_DEV_BASE_URL = https://app.fengketrade.com
# 开发环境运行端口
SHOPRO_DEV_PORT = 3000
# 接口地址前缀
SHOPRO_API_PATH = /addons/shopro/
```
## 打包方法
### 方法一:使用 HBuilderX 打包(推荐)
这是 uni-app 官方推荐的打包方式,最稳定可靠。
#### 步骤 1: 准备 HBuilderX
1. **下载 HBuilderX**
- 官网https://www.dcloud.io/hbuilderx.html
- 选择"正式版" → "标准版"即可
2. **安装必要插件**
- 启动 HBuilderX
- 工具 → 插件安装 → 搜索"uni-app编译"
- 安装"uni-app (Vue3)"编译器
#### 步骤 2: 打开项目
1. 文件 → 打开目录
2. 选择:`/Users/billy/Code/fengketrade.com/frontend`
3. 等待项目加载完成
#### 步骤 3: H5 打包
1. **发行到 H5**
- 发行 → 网站-H5手机版
- 或者点击菜单:发行 → 网站-PC Web或App适用于宽屏应用
2. **配置打包选项**
```
网站标题:风刻商城
网站域名https://app.fengketrade.com
路由模式hash推荐或 history
```
3. **点击"发行"**
- 等待编译完成
- 控制台会显示编译进度
4. **查看打包结果**
```
打包目录:/Users/billy/Code/fengketrade.com/frontend/unpackage/dist/build/h5
```
#### 步骤 4: 部署到服务器
```bash
cd /Users/billy/Code/fengketrade.com/frontend
# 压缩打包文件(排除 macOS 元数据)
tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \
-czf h5-dist.tar.gz -C unpackage/dist/build/h5 .
# 上传到服务器
scp h5-dist.tar.gz user@app.fengketrade.com:/tmp/
# 登录服务器
ssh user@app.fengketrade.com
# 解压到 Web 目录
cd /path/to/web/root
mkdir -p h5
tar -xzf /tmp/h5-dist.tar.gz -C h5/
# 设置权限
chown -R www-data:www-data h5/
chmod -R 755 h5/
```
### 方法二:使用命令行打包
如果已安装 uni-app CLI 工具,可以使用命令行打包。
#### 步骤 1: 安装 uni-app CLI
```bash
cd /Users/billy/Code/fengketrade.com/frontend
# 安装 uni-app 编译器(如果还没安装)
npm install -g @dcloudio/uvm
uvm
# 或直接安装依赖
npm install
```
#### 步骤 2: 添加构建脚本
编辑 `package.json`,添加以下脚本:
```json
{
"scripts": {
"dev:h5": "uni -p h5",
"build:h5": "uni build -p h5",
"build:h5:prod": "cross-env NODE_ENV=production uni build -p h5",
"prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\""
}
}
```
#### 步骤 3: 执行打包
```bash
# 开发环境打包
npm run build:h5
# 生产环境打包(压缩优化)
npm run build:h5:prod
```
#### 步骤 4: 查看打包结果
```bash
ls -lh unpackage/dist/build/h5/
```
### 方法三:快速部署脚本(自动化)
创建自动化部署脚本。
#### 创建部署脚本
```bash
cat > deploy-frontend.sh << 'EOF'
#!/bin/bash
# 前端自动化部署脚本
# 作者: Billy
# 日期: 2025-01-17
set -e
echo "=== 前端部署脚本 ==="
# 配置
PROJECT_DIR="/Users/billy/Code/fengketrade.com/frontend"
BUILD_DIR="$PROJECT_DIR/unpackage/dist/build/h5"
SERVER_USER="your_user"
SERVER_HOST="app.fengketrade.com"
SERVER_PATH="/var/www/html/h5"
BACKUP_DIR="/var/www/backup"
# 颜色输出
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${YELLOW}步骤 1: 检查环境...${NC}"
cd "$PROJECT_DIR"
if [ ! -d "node_modules" ]; then
echo -e "${YELLOW}安装依赖...${NC}"
npm install
fi
echo -e "${GREEN}✓ 环境检查完成${NC}"
echo -e "${YELLOW}步骤 2: 清理旧文件...${NC}"
rm -rf unpackage/dist/build/h5
echo -e "${YELLOW}步骤 3: 开始打包...${NC}"
# 如果有 HBuilderX CLI
if command -v cli &> /dev/null; then
cli publish --platform h5 --project "$PROJECT_DIR"
# 或使用 npm
elif [ -f "package.json" ]; then
npm run build:h5:prod || npm run build:h5
else
echo -e "${RED}✗ 找不到构建工具,请使用 HBuilderX 手动打包${NC}"
exit 1
fi
echo -e "${GREEN}✓ 打包完成${NC}"
echo -e "${YELLOW}步骤 4: 压缩文件...${NC}"
cd "$PROJECT_DIR"
tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \
-czf h5-dist-$(date +%Y%m%d-%H%M%S).tar.gz -C "$BUILD_DIR" .
ARCHIVE_NAME=$(ls -t h5-dist-*.tar.gz | head -1)
echo -e "${GREEN}✓ 压缩完成: $ARCHIVE_NAME${NC}"
echo -e "${YELLOW}步骤 5: 上传到服务器...${NC}"
scp "$ARCHIVE_NAME" "$SERVER_USER@$SERVER_HOST:/tmp/"
echo -e "${YELLOW}步骤 6: 部署到服务器...${NC}"
ssh "$SERVER_USER@$SERVER_HOST" << ENDSSH
set -e
# 创建备份
if [ -d "$SERVER_PATH" ]; then
echo "备份当前版本..."
sudo mkdir -p "$BACKUP_DIR"
sudo tar -czf "$BACKUP_DIR/h5-backup-\$(date +%Y%m%d-%H%M%S).tar.gz" \
-C "$SERVER_PATH" . || true
fi
# 解压新版本
echo "部署新版本..."
sudo mkdir -p "$SERVER_PATH"
sudo tar -xzf "/tmp/$ARCHIVE_NAME" -C "$SERVER_PATH"
# 设置权限
sudo chown -R www-data:www-data "$SERVER_PATH"
sudo chmod -R 755 "$SERVER_PATH"
# 清理临时文件
rm -f "/tmp/$ARCHIVE_NAME"
echo "部署完成!"
ENDSSH
echo -e "${GREEN}=== 部署成功!===${NC}"
echo -e "${GREEN}访问地址: https://app.fengketrade.com/h5${NC}"
# 清理本地临时文件
rm -f "$ARCHIVE_NAME"
EOF
chmod +x deploy-frontend.sh
```
#### 使用部署脚本
```bash
# 修改脚本中的服务器配置
vim deploy-frontend.sh
# 执行部署
./deploy-frontend.sh
```
## 部署后配置
### Nginx 配置
#### H5 项目配置
```nginx
# /etc/nginx/sites-available/app.fengketrade.com
server {
listen 80;
listen 443 ssl http2;
server_name app.fengketrade.com;
# SSL 证书配置
ssl_certificate /path/to/ssl/cert.pem;
ssl_certificate_key /path/to/ssl/key.pem;
# 强制 HTTPS
if ($scheme != "https") {
return 301 https://$server_name$request_uri;
}
# H5 前端
location /h5 {
alias /var/www/html/h5;
index index.html;
# 解决 Vue Router history 模式 404
try_files $uri $uri/ /h5/index.html;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# HTML 不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
# API 代理到 PHP 后端
location /addons/shopro {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 建行测试页面
location /ccblife-demo.html {
alias /var/www/html/public/ccblife-demo.html;
}
# 静态资源
location /static {
alias /var/www/html/public;
}
}
```
#### 重启 Nginx
```bash
# 测试配置
sudo nginx -t
# 重启服务
sudo systemctl reload nginx
```
### Apache 配置(如果使用 Apache
```apache
<VirtualHost *:443>
ServerName app.fengketrade.com
DocumentRoot /var/www/html
# SSL 配置
SSLEngine on
SSLCertificateFile /path/to/ssl/cert.pem
SSLCertificateKeyFile /path/to/ssl/key.pem
# H5 前端
Alias /h5 /var/www/html/h5
<Directory /var/www/html/h5>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# Vue Router history 模式
RewriteEngine On
RewriteBase /h5
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /h5/index.html [L]
</Directory>
# 静态资源缓存
<FilesMatch "\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">
Header set Cache-Control "max-age=2592000, public"
</FilesMatch>
# HTML 不缓存
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
</FilesMatch>
</VirtualHost>
```
## 部署检查清单
### 打包前检查
- [ ] ✅ `.env` 文件配置正确(生产环境域名)
- [ ] 检查 `manifest.json` 配置
- [ ] 移除开发环境的 console.log
- [ ] 移除调试工具(如 vconsole
- [ ] 检查建行生活模块是否正确集成
### 打包检查
```bash
# 检查打包文件大小
du -sh unpackage/dist/build/h5
# 检查关键文件是否存在
ls -lh unpackage/dist/build/h5/index.html
ls -lh unpackage/dist/build/h5/static/
# 检查是否有 source map生产环境应该移除
find unpackage/dist/build/h5 -name "*.map"
```
### 部署后检查
```bash
# 1. 检查文件是否上传成功
ssh user@app.fengketrade.com "ls -lh /var/www/html/h5"
# 2. 检查权限
ssh user@app.fengketrade.com "ls -ld /var/www/html/h5"
# 3. 测试访问
curl -I https://app.fengketrade.com/h5/
# 4. 检查 API 接口
curl https://app.fengketrade.com/addons/shopro/ccbtest
```
### 功能测试
- [ ] 访问首页是否正常
- [ ] API 接口是否可以调用
- [ ] 建行生活模块是否正常
- [ ] 支付功能是否正常
- [ ] 图片资源是否加载
- [ ] 移动端适配是否正常
## 快速部署命令
### 本地到服务器一键部署
```bash
cd /Users/billy/Code/fengketrade.com/frontend
# 1. 打包(使用 HBuilderX 或命令行)
# HBuilderX: 发行 → 网站-H5手机版
# 2. 压缩
tar --no-mac-metadata --no-xattrs --exclude='.DS_Store' \
-czf h5-$(date +%Y%m%d-%H%M%S).tar.gz \
-C unpackage/dist/build/h5 .
# 3. 上传
ARCHIVE=$(ls -t h5-*.tar.gz | head -1)
scp $ARCHIVE user@app.fengketrade.com:/tmp/
# 4. 部署(在服务器上执行)
ssh user@app.fengketrade.com << 'EOF'
ARCHIVE=$(ls -t /tmp/h5-*.tar.gz | head -1)
sudo mkdir -p /var/www/html/h5
sudo tar -xzf $ARCHIVE -C /var/www/html/h5
sudo chown -R www-data:www-data /var/www/html/h5
sudo chmod -R 755 /var/www/html/h5
rm -f $ARCHIVE
echo "部署完成!"
EOF
# 5. 测试
curl -I https://app.fengketrade.com/h5/
```
## 回滚方案
### 快速回滚
```bash
# 在服务器上执行
cd /var/www/backup
# 查看备份
ls -lht h5-backup-*.tar.gz | head -5
# 回滚到指定版本
BACKUP_FILE="h5-backup-20250117-143000.tar.gz"
sudo rm -rf /var/www/html/h5/*
sudo tar -xzf $BACKUP_FILE -C /var/www/html/h5
sudo systemctl reload nginx
echo "已回滚到: $BACKUP_FILE"
```
## 性能优化
### 打包优化
1. **启用 Gzip 压缩**(在 `vite.config.js`
```javascript
import viteCompression from 'vite-plugin-compression';
export default {
plugins: [
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
})
]
}
```
2. **代码分割**
- uni-app 会自动进行代码分割
- 按页面分包加载
3. **图片优化**
- 压缩图片资源
- 使用 WebP 格式
- 启用懒加载
### CDN 配置
修改 `.env`
```env
# 使用 CDN 加速静态资源
SHOPRO_STATIC_URL = https://cdn.fengketrade.com
```
## 常见问题
### Q: 打包后页面空白?
**原因**: 路径配置错误
**解决**:
```javascript
// manifest.json 中检查
{
"h5": {
"router": {
"mode": "hash", // 推荐使用 hash 模式
"base": "/h5/" // 根据实际部署路径调整
}
}
}
```
### Q: API 请求 404
**原因**: 接口地址配置错误
**解决**: 检查 `.env` 中的 `SHOPRO_BASE_URL`
### Q: 静态资源 404
**原因**: Nginx 配置问题
**解决**: 确保 Nginx 配置正确映射静态资源路径
### Q: 打包文件太大?
**解决**:
1. 移除无用的依赖
2. 启用代码压缩
3. 使用 CDN 加载大文件
4. 按需引入组件
## 监控和维护
### 访问日志
```bash
# Nginx 访问日志
sudo tail -f /var/log/nginx/access.log | grep "GET /h5"
# 错误日志
sudo tail -f /var/log/nginx/error.log
```
### 性能监控
使用浏览器开发者工具:
1. Network - 查看资源加载时间
2. Performance - 分析页面性能
3. Lighthouse - 综合评分
---
*文档版本: 1.0.0*
*最后更新: 2025-01-17*
*作者: Billy*