2026-02-24 17:57:25 +08:00
|
|
|
|
# X-Server Mobile 项目文档
|
|
|
|
|
|
|
|
|
|
|
|
## 项目概述
|
|
|
|
|
|
|
|
|
|
|
|
这是一个基于飞书的企业内部H5移动工具平台,采用多租户架构,支持审批流程引擎。
|
|
|
|
|
|
|
|
|
|
|
|
**关键特性:**
|
|
|
|
|
|
- 支持5种语言:中文、英文、俄语、西语、法语
|
|
|
|
|
|
- 多租户架构(用户全局唯一,关联多个公司)
|
|
|
|
|
|
- 审批流程引擎(单人审批、会签、或签、通知)
|
|
|
|
|
|
- 飞书免登集成
|
|
|
|
|
|
|
|
|
|
|
|
**技术栈:**
|
|
|
|
|
|
- 前端:React 18.3.1 + TypeScript + Vite + Ant Design Mobile
|
|
|
|
|
|
- 后端:PHP 8.5 + Webman 2.x + MySQL + Redis
|
|
|
|
|
|
- 部署:Docker
|
|
|
|
|
|
|
|
|
|
|
|
## 国际化 (i18n) - 重要!
|
|
|
|
|
|
|
|
|
|
|
|
### 前端
|
|
|
|
|
|
- 配置:`frontend/src/i18n/`
|
|
|
|
|
|
- 翻译文件:`frontend/src/i18n/locales/{zh,en,ru,es,fr}.json`
|
|
|
|
|
|
- 语言切换:
|
|
|
|
|
|
- 首页右上角国旗按钮
|
|
|
|
|
|
- 个人中心 -> 语言设置
|
|
|
|
|
|
- 存储位置:localStorage `i18nextLng`
|
|
|
|
|
|
|
|
|
|
|
|
使用方式:
|
|
|
|
|
|
```tsx
|
|
|
|
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
|
|
const { t } = useTranslation()
|
|
|
|
|
|
<div>{t('common.confirm')}</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 后端
|
|
|
|
|
|
- 翻译文件:`backend/lang/{zh,en,ru,es,fr}.php`
|
|
|
|
|
|
- 工具类:`backend/app/Util/Lang.php`
|
|
|
|
|
|
|
|
|
|
|
|
使用方式:
|
|
|
|
|
|
```php
|
|
|
|
|
|
use App\Util\Lang;
|
|
|
|
|
|
$message = Lang::get('common.confirm');
|
|
|
|
|
|
Lang::setLocale('en');
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 目录结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
/www/x-server-mobile/
|
|
|
|
|
|
├── frontend/ # React 前端 (运行在 5173)
|
|
|
|
|
|
│ ├── src/
|
|
|
|
|
|
│ │ ├── i18n/ # 国际化配置
|
|
|
|
|
|
│ │ ├── components/ # 组件
|
|
|
|
|
|
│ │ ├── pages/ # 页面
|
|
|
|
|
|
│ │ ├── modules/ # 业务模块 (approval等)
|
|
|
|
|
|
│ │ └── router/ # 路由
|
|
|
|
|
|
│ └── package.json
|
|
|
|
|
|
├── backend/ # PHP 后端 (运行在 8787)
|
|
|
|
|
|
│ ├── app/
|
|
|
|
|
|
│ │ ├── Controller/ # 控制器 (PascalCase命名)
|
|
|
|
|
|
│ │ ├── Model/ # 模型 (PascalCase命名)
|
|
|
|
|
|
│ │ ├── Service/ # 服务层
|
|
|
|
|
|
│ │ ├── Util/ # 工具类
|
|
|
|
|
|
│ │ └── Middleware/ # 中间件
|
|
|
|
|
|
│ ├── config/ # 配置
|
|
|
|
|
|
│ ├── lang/ # 语言文件
|
|
|
|
|
|
│ └── composer.json
|
|
|
|
|
|
├── docker-compose.yml # Docker 编排
|
|
|
|
|
|
├── nginx/ # OpenResty 配置
|
|
|
|
|
|
├── frpc/ # frpc 配置
|
2026-02-27 18:01:17 +08:00
|
|
|
|
├── docs/ # 项目文档
|
2026-02-28 11:22:55 +08:00
|
|
|
|
│ ├── PROJECT.md # 项目详细文档
|
|
|
|
|
|
│ ├── FEISHU_LOGIN_EXPERIENCE.md # 飞书登录实现经验(重要)
|
|
|
|
|
|
│ ├── feishu-qrcode-login.md # 飞书扫码登录原始文档
|
|
|
|
|
|
│ ├── PROJECT_ISSUES.md # 历史问题汇总
|
|
|
|
|
|
│ └── IMPLEMENTATION_PLAN.md # 实现计划
|
2026-02-24 17:57:25 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 环境配置
|
|
|
|
|
|
|
|
|
|
|
|
### 端口
|
|
|
|
|
|
- Frontend (Vite): 5173
|
|
|
|
|
|
- Backend (Webman): 8787
|
|
|
|
|
|
- OpenResty: 8080
|
|
|
|
|
|
- MySQL: 3306
|
|
|
|
|
|
- Redis: 6379
|
|
|
|
|
|
|
|
|
|
|
|
### 数据库
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- 数据库名:xserver
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- 密码:mysql_xEsdPa
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- 容器名:1Panel-mysql-409s
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- 网络:1panel-network
|
|
|
|
|
|
|
|
|
|
|
|
### Docker 容器
|
|
|
|
|
|
- 后端容器:x-server-backend
|
|
|
|
|
|
- 使用 Dockerfile:`backend/Dockerfile`
|
|
|
|
|
|
- PHP 版本:8.5
|
|
|
|
|
|
|
|
|
|
|
|
## 关键文件路径
|
|
|
|
|
|
|
|
|
|
|
|
### 前端关键文件
|
|
|
|
|
|
- 入口:`frontend/src/main.tsx`
|
2026-02-28 11:22:55 +08:00
|
|
|
|
- SDK 引入:`frontend/index.html`(飞书 H5 JSSDK + QR SDK)
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- 路由:`frontend/src/router/index.tsx`
|
|
|
|
|
|
- 布局:`frontend/src/components/layout/`
|
|
|
|
|
|
- 状态管理:`frontend/src/stores/`
|
2026-02-28 11:22:55 +08:00
|
|
|
|
- API 封装:`frontend/src/services/api.ts`(含 401 token 刷新)
|
|
|
|
|
|
- 登录页:`frontend/src/pages/login/index.tsx`
|
|
|
|
|
|
- 扫码登录:`frontend/src/pages/login/QRCodeLogin.tsx`
|
|
|
|
|
|
- 环境检测:`frontend/src/pages/feishu-env-test.tsx`
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- rem 适配:`frontend/src/utils/rem.ts`
|
|
|
|
|
|
|
|
|
|
|
|
### 后端关键文件
|
|
|
|
|
|
- 入口:`backend/start.php`
|
|
|
|
|
|
- 路由:`backend/config/route.php`
|
|
|
|
|
|
- 数据库配置:`backend/config/database.php`
|
|
|
|
|
|
- Redis 配置:`backend/config/redis.php`
|
|
|
|
|
|
- 容器配置:`backend/config/container.php`
|
|
|
|
|
|
|
|
|
|
|
|
### 重要配置文件
|
|
|
|
|
|
- Docker 编排:`docker-compose.yml`
|
|
|
|
|
|
- OpenResty 配置:`nginx/x-server-api.conf`
|
|
|
|
|
|
- frpc 配置:`frpc/frpc-dev.ini`
|
|
|
|
|
|
|
|
|
|
|
|
## 审批流程引擎
|
|
|
|
|
|
|
|
|
|
|
|
### 节点类型
|
|
|
|
|
|
- `single_approve` - 单人审批
|
|
|
|
|
|
- `and_sign` - 会签(所有人通过)
|
|
|
|
|
|
- `or_sign` - 或签(任一人通过)
|
|
|
|
|
|
- `notify` - 通知节点
|
|
|
|
|
|
|
|
|
|
|
|
### 状态
|
|
|
|
|
|
- pending: 待审批
|
|
|
|
|
|
- approved: 已通过
|
|
|
|
|
|
- rejected: 已拒绝
|
|
|
|
|
|
- cancelled: 已撤销
|
|
|
|
|
|
|
|
|
|
|
|
### 关键文件
|
|
|
|
|
|
- 服务:`backend/app/Service/ApprovalService.php`
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- 控制器:`backend/app/Controller/ApprovalController.php`
|
|
|
|
|
|
- 模型:`backend/app/Model/ApprovalOrder.php`, `ApprovalFlow.php`, `ApprovalNode.php`, `ApprovalRecord.php`
|
|
|
|
|
|
- 前端列表页:`frontend/src/pages/approval/list.tsx`
|
|
|
|
|
|
- 前端详情页:`frontend/src/pages/approval/detail.tsx`
|
|
|
|
|
|
|
|
|
|
|
|
### 列表过滤逻辑
|
|
|
|
|
|
- **我发起的 (my)**: `applicant_id = current_user_id`
|
|
|
|
|
|
- **待我审批 (pending)**: 订单状态为 pending + 当前用户在当前节点的审批人列表中 + 未审批
|
|
|
|
|
|
- **我已审批 (approved)**: 用户有审批记录(approve/reject)+ 订单状态为已完成(approved/rejected/cancelled)
|
|
|
|
|
|
|
|
|
|
|
|
### 测试数据
|
|
|
|
|
|
运行测试数据脚本:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker exec x-server-backend php test_data.php
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
该脚本会创建:
|
|
|
|
|
|
- 2 个待审批的订单(其他用户提交,用户1审批)
|
|
|
|
|
|
- 1 个已通过的订单(用户1已审批)
|
|
|
|
|
|
- 1 个已拒绝的订单(用户1已拒绝)
|
2026-02-24 17:57:25 +08:00
|
|
|
|
|
|
|
|
|
|
## 飞书集成
|
|
|
|
|
|
|
2026-02-28 11:22:55 +08:00
|
|
|
|
### 登录方式
|
|
|
|
|
|
支持两种飞书登录方式:
|
|
|
|
|
|
|
|
|
|
|
|
| 方式 | 使用场景 | SDK |
|
|
|
|
|
|
|------|----------|-----|
|
|
|
|
|
|
| **飞书内免登** | 用户在飞书客户端内打开应用,自动登录 | H5 JSSDK (`h5-js-sdk-1.5.19.js`) |
|
|
|
|
|
|
| **飞书外扫码登录** | 用户在普通浏览器中打开,用飞书扫码登录 | QR SDK (`LarkSSOSDKWebQRCode-1.0.3.js`) |
|
|
|
|
|
|
|
|
|
|
|
|
### 核心接口
|
|
|
|
|
|
- `POST /api/v1/auth/feishu-login` - 飞书免登/扫码登录
|
|
|
|
|
|
- `GET /api/v1/auth/feishu-config?company=2` - 获取公司飞书应用配置
|
|
|
|
|
|
- `POST /api/v1/auth/qrcode-login` - 生成扫码登录二维码
|
|
|
|
|
|
- `GET /api/v1/auth/qrcode-login/status?session_id=xxx` - 轮询扫码状态
|
|
|
|
|
|
- `GET /login/callback` - 飞书授权回调
|
|
|
|
|
|
|
|
|
|
|
|
### URL 参数
|
|
|
|
|
|
| 参数 | 说明 | 示例 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| `company` | 公司ID(必需) | `?company=2` |
|
|
|
|
|
|
| `app_id` | 飞书应用ID(可选) | `?app_id=cli_xxx` |
|
|
|
|
|
|
| `redirect` | 登录成功后跳转地址 | `?redirect=/dashboard` |
|
|
|
|
|
|
|
|
|
|
|
|
### Token 自动刷新
|
|
|
|
|
|
- API 返回 401 时,在飞书环境中自动调用免登刷新 token
|
|
|
|
|
|
- 刷新成功后自动重试原始请求,用户无感知
|
|
|
|
|
|
- 使用 `_retry` 标记防止死循环
|
|
|
|
|
|
|
|
|
|
|
|
### 详细文档
|
|
|
|
|
|
飞书登录完整实现经验:`docs/FEISHU_LOGIN_EXPERIENCE.md`
|
2026-02-24 17:57:25 +08:00
|
|
|
|
|
|
|
|
|
|
### 环境变量
|
|
|
|
|
|
```env
|
|
|
|
|
|
FEISHU_APP_ID=cli_xxxxxxxxxxxxx
|
|
|
|
|
|
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
|
|
|
|
|
|
FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx
|
|
|
|
|
|
FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 常用命令
|
|
|
|
|
|
|
|
|
|
|
|
### 前端
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /www/x-server-mobile/frontend
|
|
|
|
|
|
npm install
|
|
|
|
|
|
npm run dev
|
|
|
|
|
|
npm run build
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 后端
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /www/x-server-mobile/backend
|
|
|
|
|
|
composer install
|
|
|
|
|
|
php start.php start
|
|
|
|
|
|
php start.php stop
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Docker
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /www/x-server-mobile
|
|
|
|
|
|
docker compose up -d
|
|
|
|
|
|
docker compose restart backend
|
|
|
|
|
|
docker logs x-server-backend -f
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 反向代理配置(开发环境)
|
|
|
|
|
|
|
|
|
|
|
|
用户已有 frpc 和 OpenResty,配置用于调试飞书登录。
|
|
|
|
|
|
|
|
|
|
|
|
1. OpenResty 监听 8080,代理到 8787
|
|
|
|
|
|
2. frpc 将 8080 暴露到公网
|
|
|
|
|
|
3. 飞书回调地址配置为公网域名
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
### Webman 框架要点
|
2026-02-24 17:57:25 +08:00
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
**PSR-4 自动加载**
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- 目录名必须是 PascalCase(Controller、Model、Service、Util)
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
**数据库连接**
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- 使用 Webman 官方 `webman/database` 包
|
|
|
|
|
|
- 使用 `support\Db` facade 进行数据库操作
|
|
|
|
|
|
- 使用 `Db::connection()` 获取数据库连接进行事务处理
|
2026-02-24 17:57:25 +08:00
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
**Bootstrap 接口**
|
2026-02-24 17:57:25 +08:00
|
|
|
|
- 方法签名:`public static function start(?\Workerman\Worker $worker): void`
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
**官方依赖包**
|
2026-02-25 23:56:09 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
composer require workerman/webman:^2.0
|
|
|
|
|
|
composer require webman/database:^2.0
|
|
|
|
|
|
composer require webman/redis:^2.0
|
|
|
|
|
|
composer require illuminate/pagination:^11.0
|
|
|
|
|
|
composer require illuminate/events:^11.0
|
|
|
|
|
|
composer require symfony/var-dumper:^7.0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
### 前端框架要点
|
|
|
|
|
|
|
|
|
|
|
|
**React 版本**
|
|
|
|
|
|
- 必须使用 React 18.3.1(不能用19)
|
|
|
|
|
|
- @types/react 和 @types/react-dom 也要用 v18
|
|
|
|
|
|
|
|
|
|
|
|
**Ant Design Mobile**
|
|
|
|
|
|
- 必须导入全局样式:`import 'antd-mobile/es/global'`
|
|
|
|
|
|
- DatePicker 使用 `visible` 属性控制弹框显示
|
|
|
|
|
|
|
|
|
|
|
|
**Zustand 状态管理**
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- 登录时必须调用 `login(user, token)` 方法设置状态
|
|
|
|
|
|
- Token 存储在 localStorage,key 为 `token`
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
**类型定义**
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- `User.id` 是 `number` 类型(后端返回整型)
|
|
|
|
|
|
- `User.company_id` 是 `number` 类型(数据库字段名)
|
|
|
|
|
|
|
2026-02-24 17:57:25 +08:00
|
|
|
|
## 语言切换位置
|
|
|
|
|
|
|
|
|
|
|
|
1. **首页** - 右上角显示当前语言国旗,点击切换
|
|
|
|
|
|
2. **个人中心** -> 语言设置
|
|
|
|
|
|
|
|
|
|
|
|
## 当前开发状态
|
|
|
|
|
|
|
|
|
|
|
|
- ✅ 前端基础框架搭建完成
|
|
|
|
|
|
- ✅ 后端 API 框架搭建完成
|
|
|
|
|
|
- ✅ 5语言国际化支持完成
|
|
|
|
|
|
- ✅ Docker 开发环境配置完成
|
2026-02-28 11:22:55 +08:00
|
|
|
|
- ✅ 飞书登录完成
|
|
|
|
|
|
- ✅ 飞书内免登(静默授权)
|
|
|
|
|
|
- ✅ 飞书外扫码登录
|
|
|
|
|
|
- ✅ Token 过期自动刷新(飞书环境)
|
|
|
|
|
|
- ✅ 多租户支持(company 参数)
|
2026-02-25 23:56:09 +08:00
|
|
|
|
- ✅ 审批流程引擎完成
|
|
|
|
|
|
- ✅ 通用审批列表页(我发起的/待我审批/我已审批)
|
|
|
|
|
|
- ✅ 通用审批详情页(支持审批通过/拒绝/撤销操作)
|
|
|
|
|
|
- ✅ 出差申请功能完成
|
|
|
|
|
|
- ✅ 出差申请页(含行程管理、审批人选择、抄送人选择)
|
|
|
|
|
|
- ✅ 草稿自动保存/恢复功能
|
|
|
|
|
|
- ✅ 默认审批人自动加载
|
2026-02-24 17:57:25 +08:00
|
|
|
|
|
|
|
|
|
|
## 下一步工作
|
|
|
|
|
|
|
2026-02-25 23:56:09 +08:00
|
|
|
|
1. 完善审批流程的会签、或签逻辑测试
|
|
|
|
|
|
2. 实现公司切换功能
|
|
|
|
|
|
3. 完善权限控制
|
|
|
|
|
|
4. 添加更多业务类型(费用报销、请假申请、采购申请)
|
2026-02-26 09:12:21 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 快速参考
|
|
|
|
|
|
|
|
|
|
|
|
### 查看问题汇总
|
2026-02-27 18:01:17 +08:00
|
|
|
|
所有历史问题已整理在 `docs/PROJECT_ISSUES.md` 文件中。
|
2026-02-26 09:12:21 +08:00
|
|
|
|
|
2026-02-28 11:22:55 +08:00
|
|
|
|
### 飞书登录文档
|
|
|
|
|
|
飞书登录完整实现经验和调试技巧:`docs/FEISHU_LOGIN_EXPERIENCE.md`
|
|
|
|
|
|
|
2026-02-26 09:12:21 +08:00
|
|
|
|
### 让 CLAUDE 加载问题文档
|
|
|
|
|
|
```
|
2026-02-27 18:01:17 +08:00
|
|
|
|
请参考 docs/PROJECT_ISSUES.md 中的问题汇总
|
2026-02-28 11:22:55 +08:00
|
|
|
|
请参考 docs/FEISHU_LOGIN_EXPERIENCE.md 中的飞书登录经验
|
2026-02-26 09:12:21 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 调试后端
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 查看日志
|
|
|
|
|
|
docker logs x-server-backend -f --tail 50
|
|
|
|
|
|
|
|
|
|
|
|
# 重启后端
|
|
|
|
|
|
docker compose restart backend
|
|
|
|
|
|
|
|
|
|
|
|
# 复制文件到容器
|
|
|
|
|
|
docker cp host_file x-server-backend:/app/app/service/xxx.php
|
|
|
|
|
|
|
|
|
|
|
|
# 重载 workers(不重启整个服务)
|
|
|
|
|
|
docker exec x-server-backend php /app/webman reload
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 数据库操作
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 进入 MySQL 容器
|
|
|
|
|
|
docker exec -it 1Panel-mysql-409s mysql -uroot -pmysql_xEsdPa xserver
|
|
|
|
|
|
|
|
|
|
|
|
# 运行测试数据脚本
|
|
|
|
|
|
docker exec x-server-backend php /app/test_data.php
|
|
|
|
|
|
```
|