Skip to content

DSPlatform 开发指南

概述

DSPlatform 是一个基于现代化技术栈构建的多端电商管理系统,支持 PC 管理后台、H5、微信小程序、APP 等多个平台。本指南将帮助开发者快速上手 DSPlatform 的开发工作。

系统架构

技术栈

后端技术

  • 框架: ThinkPHP 8.0
  • 语言: PHP 8.0+
  • 数据库: MySQL 8.0+
  • 缓存: Redis 6.0+
  • 文件存储: 本地存储 / 阿里云 OSS

前端技术

  • 管理后台: Vue 3 + Element Plus + Vite
  • 移动端: UniApp + Vue 3
  • 构建工具: Vite 5.2+
  • 状态管理: Pinia
  • HTTP 客户端: Axios
  • 样式: SCSS + Tailwind CSS

第三方集成

  • 支付: 微信支付、支付宝
  • 短信: 阿里云短信、腾讯云短信
  • 地图: 腾讯地图、高德地图、百度地图

开发环境搭建

环境要求

开发工具

  • IDE: VS Code / PhpStorm / WebStorm
  • PHP: 8.0+ (推荐 8.1+)
  • Node.js: 16.0+ (推荐 18.0+)
  • MySQL: 8.0+
  • Redis: 6.0+
  • Git: 2.0+

PHP 扩展要求

bash
# 必需扩展
php-mysql
php-redis
php-gd
php-curl
php-mbstring
php-xml
php-zip
php-openssl
php-fileinfo
php-json
php-tokenizer
php-pdo
php-bcmath
php-intl

项目初始化

1. 获取源码

bash
# 克隆项目
git clone https://github.com/your-org/dsplatform.git
cd dsplatform

# 安装后端依赖
cd php-server
composer install

# 安装前端依赖
cd ../vue-element-admin
npm install

cd ../uniapp
npm install

2. 开发环境配置

后端配置 (php-server/.env):

env
# 开发环境配置
APP_DEBUG = true
APP_TRACE = true

# 数据库配置
DATABASE_TYPE = mysql
DATABASE_HOSTNAME = 127.0.0.1
DATABASE_DATABASE = dsplatform_dev
DATABASE_USERNAME = root
DATABASE_PASSWORD = your_password
DATABASE_HOSTPORT = 3306
DATABASE_CHARSET = utf8mb4
DATABASE_PREFIX = ds_

# Redis 配置
REDIS_HOSTNAME = 127.0.0.1
REDIS_PORT = 6379
REDIS_PASSWORD = 
REDIS_SELECT = 0

# JWT 配置
JWT_SECRET_KEY = dev_jwt_secret_key_here

前端配置 (vue-element-admin/.env.development):

env
# API 基础地址
VITE_API_BASE_URL=http://localhost:8000

# 应用标题
VITE_APP_TITLE=DSPlatform管理后台

# 是否开启代理
VITE_USE_PROXY=false

移动端配置 (uniapp/.env.development):

env
# API 基础地址
VITE_API_BASE_URL=http://localhost:8000

# 应用标题
VITE_APP_TITLE=DSPlatform

# 微信小程序 AppID
VITE_WECHAT_APPID=your_wechat_appid

开发服务器启动

后端服务

bash
cd php-server

# 使用 PHP 内置服务器(开发环境)
php think run

# 或使用 Nginx + PHP-FPM
# 配置虚拟主机指向 public 目录

管理后台

bash
cd vue-element-admin

# 启动开发服务器
npm run dev

# 访问地址: http://localhost:3000

移动端

bash
cd uniapp

# H5 开发
npm run dev:h5

# 微信小程序开发
npm run dev:mp-weixin

# APP 开发
npm run dev:app

开发规范

代码规范

PHP 代码规范

  • 遵循 PSR-12 编码规范
  • 使用 PSR-4 自动加载规范
  • 类名使用 PascalCase
  • 方法名和变量名使用 camelCase
  • 常量使用 UPPER_SNAKE_CASE

JavaScript/Vue 代码规范

  • 使用 ESLint + Prettier 格式化
  • 遵循 Vue 3 Composition API 规范
  • 使用 TypeScript 进行类型检查
  • 组件名使用 PascalCase
  • 文件名使用 kebab-case

项目结构

后端项目结构

php-server/
├── app/                    # 应用目录
│   ├── adminapi/          # 管理端API
│   ├── api/               # 用户端API
│   ├── merchantapi/       # 商户端API
│   ├── storeapi/          # 店铺端API
│   ├── riderapi/          # 骑手端API
│   ├── techapi/           # 技师端API
│   ├── bloggerapi/        # 博主端API
│   ├── common/            # 公共模块
│   ├── deshang/           # 核心业务模块
│   └── crontab/           # 定时任务
├── config/                 # 配置文件
├── public/                 # 公共资源
├── route/                  # 路由定义
├── vendor/                 # 第三方库
└── runtime/                # 运行时文件

前端项目结构

vue-element-admin/
├── src/
│   ├── api/               # API 接口
│   ├── assets/            # 静态资源
│   ├── components/        # 公共组件
│   ├── hooks/             # 组合式函数
│   ├── layout/            # 布局组件
│   ├── pages-admin/       # 管理端页面
│   ├── pages-store/       # 店铺端页面
│   ├── pages-merchant/    # 商户端页面
│   ├── pages-consumer/    # 用户端页面
│   ├── router/            # 路由配置
│   ├── stores/            # 状态管理
│   ├── styles/            # 样式文件
│   └── utils/             # 工具函数
├── public/                 # 公共资源
└── dist/                   # 构建输出

移动端项目结构

uniapp/
├── src/
│   ├── api/               # API 接口
│   ├── components/        # 公共组件
│   ├── home/              # 用户端模块
│   ├── merchant/          # 商户端模块
│   ├── store/             # 店铺端模块
│   ├── rider/             # 骑手端模块
│   ├── technician/        # 技师端模块
│   ├── video/             # 视频端模块
│   ├── shared/            # 共享模块
│   ├── stores/            # 状态管理
│   ├── utils/             # 工具函数
│   └── styles/            # 样式文件
├── static/                 # 静态资源
└── unpackage/              # 构建输出

Git 工作流

分支管理

  • main: 主分支,用于生产环境
  • develop: 开发分支,用于集成开发
  • feature/*: 功能分支,用于新功能开发
  • hotfix/*: 热修复分支,用于紧急修复

提交规范

bash
# 提交格式
<type>(<scope>): <subject>

# 类型说明
feat:     新功能
fix:      修复问题
docs:     文档更新
style:    代码格式调整
refactor: 代码重构
test:     测试相关
chore:    构建过程或辅助工具的变动

# 示例
feat(user): 添加用户注册功能
fix(order): 修复订单支付问题
docs(api): 更新API文档

数据库规范

表命名规范

  • 表名使用 ds_ 前缀
  • 表名使用小写字母和下划线
  • 字段名使用小写字母和下划线
  • 主键统一使用 id
  • 创建时间字段使用 create_at
  • 更新时间字段使用 update_at
  • 软删除字段使用 is_deleted

索引规范

  • 主键索引:PRIMARY KEY
  • 唯一索引:UNIQUE KEY
  • 普通索引:KEY
  • 外键索引:FOREIGN KEY

开发指南

后端开发

API 开发

控制器开发:

php
// app/api/controller/user/User.php
namespace app\api\controller\user;

use app\deshang\base\controller\BaseUserController;
use app\api\service\user\UserService;

class User extends BaseUserController
{
    /**
     * 获取用户信息
     * @OA\Get(
     *     path="/api/user/user/info",
     *     tags={"api/user/User"},
     *     summary="获取用户信息",
     *     description="获取当前登录用户的基础信息",
     *     @OA\Response(
     *         response=200,
     *         description="操作成功",
     *         @OA\JsonContent(
     *             @OA\Property(property="code", type="integer", example=10000),
     *             @OA\Property(property="msg", type="string", example="操作成功"),
     *             @OA\Property(property="data", type="object")
     *         )
     *     )
     * )
     */
    public function getUserInfo()
    {
        $result = (new UserService())->getUserInfo();
        return ds_json_success('操作成功', $result);
    }
}

服务层开发:

php
// app/api/service/user/UserService.php
namespace app\api\service\user;

use app\deshang\base\service\BaseUserService;
use app\common\dao\user\UserDao;

class UserService extends BaseUserService
{
    public function getUserInfo()
    {
        $user_info = (new UserDao())->getUserInfo(['id' => $this->user_id]);
        unset($user_info['password']);
        unset($user_info['pay_password']);
        return $user_info;
    }
}

路由配置:

php
// app/api/route/user.php
use think\facade\Route;
use app\api\middleware\UserAuthorizeToken;
use app\api\middleware\UserAuthorizeLog;

Route::group('user', function () {
    Route::get('user/info', 'user.User/getUserInfo');
    Route::post('user/info', 'user.User/updateUserInfo');
})->middleware([
    UserAuthorizeToken::class,
    UserAuthorizeLog::class,
]);

数据库开发

模型开发:

php
// app/common/model/user/UserModel.php
namespace app\common\model\user;

use app\deshang\base\BaseModel;

class UserModel extends BaseModel
{
    protected $table = 'ds_user';
    
    protected $hidden = ['password', 'pay_password'];
    
    protected $append = ['user_status_desc'];
    
    public function getUserStatusDescAttr($value, $data)
    {
        $status = [
            0 => '禁用',
            1 => '启用',
        ];
        return $status[$data['is_enabled']] ?? '未知';
    }
}

DAO 开发:

php
// app/common/dao/user/UserDao.php
namespace app\common\dao\user;

use app\common\dao\BaseDao;
use app\common\model\user\UserModel;

class UserDao extends BaseDao
{
    public function __construct()
    {
        parent::__construct();
        $this->model = new UserModel();
    }
    
    public function getUserInfo($condition)
    {
        return $this->model->where($condition)->findOrEmpty()->toArray();
    }
}

前端开发

管理后台开发

页面组件:

vue
<!-- src/pages-admin/user/UserList.vue -->
<template>
  <div class="user-list">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>用户列表</span>
          <el-button type="primary" @click="handleAdd">添加用户</el-button>
        </div>
      </template>
      
      <el-table :data="tableData" style="width: 100%" v-loading="loading">
        <el-table-column prop="username" label="用户名" />
        <el-table-column prop="mobile" label="手机号" />
        <el-table-column prop="user_status_desc" label="状态" />
        <el-table-column prop="create_at" label="创建时间" />
        <el-table-column label="操作" width="200">
          <template #default="scope">
            <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      
      <el-pagination
        v-model:current-page="pagination.page"
        v-model:page-size="pagination.size"
        :total="pagination.total"
        @current-change="handlePageChange"
        @size-change="handleSizeChange"
      />
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getUserList, deleteUser } from '@/api/user'

const loading = ref(false)
const tableData = ref([])
const pagination = ref({
  page: 1,
  size: 10,
  total: 0
})

const fetchData = async () => {
  loading.value = true
  try {
    const res = await getUserList({
      page: pagination.value.page,
      size: pagination.value.size
    })
    tableData.value = res.data.list
    pagination.value.total = res.data.total
  } catch (error) {
    ElMessage.error('获取数据失败')
  } finally {
    loading.value = false
  }
}

const handleAdd = () => {
  // 添加用户逻辑
}

const handleEdit = (row: any) => {
  // 编辑用户逻辑
}

const handleDelete = async (row: any) => {
  try {
    await ElMessageBox.confirm('确定删除该用户吗?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
    
    await deleteUser(row.id)
    ElMessage.success('删除成功')
    fetchData()
  } catch (error) {
    // 用户取消删除
  }
}

const handlePageChange = (page: number) => {
  pagination.value.page = page
  fetchData()
}

const handleSizeChange = (size: number) => {
  pagination.value.size = size
  fetchData()
}

onMounted(() => {
  fetchData()
})
</script>

API 接口:

typescript
// src/api/user.ts
import { request } from '@/utils/request'

export interface User {
  id: number
  username: string
  mobile: string
  is_enabled: number
  create_at: string
}

export interface UserListParams {
  page: number
  size: number
  username?: string
  mobile?: string
}

export const getUserList = (params: UserListParams) => {
  return request.get('/adminapi/user/list', { params })
}

export const getUserInfo = (id: number) => {
  return request.get(`/adminapi/user/info/${id}`)
}

export const createUser = (data: Partial<User>) => {
  return request.post('/adminapi/user/create', data)
}

export const updateUser = (id: number, data: Partial<User>) => {
  return request.put(`/adminapi/user/update/${id}`, data)
}

export const deleteUser = (id: number) => {
  return request.delete(`/adminapi/user/delete/${id}`)
}

移动端开发

页面组件:

vue
<!-- src/home/pages/index/index.vue -->
<template>
  <view class="home">
    <!-- 轮播图 -->
    <view class="banner">
      <swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
        <swiper-item v-for="item in banners" :key="item.id">
          <image :src="item.image" mode="aspectFill" class="banner-image" />
        </swiper-item>
      </swiper>
    </view>
    
    <!-- 分类导航 -->
    <view class="category-nav">
      <view 
        class="category-item" 
        v-for="item in categories" 
        :key="item.id"
        @click="goToCategory(item)"
      >
        <image :src="item.icon" class="category-icon" />
        <text class="category-name">{{ item.name }}</text>
      </view>
    </view>
    
    <!-- 商品列表 -->
    <view class="goods-list">
      <view 
        class="goods-item" 
        v-for="item in goodsList" 
        :key="item.id"
        @click="goToGoods(item)"
      >
        <image :src="item.image" class="goods-image" />
        <view class="goods-info">
          <text class="goods-name">{{ item.name }}</text>
          <text class="goods-price">¥{{ item.price }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getBannerList, getCategoryList, getGoodsList } from '@/api/home'

const banners = ref([])
const categories = ref([])
const goodsList = ref([])

const fetchBanners = async () => {
  try {
    const res = await getBannerList()
    banners.value = res.data
  } catch (error) {
    console.error('获取轮播图失败:', error)
  }
}

const fetchCategories = async () => {
  try {
    const res = await getCategoryList()
    categories.value = res.data
  } catch (error) {
    console.error('获取分类失败:', error)
  }
}

const fetchGoods = async () => {
  try {
    const res = await getGoodsList({ page: 1, size: 10 })
    goodsList.value = res.data.list
  } catch (error) {
    console.error('获取商品失败:', error)
  }
}

const goToCategory = (category: any) => {
  uni.navigateTo({
    url: `/pages/category/index?id=${category.id}`
  })
}

const goToGoods = (goods: any) => {
  uni.navigateTo({
    url: `/pages/goods/detail?id=${goods.id}`
  })
}

onMounted(() => {
  fetchBanners()
  fetchCategories()
  fetchGoods()
})
</script>

<style lang="scss" scoped>
.home {
  padding: 20rpx;
}

.banner {
  height: 300rpx;
  margin-bottom: 20rpx;
  
  .banner-image {
    width: 100%;
    height: 100%;
  }
}

.category-nav {
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 20rpx;
  
  .category-item {
    width: 25%;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20rpx 0;
    
    .category-icon {
      width: 80rpx;
      height: 80rpx;
      margin-bottom: 10rpx;
    }
    
    .category-name {
      font-size: 24rpx;
      color: #333;
    }
  }
}

.goods-list {
  display: flex;
  flex-wrap: wrap;
  
  .goods-item {
    width: 48%;
    margin: 1%;
    background: #fff;
    border-radius: 10rpx;
    overflow: hidden;
    
    .goods-image {
      width: 100%;
      height: 200rpx;
    }
    
    .goods-info {
      padding: 20rpx;
      
      .goods-name {
        font-size: 28rpx;
        color: #333;
        margin-bottom: 10rpx;
      }
      
      .goods-price {
        font-size: 32rpx;
        color: #ff4444;
        font-weight: bold;
      }
    }
  }
}
</style>

API 接口:

typescript
// src/api/home.ts
import { request } from '@/utils/request'

export interface Banner {
  id: number
  title: string
  image: string
  link: string
}

export interface Category {
  id: number
  name: string
  icon: string
}

export interface Goods {
  id: number
  name: string
  image: string
  price: number
  original_price: number
}

export const getBannerList = () => {
  return request.get('/api/home/banner/list')
}

export const getCategoryList = () => {
  return request.get('/api/home/category/list')
}

export const getGoodsList = (params: { page: number; size: number }) => {
  return request.get('/api/home/goods/list', { params })
}

测试与调试

单元测试

后端测试

bash
# 安装测试依赖
cd php-server
composer require --dev phpunit/phpunit

# 运行测试
./vendor/bin/phpunit tests/

前端测试

bash
# 管理后台测试
cd vue-element-admin
npm run test:unit

# 移动端测试
cd uniapp
npm run test:unit

接口测试

Swagger 文档

bash
# 访问 Swagger 文档
http://localhost:8000/swagger

# 使用 Swagger UI 测试 API
# 支持在线调试和文档查看

Postman 测试

bash
# 导入 Swagger 文档到 Postman
# 创建测试集合
# 设置环境变量
# 运行自动化测试

功能测试

用户功能测试

  • 注册登录: 测试用户注册、登录、退出功能
  • 个人信息: 测试用户信息修改、密码修改功能
  • 地址管理: 测试地址添加、编辑、删除功能

商品功能测试

  • 商品浏览: 测试商品列表、详情、搜索功能
  • 购物车: 测试商品加入购物车、数量修改功能
  • 收藏功能: 测试商品收藏、取消收藏功能

订单功能测试

  • 下单流程: 测试商品下单、地址选择、支付功能
  • 订单管理: 测试订单查看、取消、确认收货功能
  • 退款功能: 测试订单退款申请、处理功能

调试技巧

后端调试

php
// 使用 dd() 函数调试
dd($variable);

// 使用日志记录
\think\facade\Log::info('调试信息', $data);

// 使用数据库查询日志
\think\facade\Db::listen(function ($sql, $time, $explain) {
    // 记录 SQL 查询
});

前端调试

javascript
// Vue DevTools 调试
// 浏览器开发者工具
// 网络请求监控
// 控制台日志输出

// 移动端调试
// H5 端使用浏览器开发者工具
// 小程序端使用微信开发者工具
// APP 端使用真机调试

性能优化

后端优化

数据库优化

php
// 使用索引优化查询
// 避免 N+1 查询问题
// 使用分页查询
// 缓存热点数据

// 示例:优化商品列表查询
public function getGoodsList($params)
{
    return $this->model
        ->with(['category', 'store'])
        ->where('is_enabled', 1)
        ->order('create_at', 'desc')
        ->paginate($params['size']);
}

缓存优化

php
// 使用 Redis 缓存
// 设置合理的缓存时间
// 使用缓存标签
// 避免缓存穿透

// 示例:缓存商品信息
public function getGoodsInfo($id)
{
    $cache_key = CacheUtil::GOODS_INFO_KEY . $id;
    $goods_info = CacheUtil::get($cache_key);
    
    if (!$goods_info) {
        $goods_info = $this->model->find($id);
        CacheUtil::set($cache_key, $goods_info, 3600, CacheUtil::GOODS_TAG);
    }
    
    return $goods_info;
}

前端优化

代码分割

javascript
// 路由懒加载
const routes = [
  {
    path: '/user',
    component: () => import('@/pages/user/index.vue')
  }
]

// 组件懒加载
const UserList = defineAsyncComponent(() => import('./UserList.vue'))

图片优化

vue
<!-- 使用懒加载 -->
<image 
  :src="item.image" 
  lazy-load
  mode="aspectFill"
/>

<!-- 使用 WebP 格式 -->
<image 
  :src="item.image + '?format=webp'" 
  mode="aspectFill"
/>

常见问题

开发环境问题

Q: Composer 安装失败

bash
# 解决方案
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer install

Q: Node.js 依赖安装失败

bash
# 解决方案
npm config set registry https://registry.npmmirror.com
npm install

Q: 数据库连接失败

bash
# 检查数据库配置
# 确保 MySQL 服务正在运行
# 检查防火墙设置

代码问题

Q: API 接口返回 500 错误

bash
# 检查 PHP 错误日志
tail -f /var/log/php-fpm/error.log

# 检查应用日志
tail -f runtime/log/error.log

# 开启调试模式
APP_DEBUG = true

Q: 前端页面空白

bash
# 检查浏览器控制台错误
# 检查网络请求
# 检查路由配置
# 检查组件导入

性能问题

Q: 页面加载缓慢

bash
# 开启 Gzip 压缩
# 使用 CDN 加速
# 优化图片大小
# 减少 HTTP 请求

Q: 数据库查询慢

bash
# 添加数据库索引
# 优化 SQL 查询
# 使用缓存
# 分页查询

开发工具推荐

IDE 推荐

  • VS Code: 免费,插件丰富,支持多语言
  • PhpStorm: PHP 开发首选,功能强大
  • WebStorm: 前端开发首选,支持 Vue/React

浏览器插件

  • Vue DevTools: Vue 开发调试工具
  • Redux DevTools: 状态管理调试工具
  • Postman: API 接口测试工具

移动端调试

  • 微信开发者工具: 小程序开发调试
  • HBuilderX: UniApp 开发调试
  • Chrome DevTools: H5 页面调试

相关文档

技术文档

开发指南

部署文档


最后更新:2024-01-20
维护者:DSPlatform技术团队