996 lines
22 KiB
Markdown
996 lines
22 KiB
Markdown
|
|
# 🚀 结伴客开发指南
|
|||
|
|
|
|||
|
|
## 📋 项目概述
|
|||
|
|
|
|||
|
|
结伴客是一个基于微信小程序的结伴旅行和动物认领平台。本开发指南旨在帮助开发者快速上手项目开发,理解项目架构,并遵循统一的开发规范。
|
|||
|
|
|
|||
|
|
## 🛠️ 开发环境搭建
|
|||
|
|
|
|||
|
|
### 系统要求
|
|||
|
|
|
|||
|
|
#### 前端开发环境
|
|||
|
|
- **Node.js**: 16.0+ (推荐使用 18.x LTS)
|
|||
|
|
- **npm**: 8.0+ 或 **yarn**: 1.22+
|
|||
|
|
- **微信开发者工具**: 最新稳定版
|
|||
|
|
- **IDE**: VS Code (推荐) / WebStorm
|
|||
|
|
- **Git**: 2.30+
|
|||
|
|
|
|||
|
|
#### 后端开发环境
|
|||
|
|
- **JDK**: Java 17 (推荐使用 OpenJDK)
|
|||
|
|
- **Maven**: 3.6+
|
|||
|
|
- **IDE**: IntelliJ IDEA (推荐) / Eclipse / VS Code
|
|||
|
|
- **数据库**: MySQL 8.0+
|
|||
|
|
- **缓存**: Redis 6.0+
|
|||
|
|
- **消息队列**: RabbitMQ 3.8+
|
|||
|
|
- **容器化**: Docker 20.10+ 和 Docker Compose 1.29+
|
|||
|
|
|
|||
|
|
### 环境安装
|
|||
|
|
|
|||
|
|
#### macOS 环境安装
|
|||
|
|
```bash
|
|||
|
|
# 使用 Homebrew 安装开发工具
|
|||
|
|
# 安装 Node.js
|
|||
|
|
brew install node
|
|||
|
|
|
|||
|
|
# 安装 Java 17
|
|||
|
|
brew install openjdk@17
|
|||
|
|
|
|||
|
|
# 安装 Maven
|
|||
|
|
brew install maven
|
|||
|
|
|
|||
|
|
# 安装 Docker
|
|||
|
|
brew install docker docker-compose
|
|||
|
|
|
|||
|
|
# 安装 MySQL
|
|||
|
|
brew install mysql
|
|||
|
|
|
|||
|
|
# 安装 Redis
|
|||
|
|
brew install redis
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Windows 环境安装
|
|||
|
|
1. **Node.js**: 从 [官网](https://nodejs.org/) 下载安装
|
|||
|
|
2. **JDK 17**: 从 [Adoptium](https://adoptium.net/) 下载安装
|
|||
|
|
3. **Maven**: 从 [官网](https://maven.apache.org/) 下载配置
|
|||
|
|
4. **Docker Desktop**: 从 [官网](https://www.docker.com/products/docker-desktop) 下载安装
|
|||
|
|
5. **微信开发者工具**: 从 [官网](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) 下载安装
|
|||
|
|
|
|||
|
|
#### Ubuntu/Debian 环境安装
|
|||
|
|
```bash
|
|||
|
|
# 更新包管理器
|
|||
|
|
sudo apt update
|
|||
|
|
|
|||
|
|
# 安装 Node.js
|
|||
|
|
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
|||
|
|
sudo apt-get install -y nodejs
|
|||
|
|
|
|||
|
|
# 安装 Java 17
|
|||
|
|
sudo apt install openjdk-17-jdk
|
|||
|
|
|
|||
|
|
# 安装 Maven
|
|||
|
|
sudo apt install maven
|
|||
|
|
|
|||
|
|
# 安装 Docker
|
|||
|
|
sudo apt install docker.io docker-compose
|
|||
|
|
|
|||
|
|
# 安装 MySQL
|
|||
|
|
sudo apt install mysql-server
|
|||
|
|
|
|||
|
|
# 安装 Redis
|
|||
|
|
sudo apt install redis-server
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 环境验证
|
|||
|
|
```bash
|
|||
|
|
# 验证 Node.js 和 npm
|
|||
|
|
node --version
|
|||
|
|
npm --version
|
|||
|
|
|
|||
|
|
# 验证 Java 和 Maven
|
|||
|
|
java -version
|
|||
|
|
mvn -version
|
|||
|
|
|
|||
|
|
# 验证 Docker
|
|||
|
|
docker --version
|
|||
|
|
docker-compose --version
|
|||
|
|
|
|||
|
|
# 验证数据库连接
|
|||
|
|
mysql --version
|
|||
|
|
redis-cli --version
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🏗️ 项目架构
|
|||
|
|
|
|||
|
|
### 整体架构
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
A[微信小程序前端] --> B[API网关]
|
|||
|
|
C[管理后台前端] --> B
|
|||
|
|
D[官网前端] --> B
|
|||
|
|
|
|||
|
|
B --> E[认证服务]
|
|||
|
|
B --> F[用户服务]
|
|||
|
|
B --> G[旅行服务]
|
|||
|
|
B --> H[动物服务]
|
|||
|
|
B --> I[订单服务]
|
|||
|
|
B --> J[商家服务]
|
|||
|
|
|
|||
|
|
E --> K[MySQL数据库]
|
|||
|
|
F --> K
|
|||
|
|
G --> K
|
|||
|
|
H --> K
|
|||
|
|
I --> K
|
|||
|
|
J --> K
|
|||
|
|
|
|||
|
|
E --> L[Redis缓存]
|
|||
|
|
F --> L
|
|||
|
|
G --> L
|
|||
|
|
H --> L
|
|||
|
|
|
|||
|
|
subgraph "服务注册与发现"
|
|||
|
|
M[Eureka Server]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
E --> M
|
|||
|
|
F --> M
|
|||
|
|
G --> M
|
|||
|
|
H --> M
|
|||
|
|
I --> M
|
|||
|
|
J --> M
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 目录结构
|
|||
|
|
```
|
|||
|
|
jiebanke/
|
|||
|
|
├── frontend/ # 前端项目
|
|||
|
|
│ ├── miniprogram/ # 微信小程序
|
|||
|
|
│ ├── admin-web/ # 管理后台
|
|||
|
|
│ └── website/ # 官方网站
|
|||
|
|
├── backend-java/ # Java后端服务
|
|||
|
|
│ ├── eureka-server/ # 服务注册中心
|
|||
|
|
│ ├── gateway-service/ # API网关
|
|||
|
|
│ ├── auth-service/ # 认证服务
|
|||
|
|
│ ├── user-service/ # 用户服务
|
|||
|
|
│ ├── travel-service/ # 旅行服务
|
|||
|
|
│ ├── animal-service/ # 动物服务
|
|||
|
|
│ ├── order-service/ # 订单服务
|
|||
|
|
│ ├── merchant-service/ # 商家服务
|
|||
|
|
│ └── common/ # 公共模块
|
|||
|
|
├── docs/ # 项目文档
|
|||
|
|
├── scripts/ # 部署脚本
|
|||
|
|
└── docker-compose.yml # Docker编排文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📝 开发规范
|
|||
|
|
|
|||
|
|
### 1. 代码规范
|
|||
|
|
|
|||
|
|
#### 前端代码规范
|
|||
|
|
|
|||
|
|
##### JavaScript/TypeScript 规范
|
|||
|
|
```javascript
|
|||
|
|
// ✅ 推荐写法
|
|||
|
|
const getUserInfo = async (userId) => {
|
|||
|
|
try {
|
|||
|
|
const response = await api.get(`/users/${userId}`);
|
|||
|
|
return response.data;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户信息失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ❌ 不推荐写法
|
|||
|
|
function getUserInfo(userId) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
api.get('/users/' + userId).then(res => {
|
|||
|
|
resolve(res.data);
|
|||
|
|
}).catch(err => {
|
|||
|
|
reject(err);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### Vue 组件规范
|
|||
|
|
```vue
|
|||
|
|
<template>
|
|||
|
|
<div class="user-profile">
|
|||
|
|
<div class="user-avatar">
|
|||
|
|
<image :src="userInfo.avatar" mode="aspectFill" />
|
|||
|
|
</div>
|
|||
|
|
<div class="user-info">
|
|||
|
|
<text class="username">{{ userInfo.nickname }}</text>
|
|||
|
|
<text class="user-desc">{{ userInfo.description }}</text>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
export default {
|
|||
|
|
name: 'UserProfile',
|
|||
|
|
props: {
|
|||
|
|
userInfo: {
|
|||
|
|
type: Object,
|
|||
|
|
required: true,
|
|||
|
|
default: () => ({})
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
// 组件内部状态
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
computed: {
|
|||
|
|
// 计算属性
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
// 组件方法
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.user-profile {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-avatar {
|
|||
|
|
width: 100rpx;
|
|||
|
|
height: 100rpx;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
overflow: hidden;
|
|||
|
|
margin-right: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-info {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.username {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.user-desc {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #666;
|
|||
|
|
margin-top: 10rpx;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 后端代码规范
|
|||
|
|
|
|||
|
|
##### Java 命名规范
|
|||
|
|
```java
|
|||
|
|
// ✅ 推荐写法
|
|||
|
|
public class UserService {
|
|||
|
|
private static final int MAX_RETRY_COUNT = 3;
|
|||
|
|
private final UserMapper userMapper;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据用户ID获取用户信息
|
|||
|
|
*
|
|||
|
|
* @param userId 用户ID
|
|||
|
|
* @return 用户信息
|
|||
|
|
* @throws BusinessException 当用户不存在时抛出
|
|||
|
|
*/
|
|||
|
|
public UserDTO getUserById(Long userId) {
|
|||
|
|
if (userId == null || userId <= 0) {
|
|||
|
|
throw new IllegalArgumentException("用户ID不能为空或小于等于0");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
User user = userMapper.selectById(userId);
|
|||
|
|
if (user == null) {
|
|||
|
|
throw new BusinessException(ErrorCode.USER_NOT_FOUND, "用户不存在");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return UserConverter.toDTO(user);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ❌ 不推荐写法
|
|||
|
|
public class userservice {
|
|||
|
|
public Object getuser(Object id) {
|
|||
|
|
return userMapper.selectById((Long)id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
##### Spring Boot 控制器规范
|
|||
|
|
```java
|
|||
|
|
@RestController
|
|||
|
|
@RequestMapping("/api/v1/users")
|
|||
|
|
@Validated
|
|||
|
|
@Slf4j
|
|||
|
|
public class UserController {
|
|||
|
|
|
|||
|
|
private final UserService userService;
|
|||
|
|
|
|||
|
|
public UserController(UserService userService) {
|
|||
|
|
this.userService = userService;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@GetMapping("/{id}")
|
|||
|
|
@Operation(summary = "获取用户信息", description = "根据用户ID获取用户详细信息")
|
|||
|
|
public ApiResponse<UserDTO> getUserById(
|
|||
|
|
@PathVariable @Min(1) Long id) {
|
|||
|
|
|
|||
|
|
log.info("获取用户信息, userId: {}", id);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
UserDTO user = userService.getUserById(id);
|
|||
|
|
return ApiResponse.success(user);
|
|||
|
|
} catch (BusinessException e) {
|
|||
|
|
log.warn("获取用户信息失败, userId: {}, error: {}", id, e.getMessage());
|
|||
|
|
return ApiResponse.error(e.getCode(), e.getMessage());
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
log.error("获取用户信息系统错误, userId: {}", id, e);
|
|||
|
|
return ApiResponse.error(ErrorCode.SYSTEM_ERROR, "系统错误");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 数据库规范
|
|||
|
|
|
|||
|
|
#### 表设计规范
|
|||
|
|
```sql
|
|||
|
|
-- ✅ 推荐的表结构设计
|
|||
|
|
CREATE TABLE users (
|
|||
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
|
|||
|
|
openid VARCHAR(100) UNIQUE NOT NULL COMMENT '微信openid',
|
|||
|
|
nickname VARCHAR(50) NOT NULL COMMENT '用户昵称',
|
|||
|
|
avatar VARCHAR(255) COMMENT '头像URL',
|
|||
|
|
gender ENUM('male', 'female', 'unknown') DEFAULT 'unknown' COMMENT '性别',
|
|||
|
|
phone VARCHAR(20) UNIQUE COMMENT '手机号码',
|
|||
|
|
status ENUM('active', 'inactive', 'banned') DEFAULT 'active' COMMENT '用户状态',
|
|||
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|||
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
|||
|
|
|
|||
|
|
INDEX idx_openid (openid),
|
|||
|
|
INDEX idx_phone (phone),
|
|||
|
|
INDEX idx_status (status),
|
|||
|
|
INDEX idx_created_at (created_at)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户基础信息表';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### SQL 查询规范
|
|||
|
|
```sql
|
|||
|
|
-- ✅ 推荐的查询写法
|
|||
|
|
SELECT
|
|||
|
|
u.id,
|
|||
|
|
u.nickname,
|
|||
|
|
u.avatar,
|
|||
|
|
u.created_at
|
|||
|
|
FROM users u
|
|||
|
|
WHERE u.status = 'active'
|
|||
|
|
AND u.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
|||
|
|
ORDER BY u.created_at DESC
|
|||
|
|
LIMIT 20;
|
|||
|
|
|
|||
|
|
-- ❌ 不推荐的查询写法
|
|||
|
|
select * from users where status='active' order by created_at desc;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. API 接口规范
|
|||
|
|
|
|||
|
|
#### RESTful API 设计
|
|||
|
|
```javascript
|
|||
|
|
// ✅ 推荐的API设计
|
|||
|
|
GET /api/v1/users // 获取用户列表
|
|||
|
|
GET /api/v1/users/{id} // 获取单个用户
|
|||
|
|
POST /api/v1/users // 创建用户
|
|||
|
|
PUT /api/v1/users/{id} // 更新用户
|
|||
|
|
DELETE /api/v1/users/{id} // 删除用户
|
|||
|
|
|
|||
|
|
GET /api/v1/users/{id}/travels // 获取用户的旅行计划
|
|||
|
|
POST /api/v1/travels // 创建旅行计划
|
|||
|
|
|
|||
|
|
// ❌ 不推荐的API设计
|
|||
|
|
GET /api/getUserList
|
|||
|
|
POST /api/createUser
|
|||
|
|
GET /api/user_travels?userId=123
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 统一响应格式
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": {
|
|||
|
|
"id": 1,
|
|||
|
|
"nickname": "张三",
|
|||
|
|
"avatar": "https://example.com/avatar.jpg"
|
|||
|
|
},
|
|||
|
|
"timestamp": "2025-01-15T10:30:00Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 错误响应格式
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 400,
|
|||
|
|
"message": "参数错误",
|
|||
|
|
"data": null,
|
|||
|
|
"errors": [
|
|||
|
|
{
|
|||
|
|
"field": "phone",
|
|||
|
|
"message": "手机号格式不正确"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"timestamp": "2025-01-15T10:30:00Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. Git 工作流规范
|
|||
|
|
|
|||
|
|
#### 分支管理
|
|||
|
|
```bash
|
|||
|
|
# 主要分支
|
|||
|
|
main # 生产环境分支
|
|||
|
|
develop # 开发环境分支
|
|||
|
|
release/* # 发布分支
|
|||
|
|
hotfix/* # 热修复分支
|
|||
|
|
|
|||
|
|
# 功能分支
|
|||
|
|
feature/user-management # 用户管理功能
|
|||
|
|
feature/travel-booking # 旅行预订功能
|
|||
|
|
feature/animal-adoption # 动物认领功能
|
|||
|
|
|
|||
|
|
# 修复分支
|
|||
|
|
bugfix/login-issue # 登录问题修复
|
|||
|
|
bugfix/payment-error # 支付错误修复
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 提交信息规范
|
|||
|
|
```bash
|
|||
|
|
# 提交信息格式
|
|||
|
|
<type>(<scope>): <subject>
|
|||
|
|
|
|||
|
|
<body>
|
|||
|
|
|
|||
|
|
<footer>
|
|||
|
|
|
|||
|
|
# 示例
|
|||
|
|
feat(user): 添加用户注册功能
|
|||
|
|
|
|||
|
|
- 实现微信授权登录
|
|||
|
|
- 添加用户信息完善页面
|
|||
|
|
- 集成短信验证码功能
|
|||
|
|
|
|||
|
|
Closes #123
|
|||
|
|
|
|||
|
|
# 类型说明
|
|||
|
|
feat: 新功能
|
|||
|
|
fix: 修复bug
|
|||
|
|
docs: 文档更新
|
|||
|
|
style: 代码格式调整
|
|||
|
|
refactor: 代码重构
|
|||
|
|
test: 测试相关
|
|||
|
|
chore: 构建工具或辅助工具的变动
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🧪 测试规范
|
|||
|
|
|
|||
|
|
### 1. 前端测试
|
|||
|
|
|
|||
|
|
#### 单元测试 (Jest)
|
|||
|
|
```javascript
|
|||
|
|
// utils/formatDate.test.js
|
|||
|
|
import { formatDate } from './formatDate';
|
|||
|
|
|
|||
|
|
describe('formatDate', () => {
|
|||
|
|
test('应该正确格式化日期', () => {
|
|||
|
|
const date = new Date('2025-01-15T10:30:00Z');
|
|||
|
|
const result = formatDate(date, 'YYYY-MM-DD');
|
|||
|
|
expect(result).toBe('2025-01-15');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('应该处理无效日期', () => {
|
|||
|
|
const result = formatDate(null, 'YYYY-MM-DD');
|
|||
|
|
expect(result).toBe('');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 组件测试 (Vue Test Utils)
|
|||
|
|
```javascript
|
|||
|
|
// components/UserProfile.test.js
|
|||
|
|
import { mount } from '@vue/test-utils';
|
|||
|
|
import UserProfile from './UserProfile.vue';
|
|||
|
|
|
|||
|
|
describe('UserProfile', () => {
|
|||
|
|
test('应该正确显示用户信息', () => {
|
|||
|
|
const userInfo = {
|
|||
|
|
nickname: '张三',
|
|||
|
|
avatar: 'https://example.com/avatar.jpg'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const wrapper = mount(UserProfile, {
|
|||
|
|
props: { userInfo }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
expect(wrapper.find('.username').text()).toBe('张三');
|
|||
|
|
expect(wrapper.find('image').attributes('src')).toBe('https://example.com/avatar.jpg');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 后端测试
|
|||
|
|
|
|||
|
|
#### 单元测试 (JUnit 5)
|
|||
|
|
```java
|
|||
|
|
@ExtendWith(MockitoExtension.class)
|
|||
|
|
class UserServiceTest {
|
|||
|
|
|
|||
|
|
@Mock
|
|||
|
|
private UserMapper userMapper;
|
|||
|
|
|
|||
|
|
@InjectMocks
|
|||
|
|
private UserService userService;
|
|||
|
|
|
|||
|
|
@Test
|
|||
|
|
@DisplayName("根据ID获取用户信息 - 成功")
|
|||
|
|
void getUserById_Success() {
|
|||
|
|
// Given
|
|||
|
|
Long userId = 1L;
|
|||
|
|
User mockUser = User.builder()
|
|||
|
|
.id(userId)
|
|||
|
|
.nickname("张三")
|
|||
|
|
.status(UserStatus.ACTIVE)
|
|||
|
|
.build();
|
|||
|
|
|
|||
|
|
when(userMapper.selectById(userId)).thenReturn(mockUser);
|
|||
|
|
|
|||
|
|
// When
|
|||
|
|
UserDTO result = userService.getUserById(userId);
|
|||
|
|
|
|||
|
|
// Then
|
|||
|
|
assertThat(result).isNotNull();
|
|||
|
|
assertThat(result.getId()).isEqualTo(userId);
|
|||
|
|
assertThat(result.getNickname()).isEqualTo("张三");
|
|||
|
|
|
|||
|
|
verify(userMapper).selectById(userId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Test
|
|||
|
|
@DisplayName("根据ID获取用户信息 - 用户不存在")
|
|||
|
|
void getUserById_UserNotFound() {
|
|||
|
|
// Given
|
|||
|
|
Long userId = 999L;
|
|||
|
|
when(userMapper.selectById(userId)).thenReturn(null);
|
|||
|
|
|
|||
|
|
// When & Then
|
|||
|
|
assertThatThrownBy(() -> userService.getUserById(userId))
|
|||
|
|
.isInstanceOf(BusinessException.class)
|
|||
|
|
.hasMessage("用户不存在");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 集成测试 (Spring Boot Test)
|
|||
|
|
```java
|
|||
|
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
|||
|
|
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
|||
|
|
@Transactional
|
|||
|
|
class UserControllerIntegrationTest {
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private TestRestTemplate restTemplate;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private UserMapper userMapper;
|
|||
|
|
|
|||
|
|
@Test
|
|||
|
|
@DisplayName("获取用户信息接口测试")
|
|||
|
|
void getUserById_Integration() {
|
|||
|
|
// Given - 准备测试数据
|
|||
|
|
User user = User.builder()
|
|||
|
|
.openid("test_openid")
|
|||
|
|
.nickname("测试用户")
|
|||
|
|
.status(UserStatus.ACTIVE)
|
|||
|
|
.build();
|
|||
|
|
userMapper.insert(user);
|
|||
|
|
|
|||
|
|
// When - 调用接口
|
|||
|
|
ResponseEntity<ApiResponse<UserDTO>> response = restTemplate.exchange(
|
|||
|
|
"/api/v1/users/" + user.getId(),
|
|||
|
|
HttpMethod.GET,
|
|||
|
|
null,
|
|||
|
|
new ParameterizedTypeReference<ApiResponse<UserDTO>>() {}
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Then - 验证结果
|
|||
|
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|||
|
|
assertThat(response.getBody()).isNotNull();
|
|||
|
|
assertThat(response.getBody().getCode()).isEqualTo(200);
|
|||
|
|
assertThat(response.getBody().getData().getNickname()).isEqualTo("测试用户");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🚀 开发流程
|
|||
|
|
|
|||
|
|
### 1. 功能开发流程
|
|||
|
|
|
|||
|
|
#### 步骤1: 需求分析
|
|||
|
|
1. 阅读产品需求文档
|
|||
|
|
2. 理解业务逻辑和用户场景
|
|||
|
|
3. 确认技术实现方案
|
|||
|
|
4. 评估开发工作量
|
|||
|
|
|
|||
|
|
#### 步骤2: 技术设计
|
|||
|
|
1. 设计数据库表结构
|
|||
|
|
2. 设计API接口
|
|||
|
|
3. 设计前端页面结构
|
|||
|
|
4. 确定技术选型
|
|||
|
|
|
|||
|
|
#### 步骤3: 开发实现
|
|||
|
|
```bash
|
|||
|
|
# 1. 创建功能分支
|
|||
|
|
git checkout develop
|
|||
|
|
git pull origin develop
|
|||
|
|
git checkout -b feature/user-profile
|
|||
|
|
|
|||
|
|
# 2. 后端开发
|
|||
|
|
# - 创建实体类和DTO
|
|||
|
|
# - 实现数据访问层
|
|||
|
|
# - 实现业务逻辑层
|
|||
|
|
# - 实现控制器层
|
|||
|
|
# - 编写单元测试
|
|||
|
|
|
|||
|
|
# 3. 前端开发
|
|||
|
|
# - 创建页面组件
|
|||
|
|
# - 实现业务逻辑
|
|||
|
|
# - 调用后端API
|
|||
|
|
# - 编写组件测试
|
|||
|
|
|
|||
|
|
# 4. 联调测试
|
|||
|
|
# - 启动后端服务
|
|||
|
|
# - 启动前端应用
|
|||
|
|
# - 测试完整功能流程
|
|||
|
|
|
|||
|
|
# 5. 提交代码
|
|||
|
|
git add .
|
|||
|
|
git commit -m "feat(user): 实现用户个人资料功能"
|
|||
|
|
git push origin feature/user-profile
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 步骤4: 代码审查
|
|||
|
|
1. 创建Pull Request
|
|||
|
|
2. 同事进行代码审查
|
|||
|
|
3. 修复审查意见
|
|||
|
|
4. 合并到develop分支
|
|||
|
|
|
|||
|
|
#### 步骤5: 测试验证
|
|||
|
|
1. 部署到测试环境
|
|||
|
|
2. 执行功能测试
|
|||
|
|
3. 执行回归测试
|
|||
|
|
4. 修复发现的问题
|
|||
|
|
|
|||
|
|
### 2. 本地开发环境启动
|
|||
|
|
|
|||
|
|
#### 后端服务启动
|
|||
|
|
```bash
|
|||
|
|
# 1. 启动基础服务
|
|||
|
|
docker-compose up -d mysql redis rabbitmq
|
|||
|
|
|
|||
|
|
# 2. 启动微服务 (按顺序启动)
|
|||
|
|
cd backend-java
|
|||
|
|
|
|||
|
|
# 启动服务注册中心
|
|||
|
|
cd eureka-server
|
|||
|
|
mvn spring-boot:run &
|
|||
|
|
|
|||
|
|
# 等待Eureka启动完成,然后启动其他服务
|
|||
|
|
cd ../gateway-service
|
|||
|
|
mvn spring-boot:run &
|
|||
|
|
|
|||
|
|
cd ../auth-service
|
|||
|
|
mvn spring-boot:run &
|
|||
|
|
|
|||
|
|
cd ../user-service
|
|||
|
|
mvn spring-boot:run &
|
|||
|
|
|
|||
|
|
# 或者使用脚本一键启动
|
|||
|
|
./start-services.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 前端应用启动
|
|||
|
|
```bash
|
|||
|
|
# 1. 启动微信小程序
|
|||
|
|
cd frontend/miniprogram
|
|||
|
|
npm install
|
|||
|
|
npm run dev
|
|||
|
|
|
|||
|
|
# 2. 启动管理后台
|
|||
|
|
cd ../admin-web
|
|||
|
|
npm install
|
|||
|
|
npm run serve
|
|||
|
|
|
|||
|
|
# 3. 启动官方网站
|
|||
|
|
cd ../website
|
|||
|
|
npm install
|
|||
|
|
npm run dev
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 调试技巧
|
|||
|
|
|
|||
|
|
#### 后端调试
|
|||
|
|
```java
|
|||
|
|
// 使用日志进行调试
|
|||
|
|
@Slf4j
|
|||
|
|
@Service
|
|||
|
|
public class UserService {
|
|||
|
|
|
|||
|
|
public UserDTO getUserById(Long userId) {
|
|||
|
|
log.debug("开始获取用户信息, userId: {}", userId);
|
|||
|
|
|
|||
|
|
User user = userMapper.selectById(userId);
|
|||
|
|
log.debug("查询到用户信息: {}", user);
|
|||
|
|
|
|||
|
|
if (user == null) {
|
|||
|
|
log.warn("用户不存在, userId: {}", userId);
|
|||
|
|
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UserDTO result = UserConverter.toDTO(user);
|
|||
|
|
log.debug("转换后的用户信息: {}", result);
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 前端调试
|
|||
|
|
```javascript
|
|||
|
|
// 使用console进行调试
|
|||
|
|
const getUserInfo = async (userId) => {
|
|||
|
|
console.log('开始获取用户信息, userId:', userId);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const response = await api.get(`/users/${userId}`);
|
|||
|
|
console.log('获取用户信息成功:', response.data);
|
|||
|
|
return response.data;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取用户信息失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 使用微信开发者工具调试
|
|||
|
|
wx.request({
|
|||
|
|
url: 'https://api.jiebanke.com/users/1',
|
|||
|
|
success: (res) => {
|
|||
|
|
console.log('请求成功:', res);
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('请求失败:', err);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📦 构建与部署
|
|||
|
|
|
|||
|
|
### 1. 本地构建
|
|||
|
|
|
|||
|
|
#### 前端构建
|
|||
|
|
```bash
|
|||
|
|
# 微信小程序构建
|
|||
|
|
cd frontend/miniprogram
|
|||
|
|
npm run build
|
|||
|
|
|
|||
|
|
# 管理后台构建
|
|||
|
|
cd ../admin-web
|
|||
|
|
npm run build
|
|||
|
|
|
|||
|
|
# 官方网站构建
|
|||
|
|
cd ../website
|
|||
|
|
npm run build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 后端构建
|
|||
|
|
```bash
|
|||
|
|
# Maven构建
|
|||
|
|
cd backend-java
|
|||
|
|
mvn clean package -DskipTests
|
|||
|
|
|
|||
|
|
# Docker构建
|
|||
|
|
./build-services.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 容器化部署
|
|||
|
|
```bash
|
|||
|
|
# 构建Docker镜像
|
|||
|
|
docker-compose build
|
|||
|
|
|
|||
|
|
# 启动所有服务
|
|||
|
|
docker-compose up -d
|
|||
|
|
|
|||
|
|
# 查看服务状态
|
|||
|
|
docker-compose ps
|
|||
|
|
|
|||
|
|
# 查看服务日志
|
|||
|
|
docker-compose logs -f user-service
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔧 开发工具配置
|
|||
|
|
|
|||
|
|
### 1. VS Code 配置
|
|||
|
|
|
|||
|
|
#### 推荐插件
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"recommendations": [
|
|||
|
|
"ms-vscode.vscode-typescript-next",
|
|||
|
|
"vue.volar",
|
|||
|
|
"esbenp.prettier-vscode",
|
|||
|
|
"dbaeumer.vscode-eslint",
|
|||
|
|
"ms-vscode.vscode-json",
|
|||
|
|
"redhat.java",
|
|||
|
|
"vscjava.vscode-spring-boot-dashboard",
|
|||
|
|
"ms-python.python"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作区设置
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"editor.formatOnSave": true,
|
|||
|
|
"editor.codeActionsOnSave": {
|
|||
|
|
"source.fixAll.eslint": true
|
|||
|
|
},
|
|||
|
|
"eslint.workingDirectories": [
|
|||
|
|
"frontend/miniprogram",
|
|||
|
|
"frontend/admin-web",
|
|||
|
|
"frontend/website"
|
|||
|
|
],
|
|||
|
|
"java.configuration.workspaces": [
|
|||
|
|
"backend-java"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. IntelliJ IDEA 配置
|
|||
|
|
|
|||
|
|
#### 代码格式化
|
|||
|
|
1. 导入项目代码风格配置
|
|||
|
|
2. 启用自动格式化
|
|||
|
|
3. 配置Import优化
|
|||
|
|
|
|||
|
|
#### 插件推荐
|
|||
|
|
- Lombok
|
|||
|
|
- MyBatis Log Plugin
|
|||
|
|
- RestfulTool
|
|||
|
|
- Maven Helper
|
|||
|
|
- Git Commit Template
|
|||
|
|
|
|||
|
|
## 📚 学习资源
|
|||
|
|
|
|||
|
|
### 官方文档
|
|||
|
|
- [微信小程序开发文档](https://developers.weixin.qq.com/miniprogram/dev/framework/)
|
|||
|
|
- [Vue.js 官方文档](https://vuejs.org/)
|
|||
|
|
- [Spring Boot 官方文档](https://spring.io/projects/spring-boot)
|
|||
|
|
- [MyBatis 官方文档](https://mybatis.org/mybatis-3/)
|
|||
|
|
|
|||
|
|
### 推荐书籍
|
|||
|
|
- 《JavaScript高级程序设计》
|
|||
|
|
- 《Vue.js设计与实现》
|
|||
|
|
- 《Spring Boot实战》
|
|||
|
|
- 《Java并发编程实战》
|
|||
|
|
|
|||
|
|
### 在线课程
|
|||
|
|
- [Vue.js 从入门到精通](https://example.com)
|
|||
|
|
- [Spring Boot 微服务开发](https://example.com)
|
|||
|
|
- [MySQL 数据库优化](https://example.com)
|
|||
|
|
|
|||
|
|
## 🤝 团队协作
|
|||
|
|
|
|||
|
|
### 1. 代码审查清单
|
|||
|
|
|
|||
|
|
#### 前端代码审查
|
|||
|
|
- [ ] 代码格式是否符合规范
|
|||
|
|
- [ ] 组件是否可复用
|
|||
|
|
- [ ] 是否有内存泄漏风险
|
|||
|
|
- [ ] 错误处理是否完善
|
|||
|
|
- [ ] 用户体验是否良好
|
|||
|
|
|
|||
|
|
#### 后端代码审查
|
|||
|
|
- [ ] 业务逻辑是否正确
|
|||
|
|
- [ ] 异常处理是否完善
|
|||
|
|
- [ ] 数据库操作是否高效
|
|||
|
|
- [ ] 安全性是否考虑
|
|||
|
|
- [ ] 日志记录是否完整
|
|||
|
|
|
|||
|
|
### 2. 沟通协作
|
|||
|
|
|
|||
|
|
#### 日常沟通
|
|||
|
|
- 每日站会:同步开发进度和问题
|
|||
|
|
- 技术分享:定期分享技术心得
|
|||
|
|
- 代码审查:互相学习和改进
|
|||
|
|
|
|||
|
|
#### 文档维护
|
|||
|
|
- 及时更新API文档
|
|||
|
|
- 记录重要的技术决策
|
|||
|
|
- 维护常见问题解答
|
|||
|
|
|
|||
|
|
## 🆘 常见问题
|
|||
|
|
|
|||
|
|
### 1. 环境问题
|
|||
|
|
|
|||
|
|
**Q: Node.js 版本不兼容怎么办?**
|
|||
|
|
A: 使用 nvm 管理 Node.js 版本
|
|||
|
|
```bash
|
|||
|
|
# 安装 nvm
|
|||
|
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
|
|||
|
|
|
|||
|
|
# 安装并使用指定版本
|
|||
|
|
nvm install 18
|
|||
|
|
nvm use 18
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Q: Java 版本冲突怎么办?**
|
|||
|
|
A: 使用 JAVA_HOME 环境变量指定版本
|
|||
|
|
```bash
|
|||
|
|
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
|
|||
|
|
export PATH=$JAVA_HOME/bin:$PATH
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 开发问题
|
|||
|
|
|
|||
|
|
**Q: 微信小程序真机调试问题?**
|
|||
|
|
A: 检查以下几点:
|
|||
|
|
1. 确认服务器域名已配置
|
|||
|
|
2. 检查HTTPS证书是否有效
|
|||
|
|
3. 确认API接口返回格式正确
|
|||
|
|
|
|||
|
|
**Q: 后端服务启动失败?**
|
|||
|
|
A: 常见原因和解决方案:
|
|||
|
|
1. 端口被占用:`lsof -i :8080` 查看占用进程
|
|||
|
|
2. 数据库连接失败:检查数据库配置和网络
|
|||
|
|
3. 依赖冲突:清理Maven缓存重新构建
|
|||
|
|
|
|||
|
|
### 3. 部署问题
|
|||
|
|
|
|||
|
|
**Q: Docker 容器启动失败?**
|
|||
|
|
A: 查看容器日志定位问题
|
|||
|
|
```bash
|
|||
|
|
docker logs container_name
|
|||
|
|
docker-compose logs service_name
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Q: 数据库迁移问题?**
|
|||
|
|
A: 使用Flyway进行版本化管理
|
|||
|
|
```sql
|
|||
|
|
-- V1__Create_users_table.sql
|
|||
|
|
CREATE TABLE users (
|
|||
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|||
|
|
nickname VARCHAR(50) NOT NULL,
|
|||
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
### 联系方式
|
|||
|
|
- **技术负责人**: tech-lead@jiebanke.com
|
|||
|
|
- **开发团队**: dev@jiebanke.com
|
|||
|
|
- **技术文档**: https://docs.jiebanke.com
|
|||
|
|
- **问题反馈**: https://github.com/jiebanke/issues
|
|||
|
|
|
|||
|
|
### 紧急联系
|
|||
|
|
- **生产环境问题**: 24小时技术热线
|
|||
|
|
- **安全问题**: security@jiebanke.com
|
|||
|
|
- **数据问题**: dba@jiebanke.com
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*文档版本:v1.0*
|
|||
|
|
*最后更新:2025年1月*
|
|||
|
|
*维护团队:结伴客技术团队*
|