Appearance
ShopAgent 技术文档
1. 项目概述
ShopAgent 是一个基于 Flask 的轻量级购物网站项目,前端使用 Vue 3 CDN 方式构建交互页面,后端提供商品、用户、点击记录、最近浏览商品、人工客服工单等 REST API,数据存储使用 MySQL。
项目面向“电商商品展示 + 售前 Agent 辅助导购”场景,核心目标是:
- 展示商品列表、分类、价格、标签、热门标识和商品详情。
- 支持用户注册、登录、退出登录和登录状态校验。
- 记录用户点击商品的行为,用于生成最近点击商品上下文。
- 未登录用户使用浏览器 localStorage 临时保存点击记录,登录后可同步到数据库。
- 将最近点击商品、登录状态、用户名等上下文传入 Dify Chatbot,辅助售前咨询。
- 支持 Dify “转人工”分支调用后端接口,生成客服工单,并可选通过 SMTP 邮件通知客服。
项目入口页面标题为“阿里飞飞购物网站”,页面包含商品分类、商品搜索、最近点击、Dify 预留区、商品卡片和商品详情弹窗。
2. 技术栈
2.1 后端技术栈
| 类型 | 技术/依赖 | 当前版本/说明 |
|---|---|---|
| 编程语言 | Python | 适用于 Flask Web 服务开发 |
| Web 框架 | Flask | 3.0.3 |
| WSGI 工具库 | Werkzeug | 3.0.3,用于密码哈希校验等能力 |
| 数据库驱动 | PyMySQL | 1.1.1,用于连接 MySQL |
| 数据库 | MySQL | 使用 InnoDB、utf8mb4、JSON 字段、外键约束 |
| 会话管理 | Flask Session | 基于 app.secret_key 维护登录状态 |
| 密码安全 | Werkzeug Password Hash | generate_password_hash / check_password_hash |
| 邮件通知 | Python smtplib | 用于转人工工单邮件通知,可选配置 |
2.2 前端技术栈
| 类型 | 技术/依赖 | 说明 |
|---|---|---|
| 页面模板 | Jinja2 Template | Flask 默认模板引擎,页面位于 templates/index.html |
| 前端框架 | Vue 3 CDN | 通过 https://unpkg.com/vue@3/dist/vue.global.prod.js 引入,无需 npm/Vite |
| 网络请求 | Fetch API | 前端统一调用后端 REST API |
| 本地存储 | localStorage | 保存未登录用户点击记录 |
| 样式 | 原生 CSS | 样式文件位于 static/css/style.css |
| 静态资源 | SVG 图片 | 商品图片位于 static/images/ |
| Agent 集成 | Dify Chatbot Embed | 动态插入 Dify 官方脚本,并提供 iframe 兜底方案 |
2.3 依赖文件
项目依赖集中在 requirements.txt:
txt
Flask==3.0.3
Werkzeug==3.0.3
PyMySQL==1.1.13. 项目目录结构
text
ShopAgent/
├── app.py # Flask 后端主程序,包含数据库初始化和 API 路由
├── mysql_schema.sql # MySQL 建库建表 SQL 脚本
├── requirements.txt # Python 依赖列表
├── templates/
│ └── index.html # 前端主页面模板
├── static/
│ ├── css/
│ │ └── style.css # 页面样式
│ ├── js/
│ │ └── app.js # Vue 前端逻辑、API 调用、Dify 集成
│ └── images/
│ ├── product-1.svg # 默认商品图片
│ ├── product-2.svg
│ ├── product-3.svg
│ ├── product-4.svg
│ ├── product-5.svg
│ ├── product-6.svg
│ ├── product-7.svg
│ └── product-8.svg
└── .idea/ # IDE 配置文件,非运行必需4. 核心功能介绍
4.1 商品展示与搜索
项目内置 8 个默认商品,覆盖电脑办公、数码配件、家用电器、家具生活等分类。后端启动时会自动检查数据库,如果 products 表为空,则写入默认商品数据。
商品支持:
- 按分类筛选。
- 按商品名称、描述、分类、标签模糊搜索。
- 热门商品优先展示。
- 点击商品卡片后打开商品详情弹窗。
相关后端接口:
| 接口 | 方法 | 功能 |
|---|---|---|
/api/categories | GET | 获取商品分类列表 |
/api/products | GET | 获取商品列表,支持 category、keyword、limit 查询参数 |
/api/products/<product_id> | GET | 获取单个商品详情 |
4.2 用户注册与登录
系统支持基础账号体系:
- 注册时校验用户名和密码不能为空。
- 密码长度至少 4 位。
- 用户名唯一。
- 密码不会明文保存,而是通过 Werkzeug 生成哈希后写入数据库。
- 登录成功后将
user_id和username写入 Flask Session。
相关后端接口:
| 接口 | 方法 | 功能 |
|---|---|---|
/api/register | POST | 用户注册,成功后自动登录 |
/api/login | POST | 用户登录 |
/api/logout | POST | 退出登录并清空 Session |
/api/me | GET | 获取当前登录状态和用户信息 |
4.3 商品点击记录
点击记录用于分析用户兴趣,并作为 Dify 售前 Agent 的上下文输入。
处理逻辑:
- 用户未登录时,前端将点击记录保存到浏览器
localStorage。 - 用户登录后,前端调用同步接口,把匿名点击记录写入 MySQL。
- 用户已登录时,点击商品会直接写入
clicks表。 - 最近点击商品接口会按用户、商品聚合点击次数,并按最后点击时间倒序返回。
相关后端接口:
| 接口 | 方法 | 功能 |
|---|---|---|
/api/clicks | POST | 登录用户写入商品点击记录 |
/api/clicks/sync | POST | 登录后同步匿名点击记录 |
/api/recent-products | GET | 获取登录用户最近点击商品,支持 limit 参数 |
前端未登录点击记录 key:
js
simple_shop_anonymous_product_clicks4.4 Dify 售前 Agent 集成
前端通过 static/js/app.js 动态插入 Dify Chatbot 配置和脚本。
当前配置字段:
js
const DIFY_CHATBOT_TOKEN = "CZOJ6kUDprFbdjub";
const DIFY_BASE_URL = "http://8.163.40.249";
const DIFY_SCRIPT_URL = `${DIFY_BASE_URL}/embed.min.js`;传给 Dify 的 inputs 包含:
| 字段 | 说明 |
|---|---|
logged_in | 当前用户是否登录,字符串形式:true / false |
username | 当前登录用户名,未登录为空字符串 |
contact | 用户联系方式,当前代码预留字段 |
recent_clicked_products | 最近点击商品列表 JSON 字符串 |
最近点击商品会被标准化为如下结构:
json
{
"id": 1,
"name": "轻薄商务笔记本",
"category": "电脑办公",
"price": 4999,
"description": "适合办公、学习和轻度开发的轻薄笔记本。",
"tags": ["办公", "便携", "高性能"],
"hot": 1,
"last_clicked_at": "2026-05-24T10:00:00",
"click_count": 1
}为了提升兼容性,项目还做了以下兜底处理:
- 如果浏览器不是安全上下文导致
navigator.mediaDevices不可用,会提供兼容兜底,避免 Dify 语音能力报错影响页面。 - 如果 Dify 官方
embed.min.js加载失败或加载后未创建 DOM,会自动创建 iframe 兜底气泡。 - 页面提供“预览传给 Agent 的数据”按钮,便于调试 Dify 上下文输入。
4.5 转人工客服工单
项目提供 /api/support/handoff 接口,供 Dify 的“转人工”节点调用。
接口会把用户问题保存到 support_requests 表中。如果配置了 SMTP 环境变量,则会同步发送邮件通知客服;未配置 SMTP 时只写入数据库,不影响接口成功返回。
请求字段:
| 字段 | 必填 | 说明 |
|---|---|---|
username | 否 | 用户名 |
contact | 否 | 用户联系方式 |
question | 是 | 用户问题 |
reason | 否 | 转人工原因 |
conversation_summary | 否 | 会话摘要 |
成功响应示例:
json
{
"message": "已提交人工客服工单",
"ticket_id": 1,
"email_sent": false
}5. 系统架构说明
5.1 整体架构
text
浏览器
├─ Vue 3 页面交互
├─ Fetch API 调用 Flask 后端
├─ localStorage 保存匿名点击记录
└─ Dify Chatbot 嵌入脚本 / iframe 兜底
│
▼
Flask 后端 app.py
├─ 页面渲染:index.html
├─ 用户认证:注册 / 登录 / 退出 / 当前用户
├─ 商品接口:分类 / 列表 / 详情
├─ 点击追踪:记录点击 / 同步匿名点击 / 最近点击商品
├─ 客服工单:保存转人工请求
└─ 邮件通知:可选 SMTP
│
▼
MySQL 数据库
├─ users
├─ products
├─ clicks
└─ support_requests5.2 典型业务流程
用户浏览商品流程
text
用户打开首页
→ Flask 返回 index.html
→ Vue 初始化
→ 调用 /api/me 检查登录态
→ 调用 /api/categories 加载分类
→ 调用 /api/products 加载商品
→ 页面渲染商品列表商品点击与 Dify 上下文流程
text
用户点击商品
→ 打开商品详情弹窗
→ 判断用户是否登录
├─ 未登录:写入 localStorage
└─ 已登录:POST /api/clicks 写入 MySQL
→ 刷新最近点击商品
→ 更新 window.difyChatbotConfig.inputs
→ Dify Agent 获取用户兴趣上下文登录后匿名点击同步流程
text
用户登录 / 注册成功
→ 前端读取 localStorage 匿名点击记录
→ POST /api/clicks/sync
→ 后端校验商品是否存在
→ 写入 clicks 表,source = anonymous_synced
→ 前端清空 localStorage 点击记录
→ 刷新最近点击商品6. 数据库设计
6.1 users 表
用户账号表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | INT UNSIGNED | 主键,自增 |
username | VARCHAR(80) | 用户名,唯一 |
password_hash | VARCHAR(255) | 密码哈希 |
created_at | DATETIME | 创建时间 |
约束与索引:
- 主键:
id - 唯一索引:
uq_users_username(username)
6.2 products 表
商品表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | INT UNSIGNED | 主键,自增 |
name | VARCHAR(120) | 商品名称 |
category | VARCHAR(80) | 商品分类 |
price | DECIMAL(10,2) | 商品价格 |
image | VARCHAR(255) | 商品图片路径 |
description | TEXT | 商品描述 |
tags | JSON | 商品标签 |
hot | TINYINT(1) | 是否热门商品 |
索引:
idx_products_category(category)idx_products_hot(hot)
6.3 clicks 表
商品点击记录表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | BIGINT UNSIGNED | 主键,自增 |
user_id | INT UNSIGNED | 用户 ID |
product_id | INT UNSIGNED | 商品 ID |
clicked_at | DATETIME | 点击时间 |
source | VARCHAR(32) | 点击来源,例如 logged_in、anonymous_synced |
约束与索引:
- 外键:
user_id关联users(id),用户删除时级联删除点击记录。 - 外键:
product_id关联products(id),商品删除时级联删除点击记录。 - 索引:
idx_clicks_user_clicked_at(user_id, clicked_at) - 索引:
idx_clicks_product_id(product_id)
6.4 support_requests 表
客服工单表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | BIGINT UNSIGNED | 主键,自增 |
username | VARCHAR(80) | 用户名,可为空 |
contact | VARCHAR(120) | 联系方式,可为空 |
question | TEXT | 用户问题 |
reason | VARCHAR(255) | 转人工原因 |
conversation_summary | TEXT | 会话摘要 |
status | VARCHAR(32) | 工单状态,默认 pending |
created_at | DATETIME | 创建时间 |
索引:
idx_support_requests_status_created_at(status, created_at)
7. API 文档
7.1 页面接口
GET /
返回首页 HTML 页面。
7.2 商品接口
GET /api/categories
获取商品分类。
响应示例:
json
{
"categories": ["电脑办公", "家具生活", "家用电器", "数码配件"]
}GET /api/products
获取商品列表。
查询参数:
| 参数 | 必填 | 说明 |
|---|---|---|
category | 否 | 商品分类,默认 全部 |
keyword | 否 | 搜索关键词 |
limit | 否 | 返回数量限制,最大 50 |
响应示例:
json
{
"products": [
{
"id": 1,
"name": "轻薄商务笔记本",
"category": "电脑办公",
"price": 4999,
"image": "/static/images/product-1.svg",
"description": "适合办公、学习和轻度开发的轻薄笔记本。",
"tags": ["办公", "便携", "高性能"],
"hot": 1
}
]
}GET /api/products/<product_id>
获取指定商品详情。
异常响应:
json
{
"message": "商品不存在"
}7.3 用户接口
POST /api/register
注册用户,成功后自动登录。
请求示例:
json
{
"username": "test",
"password": "1234"
}响应示例:
json
{
"message": "注册成功",
"user": {
"id": 1,
"username": "test"
}
}POST /api/login
用户登录。
请求示例:
json
{
"username": "test",
"password": "1234"
}响应示例:
json
{
"message": "登录成功",
"user": {
"id": 1,
"username": "test"
}
}POST /api/logout
退出登录。
响应示例:
json
{
"message": "已退出登录"
}GET /api/me
获取当前用户状态。
响应示例:
json
{
"logged_in": true,
"user": {
"id": 1,
"username": "test"
}
}7.4 点击记录接口
POST /api/clicks
登录用户写入商品点击记录。
请求示例:
json
{
"product_id": 1
}响应示例:
json
{
"message": "点击记录已写入数据库"
}POST /api/clicks/sync
同步匿名点击记录到数据库。
请求示例:
json
{
"clicks": [
{
"product_id": 1,
"clicked_at": "2026-05-24T10:00:00.000Z"
}
]
}响应示例:
json
{
"message": "匿名点击记录已同步",
"saved_count": 1
}GET /api/recent-products
获取最近点击商品。
查询参数:
| 参数 | 必填 | 说明 |
|---|---|---|
limit | 否 | 数量限制,默认 5,最大 20 |
响应示例:
json
{
"logged_in": true,
"products": [
{
"id": 1,
"name": "轻薄商务笔记本",
"category": "电脑办公",
"price": 4999,
"image": "/static/images/product-1.svg",
"description": "适合办公、学习和轻度开发的轻薄笔记本。",
"tags": ["办公", "便携", "高性能"],
"hot": 1,
"last_clicked_at": "2026-05-24T10:00:00",
"click_count": 3
}
]
}7.5 人工客服接口
POST /api/support/handoff
提交转人工客服工单。
请求示例:
json
{
"username": "test",
"contact": "test@example.com",
"question": "我想了解笔记本电脑的售后政策",
"reason": "用户需要人工解释售后条款",
"conversation_summary": "用户咨询轻薄商务笔记本的保修和退换货问题"
}响应示例:
json
{
"message": "已提交人工客服工单",
"ticket_id": 1,
"email_sent": false
}8. 环境变量配置
8.1 Flask 与 MySQL 配置
| 环境变量 | 默认值 | 说明 |
|---|---|---|
SHOP_SECRET_KEY | SHOP_SECRET_KEY | Flask Session 密钥,生产环境必须修改 |
MYSQL_HOST | 127.0.0.1 | MySQL 主机 |
MYSQL_PORT | 3306 | MySQL 端口 |
MYSQL_USER | root | MySQL 用户名 |
MYSQL_PASSWORD | 123456 | MySQL 密码 |
MYSQL_DATABASE | shop_agent | 数据库名称 |
MYSQL_CHARSET | utf8mb4 | 数据库字符集 |
8.2 SMTP 邮件配置
如果需要转人工邮件通知,需要配置以下变量:
| 环境变量 | 必填 | 说明 |
|---|---|---|
SUPPORT_EMAIL_TO | 是 | 客服接收邮箱 |
SMTP_HOST | 是 | SMTP 服务器地址 |
SMTP_PORT | 否 | SMTP 端口,默认 587 |
SMTP_USER | 是 | SMTP 登录账号 |
SMTP_PASSWORD | 是 | SMTP 登录密码 |
SMTP_USE_TLS | 否 | 是否启用 TLS,默认 true |
SMTP_FROM | 否 | 发件人,默认使用 SMTP_USER |
项目总结
ShopAgent 是一个适合教学、课程设计或原型验证的轻量级电商导购项目。项目的主要特点是:
- 后端使用 Flask 快速提供 REST API。
- 数据层使用 MySQL 保存用户、商品、点击记录和客服工单。
- 前端使用 Vue 3 CDN,无需复杂前端构建流程。
- 支持未登录与已登录两种点击追踪方案。
- 与 Dify Chatbot 集成,把用户最近点击商品作为售前 Agent 上下文。
- 提供转人工接口,可沉淀客服工单并通过 SMTP 邮件通知客服。
该项目可以作为“电商网站 + AI 售前客服”的基础版本,后续可继续扩展订单、购物车、支付、商品管理后台、推荐系统、客服工单后台等功能。