优化代码
This commit is contained in:
196
doc/backend-technical-deep-dive.md
Normal file
196
doc/backend-technical-deep-dive.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# 后端架构深度解析与开发指南
|
||||
|
||||
## 1. 引言
|
||||
|
||||
本文档旨在为开发人员提供一份关于本项目(智慧农场系统)后端架构的深度技术解析。不同于基础的架构介绍,本文档聚焦于**代码实现细节**、**数据库设计范式**以及**核心业务流程**的底层逻辑。它将帮助你在接手项目后,快速理解现有代码模式(Patterns),并安全、高效地扩展新功能。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心架构与技术实现
|
||||
|
||||
本项目基于 **ThinkPHP 6** 框架开发,采用了典型的 **MVC** 架构,并结合了多应用(Multi-app)模式。
|
||||
|
||||
### 2.1 C层(Controller)技术实现
|
||||
|
||||
控制器层主要负责请求接收、参数校验和业务调度。项目在控制器层有以下显著的实现模式:
|
||||
|
||||
#### 2.1.1 基类封装 (`app/ApiRest.php`)
|
||||
所有 API 控制器均继承自 `app\ApiRest`,该基类提供了以下核心能力:
|
||||
|
||||
- **统一响应格式**:通过 `success($data)` 和 `error($msg)` 方法,强制统一 API 返回结构:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": { ... },
|
||||
"sign": "..." // 签名(可选)
|
||||
}
|
||||
```
|
||||
- **多租户隔离**:在构造函数中自动解析 `uniacid`(商户/租户ID),并赋值给 `$this->_uniacid`。这是系统支持多商户SaaS模式的核心。
|
||||
- **鉴权机制**:
|
||||
- 检查 HTTP Header 中的 `autograph`(小程序用户标识)。
|
||||
- 维护一个 `$noNeedLogin` 白名单。
|
||||
- **注意**:`shareChangeData` 方法中硬编码了大量白名单路由,这种实现方式维护成本较高,建议后续改为中间件或注解配置。
|
||||
|
||||
#### 2.1.2 胖模型,瘦控制器
|
||||
控制器(如 `app/farm/controller/IndexLand.php`)通常比较轻量,不包含复杂的计算逻辑。
|
||||
- **示例**:在“土地下单”流程中,价格计算、优惠券抵扣等逻辑全部下沉到了 Model 层 (`LandOrder::payOrderInfo`)。
|
||||
- **优点**:业务逻辑可复用,控制器代码整洁。
|
||||
|
||||
### 2.2 M层(Model)技术实现
|
||||
|
||||
模型层不仅负责数据库交互,还承担了大部分业务逻辑。
|
||||
|
||||
#### 2.2.1 基类扩展 (`app/BaseModel.php`)
|
||||
- 实现了基础的 CRUD 方法(`getRow`, `listRow`, `createRow` 等)。
|
||||
- **软删除机制**:手动实现了基于 `deleted` 字段和 `delete_time` 的软删除,未使用 TP 自带的 `SoftDelete` Trait。
|
||||
- **代码现状**:核心业务 Model(如 `LandOrder`)往往重写了 `dataAdd`/`dataUpdate` 方法,直接调用 TP 的 `insert`/`update`,并未完全依赖 `BaseModel` 的封装。这导致项目中存在两种 CRUD 风格。
|
||||
|
||||
#### 2.2.2 高级特性应用
|
||||
- **获取器 (Getters)**:大量使用 TP 的获取器处理字段格式化。例如 `LandList::getImgsAttr` 将数据库存储的逗号分隔字符串自动转换为数组。
|
||||
- **动态追加字段**:使用 `$append` 属性在 JSON 序列化时追加计算字段(如 `min_price`)。
|
||||
- **观察者模式**:在 `LandList::updateSome` 中,手动实现了观察者模式 (`app\farm\server\Land`) 来同步更新关联表数据。这是项目中少见的设计模式应用,值得保留和参考。
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据库深度解析
|
||||
|
||||
### 3.1 数据库概览
|
||||
- **引擎**:大部分表使用 `MyISAM`。
|
||||
- **风险**:MyISAM 不支持事务,且在并发写入时是表级锁。对于涉及资金交易的表(如 `balance_water`, `land_order`),强烈建议迁移到 `InnoDB`。
|
||||
- **字符集**:`utf8` / `utf8mb4`。
|
||||
- **多租户设计**:几乎所有表都包含 `uniacid` 字段,用于物理隔离不同商户的数据。
|
||||
|
||||
### 3.2 核心业务实体关系 (ER图)
|
||||
|
||||
以下是“土地认养”核心业务的实体关系推导:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
User ||--o{ LandOrder : "下单"
|
||||
User ||--o{ Address : "拥有"
|
||||
Farmer ||--o{ LandList : "管理"
|
||||
LandList ||--|{ LandSpe : "包含规格"
|
||||
LandList ||--o{ LandMassif : "包含地块"
|
||||
LandOrder }|--|| LandList : "关联土地"
|
||||
LandOrder }|--|| LandSpe : "关联规格"
|
||||
LandOrder }|--|| LandMassif : "关联地块"
|
||||
LandOrder ||--|{ LandOrderSeed : "包含种子"
|
||||
|
||||
User {
|
||||
int id PK
|
||||
int uniacid
|
||||
string openid
|
||||
string nickName
|
||||
}
|
||||
|
||||
LandList {
|
||||
int id PK
|
||||
int farmer_id FK "农场主"
|
||||
string title "土地名称"
|
||||
int status
|
||||
}
|
||||
|
||||
LandOrder {
|
||||
int id PK
|
||||
string order_code "订单号"
|
||||
decimal pay_price "支付金额"
|
||||
int pay_type "1:未支付 2:已支付"
|
||||
int status
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 关键表结构解析
|
||||
|
||||
#### 1. `ims_lbfarm_land_list` (土地表)
|
||||
- 核心商品表,存储土地的基本信息。
|
||||
- 关联表:`ims_lbfarm_land_spe` (规格), `ims_lbfarm_land_massif` (地块/位置)。
|
||||
|
||||
#### 2. `ims_lbfarm_land_order` (土地订单表)
|
||||
- **核心字段**:
|
||||
- `order_code`: 业务订单号。
|
||||
- `transaction_id`: 微信支付流水号。
|
||||
- `pay_type`: 支付状态 (1=未支付, 2=微信支付, 3=余额支付)。
|
||||
- `seed_data`: 种子信息的 JSON 快照(反范式设计,避免关联查询)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心业务流程剖析
|
||||
|
||||
### 4.1 土地认养下单与支付流程
|
||||
|
||||
这是一个跨越 Controller、Model 和 Service 的复杂流程:
|
||||
|
||||
1. **预下单 (计价)**
|
||||
- **入口**:`IndexLand::landPayOrderInfo`
|
||||
- **逻辑**:调用 `LandOrder::payOrderInfo`。
|
||||
- **计算公式**:`总价 = 土地规格单价 + (地块单价 * 认养周期) + ∑(种子单价 * 数量) - 优惠券金额`。
|
||||
|
||||
2. **创建订单**
|
||||
- **入口**:`IndexLand::landPayOrder`
|
||||
- **逻辑**:再次调用 `payOrderInfo` 校验价格,然后写入 `land_order` 表,同时写入 `land_order_seed` 表。
|
||||
|
||||
3. **支付回调 (核心)**
|
||||
- **逻辑位置**:`LandOrder::orderResult`
|
||||
- **事务处理**:
|
||||
1. 开启事务 (`Db::startTrans()`)。
|
||||
2. 更新订单状态 (`pay_type=2`, `pay_time`)。
|
||||
3. **余额扣除**:如果使用了余额抵扣,调用 `BalanceWater::addWater`。
|
||||
4. **资金流水**:记录 `FinanceWater`。
|
||||
5. **分销结算**:调用 `DistributionCash::addUserCash` 计算分销佣金。
|
||||
6. 提交事务 (`Db::commit()`)。
|
||||
- **后续动作**:发送微信订阅消息 (`paySendService`),这是异步或非事务性的操作,放在 commit 之后执行是正确的。
|
||||
|
||||
---
|
||||
|
||||
## 5. 开发实战指南
|
||||
|
||||
### 5.1 如何新增一个 API 接口?
|
||||
|
||||
假设你需要新增一个“获取最近的农场列表”接口:
|
||||
|
||||
1. **创建控制器**:
|
||||
在 `app/farm/controller` 下创建 `IndexFarm.php`,继承 `ApiRest`。
|
||||
```php
|
||||
class IndexFarm extends ApiRest {
|
||||
public function nearby() {
|
||||
$lat = $this->_param['lat'];
|
||||
$lng = $this->_param['lng'];
|
||||
// 调用 Model 获取数据
|
||||
$data = (new Farmer())->getNearby($lat, $lng);
|
||||
return $this->success($data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **实现 Model 逻辑**:
|
||||
在 `app/farm/model/Farmer.php` 中实现 `getNearby` 方法。建议参考 `LandList::indexDataList` 中的距离计算 SQL。
|
||||
|
||||
3. **配置路由**:
|
||||
在 `app/farm/route/route.php` 中注册路由(如果项目未开启自动路由)。目前看项目启用了自动路由,直接访问 `farm/index_farm/nearby` 即可。
|
||||
|
||||
4. **配置鉴权**:
|
||||
如果该接口不需要登录,需要在 `ApiRest::shareChangeData` 或控制器的构造函数中将其加入 `$noNeedLogin` 数组。
|
||||
|
||||
### 5.2 常见坑与规避
|
||||
|
||||
1. **数据库事务**:
|
||||
由于大量表使用 `MyISAM`,在涉及多表更新时(如订单状态+库存扣减),事务可能不会生效。
|
||||
- **建议**:在开发新功能涉及核心交易表时,务必检查表引擎,必要时手动 `ALTER TABLE ... ENGINE=InnoDB`。
|
||||
|
||||
2. **参数获取**:
|
||||
尽量使用 `$this->_param` 获取参数,它合并了 GET 和 POST 数据。避免直接使用 `$_GET` 或 `$_POST`。
|
||||
|
||||
3. **浮点数计算**:
|
||||
项目中存在直接使用浮点数计算金额的情况(如 `round($price, 2)`)。在涉及金额比对时,建议使用 `bccomp` 等高精度函数,避免精度丢失导致的 0.01 元误差。
|
||||
|
||||
4. **SQL 注入风险**:
|
||||
虽然 TP 的 ORM 有防注入机制,但在拼接原生 SQL(如距离计算)时,务必使用参数绑定,严禁直接拼接变量。
|
||||
|
||||
---
|
||||
|
||||
## 6. 附录:推荐的重构方向
|
||||
|
||||
1. **引入 .env 配置**:将 `config/database.php` 中的硬编码配置移至环境变量。
|
||||
2. **统一 Model 规范**:废弃 `dataAdd` 等自定义方法,统一使用 TP 标准的 `save/create`,或完善 `BaseModel` 并强制继承。
|
||||
3. **中间件鉴权**:将 `ApiRest` 中的硬编码鉴权逻辑重构为 TP 中间件。
|
||||
4. **数据库迁移**:编写脚本将所有 `MyISAM` 表迁移至 `InnoDB`。
|
||||
2138
doc/stsm_farm.sql
Normal file
2138
doc/stsm_farm.sql
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user