2025.02 / 商城小程序

文山农业公司冻品商城小程序

面向文山某农业公司冻品销售业务做的商城小程序,重点是商品分类、下单流程、门店配送和基础商品展示。

文山农业公司冻品商城小程序

技术栈

微信小程序 / PHP / MySQL / 商品管理 / 订单

项目说明

项目截图

文山农业公司冻品商城小程序

文山农业公司冻品商城小程序开发记录:从小程序到后台管理的全栈项目实践

一、项目背景

在工作期间,我为文山本地一家农业公司独立开发了一套冻品商城小程序。这个项目不是课程练习,也不是单纯为了展示技术而做的 Demo,而是一个面向公司真实业务场景的线上商城系统。

公司主要经营冻品类商品,包括肉类冻品、海鲜冻品、速冻食品、半成品菜以及部分农产品。原来的销售方式比较传统,主要依靠线下沟通、微信群发商品图片、人工统计订单、电话确认数量等方式完成。随着客户数量增加,这种方式逐渐暴露出一些问题。

首先,商品信息维护不方便。商品价格、库存、规格经常需要更新,如果只靠微信群或人工通知,很容易出现信息不同步的情况。

其次,客户下单流程不统一。有的客户通过微信发消息下单,有的通过电话下单,有的直接截图确认商品,后期统计时容易遗漏或出错。

再次,订单管理效率较低。订单需要人工整理,商品数量、客户信息、订单状态都需要反复核对,工作量较大。

因此,公司需要一个更加规范的线上系统,用来展示商品、接收订单、管理商品和维护客户信息。基于这个实际业务需求,我从零开始设计并开发了这套冻品商城小程序。

整个项目由我独立负责,包括需求分析、页面设计、小程序端开发、后端接口开发、数据库设计、管理员后台开发、服务器部署以及上线后的日常运维。

二、项目简介

文山农业公司冻品商城小程序是一套面向冻品销售场景的线上商城系统,主要由两个部分组成:客户使用的小程序端和公司内部使用的管理员后台。

小程序端主要面向客户,客户可以在微信中打开小程序,浏览冻品商品,查看商品详情,加入购物车,填写收货信息并提交订单。提交订单后,客户可以在订单页面查看自己的订单状态。

管理员后台主要面向公司内部人员,管理员可以通过网页后台维护商品信息、调整商品价格、管理库存、处理订单、维护商品分类和配置首页轮播图。这样公司就可以把原来依赖人工沟通和手工统计的流程转移到系统中完成。

项目整体采用前后端分离思路,小程序端负责页面展示和用户交互,后端负责接口数据处理和业务逻辑,MySQL 数据库负责保存用户、商品、订单、分类、轮播图等核心数据,管理员后台负责业务管理和运营维护。

三、技术选型

这个项目的技术选型以稳定、实用、便于维护为主,没有过度追求复杂架构,而是围绕公司实际业务需求进行开发。

项目主要技术包括:

微信小程序
PHP 后端接口
MySQL 数据库
HTML
CSS
JavaScript
管理员 Web 后台
Nginx
Linux 服务器

微信小程序作为客户使用入口,优势是使用方便,不需要额外安装 App,客户通过微信就可以完成商品浏览和下单。

PHP 负责后端接口开发和后台管理页面逻辑,主要处理商品查询、订单提交、订单管理、用户信息维护等功能。

MySQL 用于保存系统业务数据,包括商品信息、分类信息、订单信息、订单详情、用户信息和管理员信息等。

管理员后台使用 HTML、CSS、JavaScript 和 PHP 实现,方便公司内部人员通过浏览器进行日常管理。

服务器部署方面,我负责配置运行环境、数据库、Nginx、接口访问路径和后续运维工作,保证系统能够正常访问和稳定运行。

四、系统功能设计

整个冻品商城系统主要分为小程序用户端和管理员后台端。

小程序用户端主要功能包括:

首页轮播图展示
商品分类浏览
商品列表展示
商品详情查看
商品规格与价格展示
加入购物车
购物车商品数量调整
订单金额计算
填写收货信息
提交订单
查看我的订单
查看订单状态
个人中心

管理员后台主要功能包括:

管理员登录
后台首页数据统计
商品分类管理
商品信息管理
商品图片管理
商品价格维护
商品库存维护
商品上下架管理
订单列表查看
订单详情查看
订单状态修改
用户信息查看
首页轮播图管理
系统基础数据维护

整个系统的业务流程比较清晰:客户在小程序端浏览商品并提交订单,订单数据进入数据库,管理员在后台查看订单并进行处理。商品信息和库存由管理员在后台维护,小程序端实时读取后端接口返回的数据进行展示。

五、数据库设计

数据库设计是商城系统中非常重要的一部分。因为商城系统涉及商品、分类、用户、购物车、订单、订单详情等多类数据,如果表结构设计不清晰,后期维护和扩展都会比较麻烦。

本项目主要设计了以下几类核心数据表:

用户表
管理员表
商品分类表
商品表
订单表
订单详情表
轮播图表
收货地址表

用户表主要保存客户信息,例如用户编号、微信 openid、昵称、手机号、注册时间等。

管理员表主要保存后台管理员账号和密码,用于后台登录验证。

商品分类表主要保存冻品商品分类,例如肉类冻品、海鲜冻品、速冻食品、半成品菜等。

商品表主要保存商品名称、图片、价格、库存、规格、描述、分类、上下架状态等信息。

订单表主要保存订单主信息,例如订单编号、用户 ID、订单金额、收货信息、联系电话、订单状态和下单时间。

订单详情表主要保存订单中的具体商品信息,例如商品 ID、商品名称、购买数量、商品单价和小计金额。

轮播图表用于维护小程序首页展示图片,方便后期做商品推荐或活动展示。

收货地址表用于保存用户常用收货信息,方便客户重复下单时快速填写。

订单表和订单详情表采用主从结构设计。订单表保存一笔订单的整体信息,订单详情表保存这笔订单中包含的每一种商品。这样既方便后台查看订单,也方便后期统计商品销售情况。

六、小程序端开发

小程序端是客户直接使用的部分,所以页面设计重点是简单、清晰、下单方便。

首页主要展示轮播图、商品分类入口和推荐商品。客户进入小程序后,可以快速看到当前主推商品和常用分类。

商品分类页按照不同冻品类型展示商品,用户点击分类后,可以查看该分类下的商品列表。每个商品展示商品图片、商品名称、价格、规格和库存状态。

商品详情页用于展示更完整的商品信息,包括商品大图、价格、规格、库存、商品介绍等。用户可以在详情页选择数量并加入购物车。

购物车页面用于集中展示用户已经选择的商品。用户可以增加数量、减少数量或删除商品,系统会自动计算订单总金额。

订单确认页面用于填写收货人姓名、联系电话、收货地址和备注信息。用户确认商品和金额无误后,点击提交订单,系统会将订单信息提交到后端接口。

我的订单页面用于展示用户提交过的订单。不同订单会显示不同状态,例如待处理、已确认、配送中、已完成、已取消等。

小程序端核心代码逻辑主要包括接口请求、数据绑定、购物车计算和订单提交。

例如商品列表请求逻辑可以设计为:

Page({
  data: {
    categoryId: 0,
    goodsList: []
  },

  onLoad(options) {
    if (options.categoryId) {
      this.setData({
        categoryId: options.categoryId
      })
    }
    this.getGoodsList()
  },

  getGoodsList() {
    wx.request({
      url: 'https://你的域名/api/goods_list.php',
      method: 'GET',
      data: {
        category_id: this.data.categoryId
      },
      success: res => {
        if (res.data.code === 200) {
          this.setData({
            goodsList: res.data.data
          })
        } else {
          wx.showToast({
            title: res.data.msg || '加载失败',
            icon: 'none'
          })
        }
      }
    })
  }
})

购物车金额计算逻辑如下:

calculateTotal() {
  let total = 0

  this.data.cartList.forEach(item => {
    total += Number(item.price) * Number(item.num)
  })

  this.setData({
    totalPrice: total.toFixed(2)
  })
}

订单提交时,前端只传商品 ID、数量和收货信息,订单金额由后端重新计算,避免用户修改前端数据导致金额不准确。

submitOrder() {
  wx.request({
    url: 'https://你的域名/api/order_submit.php',
    method: 'POST',
    data: {
      user_id: this.data.userId,
      address: this.data.address,
      phone: this.data.phone,
      remark: this.data.remark,
      goods: this.data.cartList.map(item => {
        return {
          goods_id: item.id,
          num: item.num
        }
      })
    },
    success: res => {
      if (res.data.code === 200) {
        wx.showToast({
          title: '下单成功',
          icon: 'success'
        })

        wx.redirectTo({
          url: '/pages/order/order'
        })
      } else {
        wx.showToast({
          title: res.data.msg || '下单失败',
          icon: 'none'
        })
      }
    }
  })
}

七、后端接口开发

后端主要负责处理小程序请求,包括商品查询、分类查询、订单提交、订单查询等功能。接口统一返回 JSON 数据,方便小程序端解析。

接口返回格式统一设计为:

{
  "code": 200,
  "msg": "success",
  "data": {}
}

这样小程序端只需要判断 code 是否为 200,就可以知道请求是否成功。

数据库连接使用统一文件进行封装,避免每个接口重复写连接代码。

<?php
$host = '127.0.0.1';
$dbname = 'frozen_food_mall';
$username = 'root';
$password = 'root';

try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $username,
        $password
    );
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo json_encode([
        'code' => 500,
        'msg' => '数据库连接失败'
    ], JSON_UNESCAPED_UNICODE);
    exit;
}
?>

统一返回函数如下:

<?php
function success($data = [], $msg = 'success') {
    echo json_encode([
        'code' => 200,
        'msg' => $msg,
        'data' => $data
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

function error($msg = 'error', $code = 400) {
    echo json_encode([
        'code' => $code,
        'msg' => $msg,
        'data' => null
    ], JSON_UNESCAPED_UNICODE);
    exit;
}
?>

商品列表接口示例:

<?php
require_once 'db.php';
require_once 'response.php';

$categoryId = isset($_GET['category_id']) ? intval($_GET['category_id']) : 0;

if ($categoryId > 0) {
    $stmt = $pdo->prepare(
        "SELECT id, name, image, price, stock, unit, description 
         FROM goods 
         WHERE status = 1 AND category_id = ? 
         ORDER BY sort DESC, id DESC"
    );
    $stmt->execute([$categoryId]);
} else {
    $stmt = $pdo->query(
        "SELECT id, name, image, price, stock, unit, description 
         FROM goods 
         WHERE status = 1 
         ORDER BY sort DESC, id DESC"
    );
}

$list = $stmt->fetchAll(PDO::FETCH_ASSOC);

success($list);
?>

订单提交接口是后端中最核心的接口。它需要完成商品校验、库存判断、订单金额计算、订单主表写入、订单详情写入等操作。

<?php
require_once 'db.php';
require_once 'response.php';

$data = json_decode(file_get_contents('php://input'), true);

$userId = intval($data['user_id'] ?? 0);
$address = trim($data['address'] ?? '');
$phone = trim($data['phone'] ?? '');
$remark = trim($data['remark'] ?? '');
$goodsList = $data['goods'] ?? [];

if ($userId <= 0) {
    error('用户信息错误');
}

if ($address === '') {
    error('收货地址不能为空');
}

if ($phone === '') {
    error('联系电话不能为空');
}

if (empty($goodsList)) {
    error('订单商品不能为空');
}

try {
    $pdo->beginTransaction();

    $totalPrice = 0;
    $orderGoods = [];

    foreach ($goodsList as $item) {
        $goodsId = intval($item['goods_id']);
        $num = intval($item['num']);

        if ($goodsId <= 0 || $num <= 0) {
            throw new Exception('商品数据错误');
        }

        $stmt = $pdo->prepare(
            "SELECT id, name, image, price, stock 
             FROM goods 
             WHERE id = ? AND status = 1"
        );
        $stmt->execute([$goodsId]);
        $goods = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$goods) {
            throw new Exception('商品不存在或已下架');
        }

        if ($goods['stock'] < $num) {
            throw new Exception($goods['name'] . '库存不足');
        }

        $subtotal = bcmul($goods['price'], $num, 2);
        $totalPrice = bcadd($totalPrice, $subtotal, 2);

        $orderGoods[] = [
            'goods_id' => $goods['id'],
            'goods_name' => $goods['name'],
            'goods_image' => $goods['image'],
            'price' => $goods['price'],
            'num' => $num,
            'subtotal' => $subtotal
        ];
    }

    $orderNo = date('YmdHis') . mt_rand(1000, 9999);

    $stmt = $pdo->prepare(
        "INSERT INTO orders(order_no, user_id, total_price, address, phone, remark, status, create_time)
         VALUES(?, ?, ?, ?, ?, ?, 0, NOW())"
    );
    $stmt->execute([
        $orderNo,
        $userId,
        $totalPrice,
        $address,
        $phone,
        $remark
    ]);

    $orderId = $pdo->lastInsertId();

    foreach ($orderGoods as $item) {
        $stmt = $pdo->prepare(
            "INSERT INTO order_detail(order_id, goods_id, goods_name, goods_image, price, num, subtotal)
             VALUES(?, ?, ?, ?, ?, ?, ?)"
        );

        $stmt->execute([
            $orderId,
            $item['goods_id'],
            $item['goods_name'],
            $item['goods_image'],
            $item['price'],
            $item['num'],
            $item['subtotal']
        ]);

        $stmt = $pdo->prepare(
            "UPDATE goods 
             SET stock = stock - ?, sales = sales + ? 
             WHERE id = ?"
        );
        $stmt->execute([
            $item['num'],
            $item['num'],
            $item['goods_id']
        ]);
    }

    $pdo->commit();

    success([
        'order_id' => $orderId,
        'order_no' => $orderNo,
        'total_price' => $totalPrice
    ], '下单成功');

} catch (Exception $e) {
    $pdo->rollBack();
    error($e->getMessage());
}
?>

这里使用事务处理,保证订单主表、订单详情表和库存扣减要么全部成功,要么全部失败。这样可以避免出现订单生成了但库存没有扣减,或者库存扣减了但订单没有生成的问题。

八、管理员后台开发

管理员后台是公司内部人员使用最多的部分。对真实业务项目来说,只有小程序端是不够的,后台管理系统同样重要。因为商品价格、库存、图片、上下架状态和订单状态都需要由公司内部人员维护。

后台主要页面包括:

登录页面
后台首页
商品分类管理
商品列表管理
商品新增与编辑
订单列表
订单详情
用户列表
轮播图管理
管理员账号管理

后台登录使用 session 保存管理员状态,未登录用户不能访问后台页面。

<?php
session_start();

if (!isset($_SESSION['admin_id'])) {
    header('Location: login.php');
    exit;
}
?>

管理员登录逻辑如下:

<?php
session_start();
require_once '../api/db.php';

$username = trim($_POST['username'] ?? '');
$password = trim($_POST['password'] ?? '');

$stmt = $pdo->prepare("SELECT * FROM admin WHERE username = ?");
$stmt->execute([$username]);
$admin = $stmt->fetch(PDO::FETCH_ASSOC);

if ($admin && password_verify($password, $admin['password'])) {
    $_SESSION['admin_id'] = $admin['id'];
    $_SESSION['admin_name'] = $admin['username'];

    header('Location: index.php');
    exit;
} else {
    $msg = '账号或密码错误';
}
?>

后台商品管理页面是整个后台中最核心的页面之一。管理员可以新增商品、编辑商品、上传图片、设置价格、设置库存和控制上下架。

商品保存逻辑示例:

<?php
require_once 'check_login.php';
require_once '../api/db.php';

$id = intval($_POST['id'] ?? 0);
$name = trim($_POST['name'] ?? '');
$categoryId = intval($_POST['category_id'] ?? 0);
$price = trim($_POST['price'] ?? '0');
$stock = intval($_POST['stock'] ?? 0);
$unit = trim($_POST['unit'] ?? '');
$description = trim($_POST['description'] ?? '');
$status = intval($_POST['status'] ?? 1);

$image = $_POST['old_image'] ?? '';

if (!empty($_FILES['image']['name'])) {
    $ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
    $filename = date('YmdHis') . mt_rand(1000, 9999) . '.' . $ext;
    $target = '../uploads/' . $filename;

    if (move_uploaded_file($_FILES['image']['tmp_name'], $target)) {
        $image = '/uploads/' . $filename;
    }
}

if ($id > 0) {
    $stmt = $pdo->prepare(
        "UPDATE goods 
         SET category_id = ?, name = ?, image = ?, price = ?, stock = ?, unit = ?, description = ?, status = ?
         WHERE id = ?"
    );

    $stmt->execute([
        $categoryId,
        $name,
        $image,
        $price,
        $stock,
        $unit,
        $description,
        $status,
        $id
    ]);
} else {
    $stmt = $pdo->prepare(
        "INSERT INTO goods(category_id, name, image, price, stock, unit, description, status, create_time)
         VALUES(?, ?, ?, ?, ?, ?, ?, ?, NOW())"
    );

    $stmt->execute([
        $categoryId,
        $name,
        $image,
        $price,
        $stock,
        $unit,
        $description,
        $status
    ]);
}

header('Location: goods_list.php');
exit;
?>

订单管理页面用于查看客户订单并修改订单状态。管理员可以按照订单状态筛选订单,也可以进入订单详情查看客户购买了哪些商品。

订单状态更新逻辑如下:

<?php
require_once 'check_login.php';
require_once '../api/db.php';

$orderId = intval($_GET['id'] ?? 0);
$status = intval($_GET['status'] ?? 0);

if ($orderId > 0) {
    $stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?");
    $stmt->execute([$status, $orderId]);
}

header('Location: order_list.php');
exit;
?>

订单状态可以设计为:

0:待处理
1:已确认
2:配送中
3:已完成
4:已取消

后台修改订单状态后,小程序端用户再次进入订单页面,就可以看到最新状态。

九、服务器部署与运维

这个项目除了开发以外,我还负责部署和运维。对于真实业务项目来说,写完代码只是第一步,真正让系统稳定运行还需要完成服务器环境配置、数据库初始化、接口部署、域名配置和问题排查。

部署过程主要包括:

购买或准备服务器
安装 Linux 运行环境
安装 Nginx
安装 PHP
安装 MySQL
导入数据库结构
上传项目代码
配置站点目录
配置接口访问路径
配置小程序合法域名
测试小程序接口
测试后台登录
测试图片上传
测试订单提交
上线后查看运行日志

Nginx 站点配置示例:

server {
    listen 80;
    server_name your-domain.com;

    root /www/wwwroot/frozen_mall;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/tmp/php-cgi.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
    }

    location /uploads/ {
        alias /www/wwwroot/frozen_mall/uploads/;
    }
}

上线后主要运维工作包括:

检查接口是否正常访问
查看 PHP 错误日志
查看 Nginx 访问日志
排查小程序请求失败问题
维护数据库数据
备份数据库
修复线上 Bug
根据公司反馈调整功能
处理图片上传和路径问题
维护后台管理员账号

小程序上线前还需要在微信公众平台配置合法请求域名。如果没有配置,小程序真机环境无法正常请求后端接口。

十、开发过程中遇到的问题

1. 小程序本地调试接口正常,真机无法访问

这个问题主要是因为小程序真机调试不能直接访问 localhost。解决方法是将接口部署到服务器,或者使用电脑局域网 IP 进行调试。同时,小程序正式环境必须配置合法 HTTPS 域名。

2. 商品图片上传后小程序不显示

后台上传图片后,数据库中保存的是相对路径,例如:

/uploads/202401010001.jpg

但是小程序端需要完整地址才能访问图片,所以接口返回数据时需要拼接服务器域名。

$item['image'] = 'https://你的域名' . $item['image'];

3. 订单金额不能完全相信前端

购物车页面虽然会计算总金额,但后端不能直接使用前端传来的金额。因为前端数据可能被修改。最终订单金额必须由后端根据商品 ID 查询数据库价格后重新计算。

4. 库存扣减需要和订单生成保持一致

如果订单生成成功但库存没有扣减,会影响后续销售。如果库存扣减了但订单没有生成,也会造成数据错误。所以订单提交接口中使用数据库事务处理,保证订单和库存数据一致。

5. 后台图片上传需要限制格式

图片上传不能完全相信用户上传内容。正式项目中需要限制图片格式和文件大小,只允许上传 jpg、jpeg、png、webp 等图片,避免上传异常文件。

6. 公司业务需求会不断变化

真实业务项目和练习项目最大的区别是需求会不断变化。项目上线后,公司可能会提出新的管理需求,例如调整分类名称、增加商品规格、修改订单状态、调整首页展示等。因此代码结构需要保持清晰,方便后续维护和扩展。

十一、项目亮点

1. 独立完成完整业务闭环

这个项目从需求分析、小程序页面、后端接口、数据库设计、后台管理、服务器部署到后期运维,全部由我独立完成。项目覆盖了真实企业系统开发的完整流程。

2. 贴合公司真实业务

项目不是为了练习而做,而是围绕公司冻品销售业务开发,解决了商品展示、客户下单、订单统计和后台管理的问题。

3. 小程序端和后台端配套完整

系统不仅有客户使用的小程序端,还有公司内部使用的管理员后台。小程序负责下单,后台负责管理,形成了完整的商城业务闭环。

4. 订单流程清晰

系统实现了从商品浏览、加入购物车、提交订单到后台处理订单的完整流程,使订单数据更加规范,减少人工统计错误。

5. 具备上线和运维经验

项目不只是本地开发完成,还完成了服务器部署、数据库配置、接口联调、小程序配置和上线后的问题排查,积累了真实项目交付经验。

十二、后续优化方向

虽然当前系统已经能够满足公司基本冻品商城业务,但后续还可以继续优化。

第一,可以增加微信支付功能,让客户在小程序中直接完成付款。

第二,可以增加配送管理功能,例如配送员接单、配送状态更新、配送记录查看等。

第三,可以增加优惠券和满减活动,提高客户复购率。

第四,可以增加商品规格管理,例如不同重量、不同包装、不同价格的冻品商品。

第五,可以增加销售统计功能,例如每日订单量、热销商品、销售金额统计等。

第六,可以增加库存预警功能,当商品库存低于设定值时后台自动提醒。

第七,可以增加客户分层管理,例如普通客户、批发客户、长期合作客户使用不同价格体系。

第八,可以优化后台界面,使商品维护和订单处理更加高效。

十三、项目总结

文山农业公司冻品商城小程序是我在工作期间独立开发并负责运维的一个真实业务项目。相比个人练习项目,它更加注重业务可用性、数据准确性、后台管理效率和上线后的稳定运行。

通过这个项目,我完整实践了一个商城系统从 0 到 1 的开发过程,包括需求分析、数据库设计、小程序开发、后端接口开发、管理员后台开发、服务器部署和后期运维。项目上线后,公司可以通过后台维护商品和处理订单,客户可以通过小程序浏览商品并提交订单,原来依赖人工沟通和手工统计的流程得到了明显优化。

这个项目让我对全栈开发有了更加完整的理解。一个真正可用的系统,不只是把页面写出来,还需要考虑数据库结构、业务流程、接口稳定性、后台管理、服务器环境、线上问题排查和后期维护。通过该项目,我提升了小程序开发、后端接口设计、数据库建模、后台管理系统开发、服务器部署和项目运维等综合能力。