# Technical Documentation: gz-yami/mall4cloud

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/gz-yami/mall4cloud)

### Overview (`README.md`)

# Mall4cloud 开源版微服务商城系统



Mall4cloud 是 Mall4j 体系下的微服务商城产品线。本仓库是 Mall4cloud 开源版主仓库，开源版面向 B2B2C 商城架构，基于 Spring Boot 4、Spring Cloud、Nacos、Seata、MySQL、Redis、RocketMQ、Canal、Elasticsearch、MinIO 等组件构建，包含平台端、商家端、用户端和完整订单流程，适合学习微服务电商架构、评估多商户商城源码，以及进行企业商城系统二次开发。

## 项目说明

- 项目名称：Mall4cloud 微服务商城开源版、Mall4cloud B2B2C 开源商城、Mall4j 微服务商城开源仓库。
- 项目简介：Mall4cloud 是 Mall4j 体系下的微服务商城产品线；本仓库是 Mall4cloud 开源版主仓库，开源版面向 B2B2C 商城架构，适合学习微服务电商架构、评估多商户商城源码和企业商城二次开发。
- 技术说明：Mall4cloud 主线已升级到 Spring Boot 4、Spring Cloud 和 Vue3，具体依赖版本以后端 `pom.xml` 和前端 `package.json` 为准。
- 开源授权：本仓库开源版遵守 AGPLv3 协议，适合学习、评估和符合协议的使用场景。
- 商业授权：闭源商用、企业私有化交付、微服务集群交付、企业级售后和更多业务版本应参考 Mall4j 官网商业授权说明。
- 企业版本：商业版坚持 100% 源码交付、源码无加密、永久授权；付费企业版本覆盖 B2C、B2B2C、S2B2C、B2B2B、SaaS、多租户、跨境等场景，具体功能和服务范围以官网与合同确认为准。
- 版本说明：Mall4cloud 是 Mall4j 体系下的微服务商城产品线，区别于 Mall4j 单体开源版；企业版本的功能和服务以官网说明为准。
- 相关链接：[官网](https://www.mall4j.com)、[价格/功能对比](https://www.mall4j.com/price/)、[客户案例](https://www.mall4j.com/case/)、当前仓库。

## 项目特点

- Spring Boot 4 + Spring Cloud 微服务架构
- Nacos 服务注册与配置，Seata 分布式事务
- Redis 缓存、RocketMQ 消息队列、Elasticsearch 搜索、MinIO 文件存储
- 平台端、商家端、用户端、多服务模块协同
- AGPLv3 开源，商业授权和企业版本说明见“授权与版本”

## 技术版本说明

Mall4cloud 主线已升级到 Spring Boot 4、Spring Cloud 和 Vue3，适合微服务商城新项目评估和长期维护。对于仍停留在旧技术栈上的商城系统，后续框架升级、依赖兼容和安全维护成本通常更高；本项目的具体依赖版本以后端 `pom.xml` 和前端 `package.json` 为准。


## 前言

本仓库致力于提供一个功能完整、易于维护的 Mall4cloud 开源版微服务电商系统参考实现。当前开源版面向 B2B2C 商城架构，后台管理系统包含平台管理、店铺管理、商品管理、订单管理、规格管理、权限管理、资源管理等模块。Mall4j 体系下更多版本和功能范围以官网说明为准。

## 文档与快速启动

项目文档位于仓库 `doc` 目录，也可以通过以下链接查看：

- Gitee 文档：[https://gitee.com/gz-yami/mall4cloud/tree/master/doc](https://gitee.com/gz-yami/mall4cloud/tree/master/doc)
- 开发环境搭建视频：[https://www.bilibili.com/video/BV1TK411C7aV](https://www.bilibili.com/video/BV1TK411C7aV)
- Agent Skill：权威副本 `.agents/skills/mall4cloud/`（Cursor / Codex）；Claude Code 使用 `.claude/skills/mall4cloud/`（同一份内容）。按现有功能点辅助启动、使用和二次开发

建议先阅读文档，再结合视频搭建本地开发环境。先起启动中间件 MySQL、Redis、Nacos、MinIO；后端最小链路是网关 `mall4cloud-gateway`（`8000`）+ `auth` + `rbac` + `biz` + 平台端 `platform` 或商家端 `multishop`。前端 `VITE_APP_BASE_API` 只配网关，不要直连业务端口。

项目按阿里巴巴代码规约进行检查，以下是代码规约扫描结果：




具体目录结构和二次开发阅读路径，可以查看 [技术文档](doc/README.md)。

## 授权与版本

本仓库开源版使用 AGPLv3 协议。你可以按协议学习、研究、二次开发和自行部署。

闭源商用、企业私有化部署交付、微服务集群部署支持、100% 源码交付、源码无加密、永久授权、供应链/SaaS/跨境版本、演示环境和企业级售后支持属于商业授权或企业版本范围，可以通过 Mall4j 官网了解。

- 开源版：微服务 B2B2C 商城系统，适合学习、评估和符合 AGPLv3 的使用场景。
- 企业版本：覆盖 B2C、B2B2C、S2B2C、B2B2B、SaaS、多租户、跨境商城等业务场景，具体功能以官网版本页为准。
- Mall4j 商城官网：[https://www.mall4j.com](https://www.mall4j.com)
- 版本价格与功能对比：[https://www.mall4j.com/price/](https://www.mall4j.com/price/)
- 客户案例：[https://www.mall4j.com/case/](https://www.mall4j.com/case/)

## 商业授权支持



## 开源版与企业版本

| 对比项 | 开源版 | 企业版本 |
| --- | --- | --- |
| 学习微服务电商架构 | 支持 | 支持 |
| 授权方式 | AGPLv3 开源协议 | 商业授权 |
| 闭源商用 | 需另行取得商业授权 | 按商业授权使用 |
| 部署方式 | 可自行部署（遵循 AGPLv3） | 可提供企业私有化部署交付服务 |
| 仓库/版本定位 | Mall4cloud 开源版主仓库，面向微服务 B2B2C 商城架构 | Mall4j/Mall4cloud 企业版本体系，不等同于本开源仓库的增强版 |
| 版本范围 | 微服务 B2B2C 开源架构与基础业务能力 | 可覆盖 B2C、B2B2C、S2B2C、B2B2B、SaaS、多租户、跨境等版本 |
| 100% 源码交付、源码无加密、永久授权 | 可获取当前开源代码，不等同商业交付承诺 | 商业版支持，具体以官网和合同为准 |
| 企业级售后支持 | 社区交流为主 | 可提供商业支持 |

## 常见问题

### Mall4cloud 是什么？

Mall4cloud 是 Mall4j 体系下的微服务商城产品线。本仓库是 Mall4cloud 开源版主仓库，开源版面向 B2B2C 商城架构，基于 Spring Boot 4、Spring Cloud、Nacos、Seata、Redis、RocketMQ、Elasticsearch、MinIO 等组件构建。

### Mall4cloud 和 Mall4j 有什么区别？

Mall4j 开源版主仓库更适合了解 Java 单体商城和 B2C 单商户基础能力；Mall4cloud 开源版主仓库面向微服务 B2B2C 商城架构，包含平台端、商家端、用户端和多个后端服务模块。

### Mall4cloud 是否适合企业私有化部署？

开源版可以按 AGPLv3 协议自行部署和评估。

闭源商用、企业私有化交付、集群部署支持、长期售后和更多业务版本，可以通过 Mall4j 官网了解企业版本。

### 前端应该连哪个端口？

管理后台和 uni-app 都只连网关 `8000`。auth、rbac、product 等业务服务有各自端口，用于本机启动和注册 Nacos，不要配进 `VITE_APP_BASE_API`。

仓库示例 IP 是 `192.168.1.46`，拿到源码后请批量替换成自己的地址。

### Mall4cloud 是否已经升级到 Spring Boot 4？

Mall4cloud 主线已升级到 Spring Boot 4，具体依赖版本以后端 `pom.xml` 和前端 `package.json` 为准。

## 相关资料

- 技术论坛：[https://www.mall4j.com/forum/](https://www.mall4j.com/forum/)
- Gitee 主仓库：[https://gitee.com/gz-yami/mall4cloud](https://gitee.com/gz-yami/mall4cloud)
- GitHub 主仓库：[https://github.com/gz-yami/mall4cloud](https://github.com/gz-yami/mall4cloud)

## 相关开源仓库

| 仓库 | 说明 |
| --- | --- |
| [mall4cloud](https://gitee.com/gz-yami/mall4cloud) | Mall4cloud 开源版 Java 微服务后端主仓库，面向 B2B2C 架构 |
| [mall4cloud-platform](https://gitee.com/gz-yami/mall4cloud-platform) | 平台端管理后台，默认账号 `admin`，密码 `123456` |
| [mall4cloud-multishop](https://gitee.com/gz-yami/mall4cloud-multishop) | 商家端管理后台，默认账号 `admin`，密码 `123456` |
| [mall4cloud-uniapp](https://gitee.com/gz-yami/mall4cloud-uniapp) | uni-app 用户端 |
| [mall4j](https://gitee.com/gz-yami/mall4j) | Mall4j 开源版主仓库，面向 B2C 单商户商城 |

## 演示地址

演示地址：

pc端：[https://cloud-pc.mall4j.com](https://cloud-pc.mall4j.com)

H5端：[https://h5.mall4j.com/cloud](https://h5.mall4j.com/cloud)

小程序演示



## 目录结构规范

我们也有自己的目录结构



- VO（View Object）：显示层对象，通常是 Web 向模板渲染引擎层传输的对象。
- DTO（Data Transfer Object）：数据传输对象，前端像后台进行传输的对象，类似于param。
- BO（Business Object）：业务对象，内部业务对象，只在内部传递，不对外进行传递。
- Model：模型层，此对象与数据库表结构一一对应，通过 Mapper 层向上传输数据源对象。
- Controller：主要是对外部访问控制进行转发，各类基本参数校验，或者不复用的业务简单处理等。为了简单起见，一些与事务无关的代码也在这里编写。
- FeignClient：由于微服务之间存在互相调用，这里是内部请求的接口。
- Controller：主要是对内部访问控制进行转发，各类基本参数校验，或者不复用的业务简单处理等。为了简单起见，一些与事务无关的代码也在这里编写。
- Service 层：相对具体的业务逻辑服务层。
- Manager 层：通用业务处理层，它有如下特征：
  - 1） 对第三方平台封装的层，预处理返回结果及转化异常信息，适配上层接口。
  - 2） 对 Service 层通用能力的下沉，如缓存方案、中间件通用处理。
  - 3） 与 DAO 层交互，对多个 DAO 的组合复用。
- Mapper持久层：数据访问层，与底层 MySQL进行数据交互。
- Listener：监听 `RocketMQ` 进行处理，有时候会监听`easyexcel`相关数据。

关于`FeignClient`，由于微服务之间存在互相调用，`Feign` 是http协议，理论上是为了解耦，而实际上提供方接口进行修改，调用方却没有进行修改的时候，会造成异常，所以我们抽取出来。还有就是对内暴露的接口，是很多地方都公用的，所以我们还将接口抽取了出了一个模块，方便引用。可以看到`mall4cloud-api`这个模块下是所有对内`feign`接口的信息。

## 目录结构

```
mall4cloud
├─mall4cloud-api -- 内网接口
│  ├─mall4cloud-api-auth  -- 授权对内接口
│  ├─mall4cloud-api-biz  -- biz对内接口
│  ├─mall4cloud-api-leaf  -- 美团分布式id生成接口
│  ├─mall4cloud-api-multishop  -- 店铺对内接口
│  ├─mall4cloud-api-order  -- 订单对内接口
│  ├─mall4cloud-api-platform  -- 平台对内接口
│  ├─mall4cloud-api-product  -- 商品对内接口
│  ├─mall4cloud-api-rbac  -- 用户角色权限对内接口
│  ├─mall4cloud-api-search  -- 搜索对内接口
│  └─mall4cloud-api-user  -- 用户对内接口
├─mall4cloud-auth  -- 授权校验模块
├─mall4cloud-biz  -- mall4cloud 业务代码。如图片上传/短信等
├─mall4cloud-common -- 一些公共的方法
│  ├─mall4cloud-common-cache  -- 缓存相关公共代码
│  ├─mall4cloud-common-core  -- 公共模块核心（公共中的公共代码）
│  ├─mall4cloud-common-database  -- 数据库连接相关公共代码
│  ├─mall4cloud-common-order  -- 订单相关公共代码
│  ├─mall4cloud-common-product  -- 商品相关公共代码
│  ├─mall4cloud-common-rocketmq  -- rocketmq相关公共代码
│  └─mall4cloud-common-security  -- 安全相关公共代码
├─mall4cloud-gateway  -- 网关
├─mall4cloud-leaf  -- 基于美团leaf的生成id服务
├─mall4cloud-multishop  -- 商家端
├─mall4cloud-order  -- 订单服务
├─mall4cloud-payment  -- 支付服务
├─mall4cloud-platform  -- 平台端
├─mall4cloud-product  -- 商品服务
├─mall4cloud-rbac  -- 用户角色权限模块
├─mall4cloud-search  -- 搜索模块
└─mall4cloud-user  -- 用户服务
```

## 商城部署后 API 地址

| 服务                                                 | 地址                    |
| ---------------------------------------------------- |-----------------------|
| mall4cloud-gatway 网关服务                           | http://127.0.0.1:8000 |
| mall4cloud-auth  授权校验服务                        | http://127.0.0.1:9101 |
| mall4cloud-biz     业务代码服务（如图片上传/短信等） | http://127.0.0.1:9000 |
| mall4cloud-leaf   基于美团leaf的生成id服务           | http://127.0.0.1:9100 |
| mall4cloud-multishop 商家服务                        | http://127.0.0.1:9103 |
| mall4cloud-order         订单服务                    | http://127.0.0.1:9106 |
| mall4cloud-payment   支付服务                        | http://127.0.0.1:9113 |
| mall4cloud-product    商品服务                       | http://127.0.0.1:9104 |
| mall4cloud-rbac          用户角色服务                | http://127.0.0.1:9102 |
| mall4cloud-search      搜索服务                      | http://127.0.0.1:9108 |
| mall4cloud-user          用户服务                    | http://127.0.0.1:9105 |



## 部署教程

部署教程请参考 [环境搭建](doc/2-环境搭建/README.md) 和 [中间件 Docker Compose](doc/8-部署运维/2-中间件-docker-compose/README.md)。

## 代码运行相关截图

### 1.后台截图

- 平台端

  

- 商家端

  

  

### 2.小程序截图



### 3.uni-app截图




## 提交反馈
- mall4cloud开源技术QQ群：561496886
- Mall4j 商城官网：[https://www.mall4j.com](https://www.mall4j.com)

## 支持项目
如果这个项目对你有帮助，欢迎 Star、Fork 和提交 Issue。

---

### Overview (`doc/README.md`)

# Mall4cloud 技术文档

用 Cursor、Codex 或 Claude Code 打开本仓库时，可使用内置 Skill：`.agents/skills/mall4cloud/`（Claude Code 为 `.claude/skills/mall4cloud/`，内容相同）。它按开源版**现有功能点**辅助启动、使用和二次开发，详细说明仍以本 `doc/` 目录为准。不要套用 Mall4j 单体仓库的模块和端口。

这里是 Mall4cloud 开源版的技术文档入口。按下面顺序阅读：先跑起来，再理解结构，最后做一个后台管理二开练习。

## 阅读路径

| 目标 | 阅读顺序 | 验收结果 |
| --- | --- | --- |
| 30 分钟跑起基础链路 | [环境搭建](./2-环境搭建/README.md) -> [中间件 Docker Compose](./8-部署运维/2-中间件-docker-compose/README.md) -> [故障排查](./9-故障排查/README.md) | 网关、后端服务、平台端或商家端能登录 |
| 2 小时读懂系统结构 | [项目概览](./1-项目概览/README.md) -> [技术框架](./3-技术框架/README.md) -> [技术实现](./4-技术实现/README.md) -> [数据模型](./7-数据模型/README.md) | 能说清服务、前端、权限、数据库的请求链路 |
| 1 天完成简单二开 | [数据模型](./7-数据模型/README.md) -> [权限体系](./4-技术实现/1-登录认证与权限链路.md) -> [二次开发](./5-二次开发/README.md) | 能按现有模式写一个后台管理功能 |

## 一级目录

1. [项目概览](./1-项目概览/README.md)
2. [环境搭建](./2-环境搭建/README.md)
3. [技术框架](./3-技术框架/README.md)
4. [技术实现](./4-技术实现/README.md)
5. [二次开发](./5-二次开发/README.md)
6. [核心业务](./6-核心业务/README.md)
7. [数据模型](./7-数据模型/README.md)
8. [部署运维](./8-部署运维/README.md)
9. [故障排查](./9-故障排查/README.md)

## 学习方式

检查题采用“题目 + 答案 + 原因”。阅读时先看结论，再打开对应代码、配置或 SQL 核对一次。

---

### Overview (`doc/1-项目概览/README.md`)

# 项目概览

先用这部分建立全局地图：Mall4cloud 是什么、先看什么、每个模块负责什么。

## 阅读顺序

1. [项目定位与阅读路径](./1-项目定位与阅读路径.md)
2. [模块职责地图](./2-模块职责地图.md)
3. [学习与自检](./3-学习与自检.md)

读完后，你应该能判断自己接下来要进入环境搭建、技术框架、数据模型还是二次开发。

---

### Overview (`doc/2-环境搭建/README.md`)

# 环境搭建

这部分只服务一个目标：尽快跑起后端网关、必要服务和一个管理后台。

## 阅读顺序

1. [环境要求](./1-环境要求.md)
2. [数据库初始化](./2-数据库初始化.md)
3. [后端启动](./3-后端启动.md)
4. [管理后台启动](./4-管理后台启动.md)
5. [移动端接口地址](./5-移动端接口地址.md)

中间件用 Docker Compose 时，直接看 [部署运维/中间件 Docker Compose](../8-部署运维/2-中间件-docker-compose/README.md)。

---

### Overview (`doc/3-技术框架/README.md`)

# 技术框架

这部分解释项目骨架。先知道代码放哪里、请求怎么走、前后端如何对应。

## 阅读顺序

1. [后端分层与模块](./1-后端分层与模块.md)
2. [前端工程与动态路由](./2-前端工程与动态路由.md)

权限和分页等具体机制在 [技术实现](../4-技术实现/README.md)。

---

### Overview (`doc/4-技术实现/README.md`)

# 技术实现

这部分讲二开最常碰到的机制：权限、上传、分页、验证、异常、消息和搜索。

## 阅读顺序

1. [登录认证与权限链路](./1-登录认证与权限链路.md)
2. [文件上传与富文本安全](./2-文件上传与富文本安全.md)
3. [分页、验证、异常与事务](./3-分页验证异常与事务.md)
4. [消息、搜索与库存链路](./4-消息搜索与库存链路.md)

---

### Overview (`doc/5-二次开发/README.md`)

# 二次开发

这部分用一个后台管理功能示例，演示项目现有开发模式。

## 阅读顺序

1. [后台管理功能二开示例](./1-后台管理功能二开示例.md)
2. [命名约定与验收清单](./2-命名约定与验收清单.md)

示例只写教程，不在仓库里新增业务代码。开始前先读 [权限链路](../4-技术实现/1-登录认证与权限链路.md) 和 [核心表速查](../7-数据模型/2-权限商品订单核心表.md)。

---

### Overview (`doc/6-核心业务/README.md`)

# 核心业务

本目录只写业务阅读路线，不展开订单、支付、库存等高风险链路的改造教程。

## 阅读顺序

1. [商品、订单、库存阅读路线](./1-商品订单库存阅读路线.md)

业务表的结构解释在 [数据模型](../7-数据模型/README.md)。

---

### Overview (`doc/7-数据模型/README.md`)

# 数据模型

跑起来之后，先读数据库。权限、商品 SKU、订单结算相关表决定了登录、菜单、商品展示、下单和二开的基础理解。

## 阅读顺序

1. [数据库阅读路线](./1-数据库阅读路线.md)
2. [权限、商品、订单核心表速查](./2-权限商品订单核心表.md)
3. [带答案的表结构检查题](./3-带答案的表结构检查题.md)

---

### Overview (`doc/8-部署运维/README.md`)

# 部署运维

这部分区分本地启动、中间件一键部署、生产配置和上线检查。没有确认过的配置会列为待核对项。

## 阅读顺序

1. [部署路径与上线检查](./1-部署路径与上线检查.md)
2. [中间件 Docker Compose](./2-中间件-docker-compose/README.md)
3. [已知风险与待核对项](./3-已知风险与待核对项.md)

---

### Overview (`doc/9-故障排查/README.md`)

# 故障排查

这部分优先覆盖新读者第一次启动最常遇到的问题。

## 阅读顺序

1. [启动、登录、接口常见问题](./1-启动登录接口常见问题.md)

---

### Overview (`front-end/mall4cloud-multishop/README.md`)

# README



## 前言

本商城是基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的微服务B2B2C电商商城系统，采用主流的互联网技术架构、全新的UI设计、支持集群部署、服务注册和发现以及拥有完整的订单流程等，代码完全开源，没有任何二次封装，是一个非常适合二次开发的电商平台系统。

本商城致力于为中大型企业打造一个功能完整、易于维护的微服务B2B2C电商商城系统，采用主流微服务技术实现。后台管理系统包含平台管理，店铺管理、商品管理、订单管理、规格管理、权限管理、资源管理等模块



## 授权

除开源版本外，本商城还提供商业版本的商城，欲知详情，请访问官网。

商城官网：https://www.mall4j.com

商城使用 AGPLv3 开源，请遵守 AGPLv3 的相关条款，或者联系作者获取商业授权(https://www.mall4j.com)



## 项目链接

JAVA后台：https://gitee.com/gz-yami/mall4cloud

平台端：https://gitee.com/gz-yami/mall4cloud-platform

商家端：https://gitee.com/gz-yami/mall4cloud-multishop

uni-app：https://gitee.com/gz-yami/mall4cloud-uniapp

## 部署教程

### 1.安装nodejs

[NodeJS](https://nodejs.org/) 项目要求最低 18.12.0，推荐 20.9.0

如果不了解怎么安装nodejs的，可以参考 [菜鸟教程的nodejs相关](https://www.runoob.com/nodejs/nodejs-install-setup.html)

### 2.启动

- 项目要求使用 [pnpm](https://www.pnpm.cn/) 包管理工具
- 使用编辑器打开项目，在根目录执行以下命令安装依赖

```
pnpm install
```

- 运行

```
pnpm run dev
```

- 部署

```
pnpm run build
```

- 如果不想使用 pnpm，请删除 `package.json` 文件中 `preinstall` 脚本后再进行安装

```json
{
    "scripts" : {
        "preinstall": "npx only-allow pnpm"  // 使用其他包管理工具（npm、yarn、cnpm等）请删除此命令
    }
}
```

## 目录结构

```
├── build                      # 构建相关
├── public                     # 静态资源
│   │── favicon.ico            # favicon图标
│   └── index.html             # html模板
├── src                        # 源代码
│   ├── api                    # 所有请求(根据不同后台不同的服务分包)
│   ├── assets                 # 主题 字体等静态资源
│   ├── components             # 全局公用组件
│   ├── directive              # 全局指令
│   ├── filters                # 全局 filter
│   ├── icons                  # 项目所有 svg icons
│   ├── lang                   # 国际化 language
│   ├── layout                 # 全局 layout
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局样式
│   ├── utils                  # 全局公用方法
│   ├── views                  # views 所有页面
│   ├── App.vue                # 入口页面
│   ├── main.js                # 入口文件 加载组件 初始化等
│   └── permission.js          # 权限管理
├── tests                      # 测试
├── .env.xxx                   # 环境变量配置
├── .eslintrc.js               # eslint 配置项
├── .babelrc                   # babel-loader 配置
├── .travis.yml                # 自动化CI配置
├── vue.config.js              # vue-cli 配置
├── postcss.config.js          # postcss 配置
└── package.json               # package.json
```



## 运行相关截图

### 后台截图





## 提交反馈

- Mall4j官网 https://www.mall4j.com

- 如需购买商业版源码，请联系商务微信

  


- mall4cloud开源技术QQ群：561496886



## springboot版本商城请点击

https://gitee.com/gz-yami/mall4j



更多内容请查看 主项目 https://gitee.com/gz-yami/mall4cloud

---

### Overview (`front-end/mall4cloud-platform/README.md`)

# README



## 前言

本商城是基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的微服务B2B2C电商商城系统，采用主流的互联网技术架构、全新的UI设计、支持集群部署、服务注册和发现以及拥有完整的订单流程等，代码完全开源，没有任何二次封装，是一个非常适合二次开发的电商平台系统。

本商城致力于为中大型企业打造一个功能完整、易于维护的微服务B2B2C电商商城系统，采用主流微服务技术实现。后台管理系统包含平台管理，店铺管理、商品管理、订单管理、规格管理、权限管理、资源管理等模块



## 授权

除开源版本外，本商城还提供商业版本的商城，欲知详情，请访问官网。

商城官网：https://www.mall4j.com

商城使用 AGPLv3 开源，请遵守 AGPLv3 的相关条款，或者联系作者获取商业授权(https://www.mall4j.com)



## 项目链接

JAVA后台：https://gitee.com/gz-yami/mall4cloud

平台端：https://gitee.com/gz-yami/mall4cloud-platform

商家端：https://gitee.com/gz-yami/mall4cloud-multishop

uni-app：https://gitee.com/gz-yami/mall4cloud-uniapp

## 部署教程

### 1.安装nodejs

[NodeJS](https://nodejs.org/) 项目要求最低 18.12.0，推荐 20.9.0

如果不了解怎么安装nodejs的，可以参考 [菜鸟教程的nodejs相关](https://www.runoob.com/nodejs/nodejs-install-setup.html)

### 2.启动

- 项目要求使用 [pnpm](https://www.pnpm.cn/) 包管理工具
- 使用编辑器打开项目，在根目录执行以下命令安装依赖

```
pnpm install
```

- 运行

```
pnpm run dev
```

- 部署

```
pnpm run build
```

- 如果不想使用 pnpm，请删除 `package.json` 文件中 `preinstall` 脚本后再进行安装

```json
{
    "scripts" : {
        "preinstall": "npx only-allow pnpm"  // 使用其他包管理工具（npm、yarn、cnpm等）请删除此命令
    }
}
```

## 目录结构

```
├── build                      # 构建相关
├── public                     # 静态资源
│   │── favicon.ico            # favicon图标
│   └── index.html             # html模板
├── src                        # 源代码
│   ├── api                    # 所有请求(根据不同后台不同的服务分包)
│   ├── assets                 # 主题 字体等静态资源
│   ├── components             # 全局公用组件
│   ├── directive              # 全局指令
│   ├── filters                # 全局 filter
│   ├── icons                  # 项目所有 svg icons
│   ├── lang                   # 国际化 language
│   ├── layout                 # 全局 layout
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局样式
│   ├── utils                  # 全局公用方法
│   ├── views                  # views 所有页面
│   ├── App.vue                # 入口页面
│   ├── main.js                # 入口文件 加载组件 初始化等
│   └── permission.js          # 权限管理
├── tests                      # 测试
├── .env.xxx                   # 环境变量配置
├── .eslintrc.js               # eslint 配置项
├── .babelrc                   # babel-loader 配置
├── .travis.yml                # 自动化CI配置
├── vue.config.js              # vue-cli 配置
├── postcss.config.js          # postcss 配置
└── package.json               # package.json
```



## 运行相关截图

### 后台截图







## 提交反馈
- Mall4j官网 https://www.mall4j.com
- 如需购买商业版源码，请联系商务微信

  


- mall4cloud开源技术QQ群：561496886



## springboot版本商城请点击
https://gitee.com/gz-yami/mall4j



更多内容请查看 主项目 https://gitee.com/gz-yami/mall4cloud

---

### Overview (`front-end/mall4cloud-uniapp/README.md`)

- # README

  

  ## 前言

  本商城是基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的微服务B2B2C电商商城系统，采用主流的互联网技术架构、全新的UI设计、支持集群部署、服务注册和发现以及拥有完整的订单流程等，代码完全开源，没有任何二次封装，是一个非常适合二次开发的电商平台系统。

  本商城致力于为中大型企业打造一个功能完整、易于维护的微服务B2B2C电商商城系统，采用主流微服务技术实现。后台管理系统包含平台管理，店铺管理、商品管理、订单管理、规格管理、权限管理、资源管理等模块

  

  ## 授权

  除开源版本外，本商城还提供商业版本的商城，欲知详情，请访问官网。

  商城官网：https://www.mall4j.com

  商城使用 AGPLv3 开源，请遵守 AGPLv3 的相关条款，或者联系作者获取商业授权(https://www.mall4j.com)

  

  ## 项目链接

  JAVA后台：https://gitee.com/gz-yami/mall4cloud

  平台端：https://gitee.com/gz-yami/mall4cloud-platform

  商家端：https://gitee.com/gz-yami/mall4cloud-multishop

  uni-app：https://gitee.com/gz-yami/mall4cloud-uniapp

  ## 部署教程

  ### 1.安装nodejs

  [NodeJS](https://nodejs.org/) 项目要求最低 18.12.0，推荐 20.9.0

  如果不了解怎么安装nodejs的，可以参考 [菜鸟教程的nodejs相关](https://www.runoob.com/nodejs/nodejs-install-setup.html)

  ### 2.启动

  - 项目要求使用 [pnpm](https://www.pnpm.cn/) 包管理工具
  - 使用编辑器打开项目，在根目录执行以下命令安装依赖

  ```
  pnpm install
  ```

  - 运行

  ```
  pnpm run dev:h5
  ```

  - 部署

  ```
  pnpm run build:h5
  ```

  - 如果不想使用 pnpm，请删除 `package.json` 文件中 `preinstall` 脚本后再进行安装

  ```json
  {
      "scripts" : {
          "preinstall": "npx only-allow pnpm"  // 使用其他包管理工具（npm、yarn、cnpm等）请删除此命令
      }
  }
  ```

  ## 技术介绍

  

  本项目是一个uniapp的项目，使用cli进行构建，目录结构如下

  

  ```
  ├── dist                       # 构建相关
  ├── src                        # 源代码
  │   ├── components             # 全局公用组件
  │   ├── hybrid                 # webview本地页面
  │   ├── js_sdk                 # 外部js
  │   ├── lang                   # 国际化 language
  │   ├── package-activities     # 活动分包
  │   ├── package-refund         # 退款分包
  │   ├── package-shop           # 店铺分包
  │   ├── package-user           # 用户分包
  │   ├── pages                  # 主包
  │   ├── router                 # 路由配置
  │   ├── static                 # 静态资源
  │   ├── uni_modules            # uni第三方组件
  │   ├── utils                  # 全局公用方法
  │   ├── wxs                    # wxs
  │   ├── app.css                # 全局样式
  │   ├── App.vue                # 入口页面
  │   ├── main.js                # 入口文件 加载组件 初始化等
  │   ├── manifest.json          # uniapp 项目配置
  │   ├── pages.json             # 页面配置文件
  │   ├── manifest.json          # uniapp 项目配置
  │   ├── popup.scss             # 全局弹窗样式
  │   └── uni.scss         	   # uni样式变量
  ├── .editorconfig              # 编辑器配置
  ├── .env.xxx                   # 环境变量配置
  ├── .eslintxxx.xx              # eslint 相关配置
  ├── .gitignore                 # git 忽略清单
  ├── .npmrc                 	   # npm 配置
  ├── Dockerfile                 # docker部署配置
  ├── index.html             	   # html 模板
  ├── nginx.conf                 # nginx 配置
  ├── package.json               # package.json
  ├── tsconfig.json			   # ts 编译配置
  └── vite.config.js             # vite 配置文件
  ```


  ## 运行相关截图

  ### 1.小程序截图

  

  ### 2.uni-app截图

  

  ## 提交反馈

  - Mall4j官网 https://www.mall4j.com

  - 如需购买商业版源码，请联系商务微信

    


  - mall4cloud开源技术QQ群：561496886

  

  ## springboot版本商城请点击

  https://gitee.com/gz-yami/mall4j


  ## 更多信息请查看官网 <https://www.mall4j.com>

---

### Overview (`mall4cloud-common/mall4cloud-common-cache/README.md`)

### 一些通用的缓存配置

缓存key命名规范:

服务名:业务名:方法名:key, 大小写以下划线分割 如
mall4cloud_oauth:token:access:
mall4cloud_oauth:token:refresh_to_access:
mall4cloud_oauth:token:uid_to_access:

---

### Overview (`doc/8-部署运维/2-中间件-docker-compose/README.md`)

# 中间件 Docker Compose

这个目录保留 Mall4cloud 本地或单机服务器启动中间件所需的 compose 文件和配置材料。它不是通用 Docker 教程；Docker/Compose 安装问题请先查官方文档。

## 先改服务器地址

compose 和初始化 SQL 中仍保留示例地址。启动前请把本目录下的示例 IP 统一替换为你的服务器或本机局域网 IP：

```bash
192.168.1.46 -> <你的服务器IP>
```

需要重点核对的地方：

- `docker-compose.yaml`、`docker-compose-native.yaml` 中的 Nacos、MinIO、Seata、RocketMQ 地址。
- `mysql/initdb/mall4cloud_nacos.sql` 中 Nacos 配置中心写入的 Redis、Seata、OSS、RocketMQ、各服务 MySQL 地址。
- `seata/application.yml` 中的 Seata 数据库地址。
- `canal/conf/example/instance.properties` 中的 MySQL 地址和账号。

## 启动前权限

Linux 服务器上进入本目录后执行：

```bash
chmod -R 777 ./rocketmq
chmod -R 666 ./minio/data
chmod -R 777 ./elasticsearch/data
```

## 启动命令

优先使用带国内镜像地址的 compose：

```bash
docker compose -f docker-compose.yaml up -d --build
```

如果服务器访问 Docker Hub 稳定，也可以使用原生镜像版本：

```bash
docker compose -f docker-compose-native.yaml up -d --build
```

`docker-compose.yaml` 使用处理过的镜像地址，适合国内网络优先尝试。`docker-compose-native.yaml` 使用原生镜像名，只有在服务器 Docker Hub 或镜像源稳定时再使用；不需要重命名文件，直接用 `-f` 指定即可。

首次拉取镜像体积较大，等待时间较长是正常现象。若 RocketMQ 报 `No route info of this topic`，先检查 `rocketmq/broker/conf/broker.conf` 是否成功挂载、`rocketmq/` 目录权限是否足够，再重建 broker/dashboard/namesrv。

## 端口与默认账号

| 中间件 | 端口/地址 | 默认账号 |
| --- | --- | --- |
| MySQL | `<你的服务器IP>:3306` | `root / 80jpnH4.r5g` |
| Redis | `<你的服务器IP>:6379` | 密码 `80jpnH4.r5g` |
| Nacos 控制台 | `http://<你的服务器IP>:8080/index.html` | 本目录初始化 SQL 写入的是 `nacos / 80jpnH4.r5g`，服务配置也必须保持一致 |
| MinIO API | `http://<你的服务器IP>:9000` | 用于后端和前端资源访问 |
| MinIO 控制台 | `http://<你的服务器IP>:9001` | `admin / 80jpnH4.r5g` |
| Elasticsearch | `http://<你的服务器IP>:9200` | `elastic / 80jpnH4.r5g` |
| RocketMQ namesrv | `<你的服务器IP>:9876` | 无控制台账号 |
| RocketMQ dashboard | `http://<你的服务器IP>:8180` | 当前未配置登录保护，生产环境不要裸露 |
| Canal | `<你的服务器IP>:11111` | 由 `canal/conf` 配置 |
| Seata | `<你的服务器IP>:8091` | Seata 2.6 只使用 8091 |

## MinIO 桶

进入 MinIO 控制台后创建桶：

```text
mall4cloud
```

端口用途：

- `9000`：对象访问和上传地址，Nacos 的 `biz.oss.endpoint`、前端 `VITE_APP_RESOURCES_URL` 都应指向这里。
- `9001`：控制台地址，只给运维人员使用。

如果前端图片无法展示，先检查桶是否存在、桶策略是否允许读取、Nacos 中 `biz.oss.resources-url` 是否是 `http://<你的服务器IP>:9000/mall4cloud`。

参考截图：

## Elasticsearch 索引

搜索服务依赖 `product` 和 `order` 两类索引。mapping 以仓库根目录为准：

- [product mapping](../../../es/product.md)
- [order mapping](../../../es/order.md)

创建索引时用 Elasticsearch 账号 `elastic / 80jpnH4.r5g`。下面截图只作为操作界面参考：

- 
- 

## Nacos、Canal、Seata

初始化配置在 `mysql/initdb` 下：

- `mall4cloud_nacos.sql`：写入 Nacos 配置中心，包含网关路由、数据源、Redis、OSS、RocketMQ、Seata 地址。
- `mall4cloud_seata.sql`：Seata 事务表。
- Canal 相关配置保留在 `canal/conf`。

Nacos v3 控制台走 `8080`，服务客户端配置仍使用 `8848`，Nacos 2/3 客户端还会用到 `9848`、`9849`。防火墙或安全组只放开控制台端口时，Java 服务仍可能连不上。

Seata 2.6 本目录的 compose 只暴露 `8091`。如果需要控制台能力，请按 Seata 官方方式单独部署，不要在 `seata/application.yml` 中添加未验证的 console 配置。

## 验收

- `docker ps` 能看到 MySQL、Redis、Nacos、Seata、ES、Canal、RocketMQ、MinIO 容器运行。
- Nacos 控制台能登录，并能看到 `application.yml`、`mall4cloud-gateway.yml` 等配置。
- MySQL 中存在 `mall4cloud_auth`、`mall4cloud_rbac`、`mall4cloud_product`、`mall4cloud_order` 等库。
- MinIO 存在 `mall4cloud` 桶。
- RocketMQ dashboard 能看到 namesrv。

如果这些都通过，再回到 [环境搭建/后端启动](../../2-环境搭建/3-后端启动.md)。

---

### Overview (`front-end/mall4cloud-multishop/public/static/js/tinymce/js/tinymce/langs/README.md`)

This is where language files should be placed.

Please DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/

---

### Doc 1 项目概览 1 项目定位与阅读路径 (`doc/1-项目概览/1-项目定位与阅读路径.md`)

# 项目定位与阅读路径

Mall4cloud 开源版是一个微服务 B2B2C 商城系统。当前仓库包含后端微服务、数据库脚本、中间件配置和三个前端工程：平台端、商家端、uni-app 用户端。

## 适合谁读

| 读者 | 先解决的问题 |
| --- | --- |
| 新维护者 | 先把项目跑起来，再知道服务、数据库和前端怎么连起来。 |
| 二次开发者 | 先理解权限、数据表和后台管理功能模式，再新增后台功能。 |
| 开源评估者 | 先判断技术栈、运行成本、授权边界和是否符合自己的商城场景。 |

## 30 分钟目标：跑起基础链路

先不用跑全量业务，重点确认网关、登录、权限和一个后台端可用：

1. MySQL、Redis、Nacos、MinIO 先可用。
2. 网关 `mall4cloud-gateway` 在 `8000` 端口可访问。
3. 认证、权限、平台端或商家端相关服务启动。
4. 管理后台能打开登录页，并能请求网关。

如果使用 Docker Compose 中间件，先看 [中间件 Docker Compose](../8-部署运维/2-中间件-docker-compose/README.md)。

## 2 小时目标：读懂结构

按这个顺序建立地图：

1. [模块职责地图](./2-模块职责地图.md)
2. [后端分层与模块](../3-技术框架/1-后端分层与模块.md)
3. [前端工程与动态路由](../3-技术框架/2-前端工程与动态路由.md)
4. [登录认证与权限链路](../4-技术实现/1-登录认证与权限链路.md)
5. [数据库阅读路线](../7-数据模型/1-数据库阅读路线.md)

读完后应该能回答：请求从前端发出后，经过网关到哪个服务；菜单从哪里来；按钮权限为什么能隐藏；商品和订单核心表怎么关联。

## 1 天目标：完成一次后台二开

优先做后台管理功能，不先碰订单、支付、库存、结算。可以从热搜、轮播图、附件分组这类后台 CRUD 学起，再迁移到自己的功能。

学习路线：

1. 读 [核心表速查](../7-数据模型/2-权限商品订单核心表.md)。
2. 读 [权限链路](../4-技术实现/1-登录认证与权限链路.md)。
3. 照 [后台管理功能二开示例](../5-二次开发/1-后台管理功能二开示例.md) 写自己的文档或代码。
4. 用 [命名约定与验收清单](../5-二次开发/2-命名约定与验收清单.md) 自检。

## 阅读原则

- 每个目录对应一个学习阶段。
- 检查题都给答案和原因。
- 结论以当前源码、配置和 SQL 为准。

---

### Doc 1 项目概览 2 模块职责地图 (`doc/1-项目概览/2-模块职责地图.md`)

# 模块职责地图

本页帮助你先知道“改哪里”，再去看细节。

## 后端服务

| 模块 | 职责 | 常见二开入口 |
| --- | --- | --- |
| `mall4cloud-gateway` | 网关，端口 `8000`，按 Nacos 路由把 `/mall4cloud_xxx/**` 转给服务 | 调整跨域、路由、统一入口 |
| `mall4cloud-auth` | 登录、Token 发放和校验 | 登录方式、账号体系 |
| `mall4cloud-rbac` | 菜单、角色、按钮权限、接口权限 | 新增菜单和按钮权限 |
| `mall4cloud-platform` | 平台端后台业务 | 平台用户、系统配置、平台运营功能 |
| `mall4cloud-multishop` | 商家端后台业务 | 店铺用户、店铺装修、商家运营功能 |
| `mall4cloud-product` | 商品、分类、品牌、SKU、库存 | 商品管理和库存相关功能 |
| `mall4cloud-order` | 订单、订单项、收货地址、发货 | 订单查询和发货，先阅读再改造 |
| `mall4cloud-payment` | 支付信息和支付通知 | 支付接入，先阅读再改造 |
| `mall4cloud-biz` | 文件上传、附件等通用业务 | MinIO 上传、附件管理 |
| `mall4cloud-search` | Elasticsearch 搜索、Canal 消息消费 | 商品/订单索引同步 |
| `mall4cloud-user` | 用户、地址、区域 | 用户资料和收货地址 |
| `mall4cloud-leaf` | 分布式 ID | 新表主键生成策略 |
| `mall4cloud-api` | 服务间 Feign 接口 | 跨服务调用契约 |
| `mall4cloud-common` | 公共响应、异常、分页、安全、缓存、数据库、消息 | 新功能复用基础能力 |

## 前端工程

| 工程 | 角色 | 怎么读 |
| --- | --- | --- |
| `front-end/mall4cloud-platform` | 平台端管理后台 | 重点读登录、动态菜单、后台列表页模式 |
| `front-end/mall4cloud-multishop` | 商家端管理后台 | 重点读商家端菜单和同类 CRUD 页面 |
| `front-end/mall4cloud-uniapp` | 用户端 | 先核对接口地址，后台跑通后再看页面 |

平台端和商家端都是 Vue 3 + Vite + Pinia + Element Plus，环境变量使用 `VITE_APP_*`。

## 数据库脚本

| 目录/文件 | 用途 |
| --- | --- |
| `db/*.sql` | 当前库表结构和初始数据，是数据模型文档的主要依据 |
| `doc/8-部署运维/2-中间件-docker-compose/mysql/initdb/*.sql` | Docker Compose 启动中间件时导入 Nacos、Seata 等配置 |
| `es/product.md`、`es/order.md` | Elasticsearch 索引 mapping |

## 请求链路

管理后台请求 `VITE_APP_BASE_API`，默认指向网关 `8000`。网关读取 Nacos 中的路由配置，例如 `/mall4cloud_rbac/**` 转到 `mall4cloud-rbac`。业务服务再通过 MySQL、Redis、RocketMQ、Seata、MinIO、Elasticsearch 等中间件完成各自职责。

## 练习：我应该改哪个模块

问题：要新增一个“公告管理”后台功能，第一步应该去订单服务吗？

答案：不应该。公告管理更像平台端运营能力。优先在 `mall4cloud-platform` 增加后端接口，在 `front-end/mall4cloud-platform` 增加页面，在 `mall4cloud-rbac` 的菜单和按钮权限中补权限。

为什么：订单服务涉及交易状态和库存协作，不适合作为学习项目分层的第一个改动点。

---

### Doc 1 项目概览 3 学习与自检 (`doc/1-项目概览/3-学习与自检.md`)

# 学习与自检

这部分说明怎么读文档、怎么确认自己读懂了。检查题不是考试，答案和原因都会直接给出。

## 阅读时先看三件事

| 问题 | 读到什么程度算够 |
| --- | --- |
| 我现在要做什么？ | 知道本页目标和下一步入口。 |
| 项目怎么实现？ | 能指出对应源码、配置或 SQL。 |
| 我怎么知道自己做对了？ | 能对照验收信号或检查题说明原因。 |

## 检查题怎么用

每道题都有三个部分：

1. 题目：先自己判断一次。
2. 答案：直接给出正确结论。
3. 为什么：说明依据来自哪个代码、配置或 SQL 事实。

示例：

问题：平台端前端应该配置 `VUE_APP_BASE_API` 还是 `VITE_APP_BASE_API`？

答案：配置 `VITE_APP_BASE_API`。

为什么：当前平台端和商家端是 Vite 工程，`.env.development` 中使用 `VITE_APP_BASE_API`，代码通过 `import.meta.env` 读取。

## 读者自检

| 阶段 | 自检答案应该是什么 |
| --- | --- |
| 启动后 | 网关 `8000` 可访问，后台登录页能请求到网关。 |
| 读完框架后 | 能说出 `Controller -> Service -> Mapper -> DB` 的基本链路和 Feign 的用途。 |
| 读完权限后 | 能说出菜单控制页面可见性，按钮权限控制按钮显示，后端接口权限控制请求是否放行。 |
| 读完数据模型后 | 能说出 `menu`、`menu_permission`、`role_menu`、`user_role` 的关系。 |

## 避免误判

- 不用截图代替源码、配置或 SQL 结论。
- 不把通用安装教程当成项目配置。
- 不把未经核对的命令写成当前可用。

---

### Doc 2 环境搭建 1 环境要求 (`doc/2-环境搭建/1-环境要求.md`)

# 环境要求

以下版本来自当前仓库配置。后台前端是 Vue 3 + Vite，后端要求 JDK 17。

## 后端

| 组件 | 当前要求 | 依据 |
| --- | --- | --- |
| JDK | 17 | 根 `pom.xml` 的 `java.version` |
| Maven | 3.9+ | 当前多模块 Maven 工程 |
| Spring Boot | 4.0.3 | 根 `pom.xml` |
| Spring Cloud | 2025.1.1 | 根 `pom.xml` |
| Spring Cloud Alibaba | 2025.1.0.0 | 根 `pom.xml` |
| Nacos Client | 3.1.1 | 根 `pom.xml` |
| Seata | 2.6.0 | 根 `pom.xml` |

## 前端

| 组件 | 当前要求 | 依据 |
| --- | --- | --- |
| Node.js | `^20.19.0 || >=22.12.0` | 平台端/商家端 `package.json` |
| pnpm | `>=8 <11` | 平台端/商家端 `package.json` |
| Vite | 8.x | 平台端/商家端 `package.json` |
| Vue | 3.5.x | 平台端/商家端 `package.json` |

前端包管理器必须使用 pnpm。`package.json` 里有 `preinstall: npx only-allow pnpm`，用 npm/yarn 安装会被拦截。

## 中间件

| 组件 | 版本 | 当前配置来源 |
| --- | --- | --- |
| MySQL | 8.0.x | Docker Compose 使用 MySQL 8 |
| Redis | 7.0 | Docker Compose |
| Nacos | 3.1.1 | Docker Compose |
| Seata | 2.6.0 | Docker Compose |
| RocketMQ | 5.2.0 | Docker Compose |
| Elasticsearch | 7.17.21 | Docker Compose |
| MinIO | `RELEASE.2024-04-18T19-09-19Z` | Docker Compose |
| Canal | 1.1.7 | Docker Compose |

## 检查题

问题：当前后台前端启动前，应该先安装 pnpm 还是直接用 npm install？

答案：先安装并使用 pnpm。

为什么：平台端和商家端 `package.json` 声明了 pnpm 版本范围，并用 `only-allow pnpm` 限制安装工具。

问题：当前后端是否还是 JDK 8 项目？

答案：不是，当前要求是 JDK 17。

为什么：根 `pom.xml` 的 `java.version` 是 `17`。

---

### Doc 2 环境搭建 2 数据库初始化 (`doc/2-环境搭建/2-数据库初始化.md`)

# 数据库初始化

数据库初始化分两类：业务库脚本和中间件初始化脚本。

## 业务库脚本

仓库根目录 `db/` 下是当前业务表结构和初始数据：

| 文件 | 数据库 |
| --- | --- |
| `mall4cloud_auth.sql` | 账号与登录 |
| `mall4cloud_rbac.sql` | 菜单、角色、按钮权限 |
| `mall4cloud_platform.sql` | 平台端用户与配置 |
| `mall4cloud_multishop.sql` | 店铺、商家用户、热搜、轮播 |
| `mall4cloud_product.sql` | 商品、SKU、库存 |
| `mall4cloud_order.sql` | 订单、订单项、收货地址 |
| `mall4cloud_biz.sql` | 附件和附件分组 |
| `mall4cloud_user.sql`、`mall4cloud_user_area_data.sql` | 用户与地区数据 |
| `mall4cloud_leaf.sql` | 分布式 ID |
| `mall4cloud_payment.sql` | 支付信息 |

如果不用 Docker Compose 自动初始化，请手动创建库并执行这些 SQL。

## 中间件初始化脚本

使用 [中间件 Docker Compose](../8-部署运维/2-中间件-docker-compose/README.md) 时，`mysql/initdb` 会导入 Nacos、Seata、Canal 等配置。这里包含网关路由、各服务数据源、Redis、RocketMQ、OSS、Seata 地址。

启动前请先把示例 IP 替换为你的环境 IP，否则服务可能注册到了错误地址。

## 默认账号

当前初始化数据里可见：

| 端 | 用户名 | 密码 | 说明 |
| --- | --- | --- | --- |
| 平台端 | `admin` | `123456` | 根 README 对应平台端仓库说明；账号记录在 `mall4cloud_auth.sql`、`mall4cloud_platform.sql` |
| 商家端 | `admin` | `123456` | 根 README 对应商家端仓库说明；账号记录在 `mall4cloud_auth.sql`、`mall4cloud_multishop.sql` |

如果登录失败，先确认导入的 auth、platform、multishop、rbac 库来自同一套脚本，不要混用其他版本数据。

## 验收

- MySQL 中存在 `mall4cloud_auth`、`mall4cloud_rbac`、`mall4cloud_product`、`mall4cloud_order` 等库。
- `mall4cloud_auth.auth_account` 中有平台端和商家端账号。
- `mall4cloud_rbac.menu` 和 `mall4cloud_rbac.menu_permission` 中有菜单和按钮权限数据。
- Nacos 中有 `application.yml` 和 `mall4cloud-gateway.yml`。

## 检查题

问题：为什么只导入 `db/mall4cloud_product.sql` 不能完成后台登录？

答案：登录至少还依赖 `mall4cloud_auth`、`mall4cloud_rbac`，平台端或商家端还依赖对应的 `mall4cloud_platform` 或 `mall4cloud_multishop`。

为什么：账号在 auth 库，菜单和权限在 rbac 库，登录后用户信息来自平台端或商家端业务库。

---

### Doc 2 环境搭建 3 后端启动 (`doc/2-环境搭建/3-后端启动.md`)

# 后端启动

先跑“登录 + 权限 + 一个后台端”链路，不要一开始全量启动所有业务。

## 先启动中间件

至少需要：

- MySQL
- Redis
- Nacos
- MinIO

如果要验证商品搜索、订单搜索或商品发布后的索引同步，再启动 Elasticsearch、RocketMQ、Canal。

## 服务端口

| 服务 | 端口 |
| --- | --- |
| `mall4cloud-gateway` | `8000` |
| `mall4cloud-leaf` | `9100` |
| `mall4cloud-auth` | `9101` |
| `mall4cloud-rbac` | `9102` |
| `mall4cloud-multishop` | `9103` |
| `mall4cloud-product` | `9104` |
| `mall4cloud-user` | `9105` |
| `mall4cloud-order` | `9106` |
| `mall4cloud-search` | `9108` |
| `mall4cloud-platform` | `9112` |
| `mall4cloud-payment` | `9113` |
| `mall4cloud-biz` | `9000` |

这些端口来自各服务 `bootstrap.yml` 和网关 `application.yml`。

## 最小平台端链路

启动：

1. `mall4cloud-gateway`
2. `mall4cloud-auth`
3. `mall4cloud-rbac`
4. `mall4cloud-platform`
5. `mall4cloud-biz`

如果页面需要商品、店铺或订单数据，再启动对应服务。

## 最小商家端链路

启动：

1. `mall4cloud-gateway`
2. `mall4cloud-auth`
3. `mall4cloud-rbac`
4. `mall4cloud-multishop`
5. `mall4cloud-biz`

商品管理页面还会需要 `mall4cloud-product`。

## Nacos 地址

各服务默认从环境变量读取 Nacos：

```text
NACOS_HOST，默认 192.168.1.46
NACOS_PORT，默认 8848
```

本地开发时，如果 Nacos 不在 `192.168.1.46`，请设置环境变量或修改启动配置。还要注意：当前服务 bootstrap 中 Nacos 用户名/密码默认写的是 `nacos/nacos`；如果使用本仓库 Compose 初始化 SQL，Nacos 用户表写入的是 `nacos / 80jpnH4.r5g`，两边必须统一。

## 开发机资源

一次在 IDEA 中启动多个微服务会比较吃内存。当前项目使用 JDK 17，如果本机内存紧张，优先只启动最小链路服务，并给单个服务配置类似 `-Xms512m -Xmx512m -Xss256k` 的启动参数。

不要无脑全仓替换 `192.168.1.46`。更稳妥的做法是重点核对 bootstrap、Nacos 配置、Compose、Canal、Seata、RocketMQ 和前端 `.env.*` 中的示例地址。

## 验收

- 网关日志显示服务启动在 `8000`。
- 服务能注册到 Nacos。
- 访问 `http://<网关IP>:8000/mall4cloud_rbac/menu/route` 时，如果未登录返回未授权，说明路由到了后端。
- 登录成功后，前端能获取菜单。

## 检查题

问题：前端请求应该直连 `mall4cloud-rbac:9102` 吗？

答案：不应该，管理后台应请求网关 `8000`。

为什么：Nacos 中 `mall4cloud-gateway.yml` 配置了 `/mall4cloud_rbac/**` 等路由，前端只需要配置网关地址。

---

### Doc 2 环境搭建 4 管理后台启动 (`doc/2-环境搭建/4-管理后台启动.md`)

# 管理后台启动

平台端和商家端都是 Vite 工程，命令和环境变量以当前 `package.json`、`.env.*` 为准。

## 安装依赖

平台端：

```bash
cd front-end/mall4cloud-platform
pnpm install
pnpm dev
```

商家端：

```bash
cd front-end/mall4cloud-multishop
pnpm install
pnpm dev
```

当前 Vite dev server 端口是 `9527`。如果两个后台同时启动，第二个需要手动换端口或按 Vite 提示处理。

## 关键环境变量

`.env.development` 和 `.env.production` 使用：

```text
VITE_APP_BASE_API
VITE_APP_RESOURCES_URL
VITE_APP_RESOURCES_TYPE
```

本地常见配置：

```text
VITE_APP_BASE_API = 'http://<网关IP>:8000'
VITE_APP_RESOURCES_URL = 'http://<MinIO-IP>:9000/mall4cloud'
VITE_APP_RESOURCES_TYPE = '1'
```

不要使用 `VUE_APP_*`，当前后台只读取 `VITE_APP_*`。

## 登录

默认账号参考 [数据库初始化](./2-数据库初始化.md#默认账号)：

- 平台端：`admin / 123456`
- 商家端：`admin / 123456`

登录失败先看浏览器 Network：

- 请求是否发到 `VITE_APP_BASE_API`。
- 是否返回 `A00004` 未授权。
- 是否跨域失败。
- auth、rbac、platform/multishop 服务是否已启动。

## 检查题

问题：图片能上传但前端显示 404，先改哪个配置？

答案：先核对 `VITE_APP_RESOURCES_URL` 和 Nacos 中 `biz.oss.resources-url` 是否都指向 `http://<MinIO-IP>:9000/mall4cloud`。

为什么：上传和展示使用的资源域名必须和 MinIO 桶地址一致，后台返回的是对象路径，前端需要拼接正确资源地址。

---

### Doc 2 环境搭建 5 移动端接口地址 (`doc/2-环境搭建/5-移动端接口地址.md`)

# 移动端接口地址

本页只说明 uni-app 用户端如何接后端，不展开移动端源码结构。

## 当前范围

`front-end/mall4cloud-uniapp` 是用户端工程。新维护者先确保后端网关和管理后台可用，再处理移动端。

## 要核对什么

1. 网关地址：应指向 `http://<网关IP>:8000`。
2. 图片资源地址：应指向 `http://<MinIO-IP>:9000/mall4cloud`。
3. H5、小程序、App 构建模式下的接口地址是否分别配置。

## 为什么不先看移动端源码

先跑通网关、登录、权限和管理后台，再处理移动端。移动端涉及发布平台、运行时权限、端侧兼容和页面业务，不适合作为第一批启动任务。

## 检查题

问题：后台管理端没跑通时，是否应该先调 uni-app 页面？

答案：不要。

为什么：后台登录、权限、网关、数据库、中间件是基础链路。基础链路没通时，移动端错误会混入更多端侧变量，排查成本更高。

---

### Doc 3 技术框架 1 后端分层与模块 (`doc/3-技术框架/1-后端分层与模块.md`)

# 后端分层与模块

Mall4cloud 后端是多模块 Maven 工程。二开时优先沿用现有分层，不要为了一个功能引入新架构。

## 常见分层

| 层 | 常见目录 | 职责 |
| --- | --- | --- |
| Controller | `controller` | 接收 HTTP 请求、做参数校验、调用 Service、返回 `ServerResponseEntity` |
| DTO | `dto` | 前端传入参数 |
| VO | `vo` | 返回给前端的数据 |
| Service | `service`、`service/impl` | 业务编排、事务边界、跨服务调用 |
| Mapper | `mapper` | MyBatis 数据访问接口 |
| XML | `resources/mapper` | SQL |
| Model | `model` | 与数据库表结构对应 |
| FeignClient | `mall4cloud-api-*` | 服务间调用契约 |
| Common | `mall4cloud-common-*` | 公共响应、异常、分页、缓存、安全、消息等能力 |

## 一个后台列表请求怎么走

以常见列表页为例：

1. 前端页面调用 `src/api/**` 中的方法。
2. 请求进入网关 `mall4cloud-gateway:8000`。
3. 网关按 Nacos 路由转发到对应服务。
4. Controller 接收 `PageDTO` 和搜索条件。
5. Service 调 Mapper 查询。
6. `PageUtil.doPage` 生成 `PageVO`。
7. Controller 返回 `ServerResponseEntity.success(pageVO)`。

## Feign 的位置

跨服务调用不要直接拼 URL。项目把内部接口放在 `mall4cloud-api` 下，例如商品库存锁定、用户信息、权限校验等。服务实现方实现对应 Feign 接口，调用方只依赖 API 模块。

## 新功能落点

- 后台 CRUD 功能优先找同业务域已有的 `HotSearch`、`IndexImg`、`Attr`、`Category` 模式。
- 需要跨服务时，先判断是否已有 `mall4cloud-api-*` 接口；没有再补接口契约。
- 需要事务跨服务时，参考已有 `@GlobalTransactional` 用法，不要只加本地事务。

## 检查题

问题：新增后台表单时，前端传来的对象应该直接用 Model 接收吗？

答案：不要，优先使用 DTO。

为什么：当前项目用 DTO 承接前端输入，用 Model 对应数据库表，用 VO 返回前端。直接暴露 Model 容易把数据库字段和接口契约绑死。

---

### Doc 3 技术框架 2 前端工程与动态路由 (`doc/3-技术框架/2-前端工程与动态路由.md`)

# 前端工程与动态路由

平台端和商家端是 Vue 3 + Vite + Pinia + Element Plus，环境变量使用 `VITE_APP_*`。

## 关键目录

| 目录 | 用途 |
| --- | --- |
| `src/api` | 后端接口封装 |
| `src/views/modules` | 后台业务页面 |
| `src/router` | 基础静态路由 |
| `src/stores/modules/user.js` | 登录用户、权限列表、菜单 ID |
| `src/stores/modules/permission.js` | 动态路由生成 |
| `src/directive/permission` | `v-permission` 按钮权限 |
| `src/directive/rich` | 富文本展示前的 DOMPurify 清洗 |
| `.env.*` | `VITE_APP_*` 环境变量 |

## 动态菜单怎么来

登录后，`src/permission.js` 执行：

1. `getUserInfo()` 获取用户信息和是否管理员。
2. `listPermissions()` 获取按钮权限编码。
3. 非管理员调用 `listMenuIds()` 获取可见菜单 ID。
4. `permissionStore.generateRoutes(menuIds)` 调 `/mall4cloud_rbac/menu/route` 获取菜单路由。
5. `component` 为 `Layout` 时加载布局，否则从 `src/views/modules` 下按路径加载页面。

后台菜单的 `component` 字段必须和页面路径对应。例如 `product/category` 会加载 `src/views/modules/product/category/index.vue`。

## 按钮权限怎么来

页面按钮使用：

```vue
v-permission="['product:category:save']"
```

用户登录后，前端把 `/mall4cloud_rbac/menu_permission/list` 返回的权限编码放到 `roles`。`v-permission` 指令判断当前用户是否有对应编码，没有就移除按钮。管理员直接放行。

## 检查题

问题：新增菜单后，页面文件存在但点击菜单空白，最可能先查什么？

答案：先查 `menu.component` 是否能映射到 `src/views/modules/<component>/index.vue` 或 `src/views/modules/<component>.vue`。

为什么：动态路由通过 `import.meta.glob('../../views/modules/**/**.vue')` 加载页面，路径对不上就没有组件。

---

### Doc 4 技术实现 1 登录认证与权限链路 (`doc/4-技术实现/1-登录认证与权限链路.md`)

# 登录认证与权限链路

权限链路分三层：登录 Token、菜单可见性、按钮/接口权限。二开后台功能时三层都要考虑。

## 登录 Token

1. 前端登录调用 auth 服务。
2. `mall4cloud-auth` 校验账号密码后，通过 `TokenStore.storeAndGetVo` 生成 access token 和 refresh token，并把用户信息写入 Redis。
3. 前端后续请求在 `Authorization` 请求头带 token。
4. 业务服务里的 `AuthFilter` 调 `TokenFeignClient.checkToken(accessToken)` 校验 token。
5. 校验成功后，用户信息放入 `AuthUserContext`。

Token 相关的当前入口：

| 入口 | 作用 |
| --- | --- |
| `LoginController /ua/login` | 登录后清理权限缓存，并返回 token |
| `TokenStore` | 保存、刷新、删除、解析 token |
| `TokenFeignController` | 提供内部 `checkToken` 校验 |
| `AuthFilter` | 每个业务请求进入服务后的 token 和权限校验 |

不需要登录的路径由 `AuthConfigAdapter.excludePathPatterns` 控制。默认放行：

- `/v3/api-docs`
- 内部 Feign 路径
- `/**/ua/**`

## 菜单权限

菜单数据在 `mall4cloud_rbac.menu`。关键字段：

| 字段 | 用途 |
| --- | --- |
| `menu_id` | 菜单 ID |
| `parent_id` | 父菜单 |
| `biz_type` | `1` 商家端，`2` 平台端 |
| `path` | 前端路由路径 |
| `component` | 前端页面组件路径 |
| `title`、`icon` | 侧边栏展示 |

非管理员登录后，前端用 `/mall4cloud_rbac/menu/list_menu_ids` 获取可见菜单 ID，再过滤 `/mall4cloud_rbac/menu/route` 返回的菜单。

## 按钮权限

按钮权限数据在 `mall4cloud_rbac.menu_permission`。关键字段：

| 字段 | 用途 |
| --- | --- |
| `menu_id` | 归属菜单 |
| `permission` | 前端按钮权限编码 |
| `uri` | 后端接口 URI |
| `method` | 请求方法：1 GET、2 POST、3 PUT、4 DELETE |
| `biz_type` | 商家端或平台端 |

角色授权通过 `role_menu` 关联菜单和按钮权限，用户通过 `user_role` 关联角色。

## 后端接口权限

`AuthFilter` 对平台端和商家端用户会调用 `PermissionFeignClient.checkPermission`。当前 `AuthConfigAdapter` 没有全局 `needRbac()` 开关；是否进入 RBAC 校验由用户 `sysType` 决定，平台端和商家端会校验，普通用户端直接放行 RBAC。

`mall4cloud-rbac` 中的实现会：

1. 查询用户拥有的权限编码。
2. 查询当前端所有 `uri + method -> permission` 映射。
3. 用 `AntPathMatcher` 匹配请求 URI 和方法。
4. 如果接口匹配到权限要求，用户必须拥有该权限；如果没有匹配到权限要求，则放行。

这意味着：只加前端按钮权限不够，后端接口权限也要能对应上。

## 新增页面/按钮时要做什么

| 要新增 | 必做 |
| --- | --- |
| 页面菜单 | 新增 `menu`，`component` 对应前端页面路径 |
| 新增按钮 | 新增 `menu_permission`，编码与前端 `v-permission` 一致 |
| 接口保护 | `menu_permission.uri` 和 `method` 对上 Controller 路径和 HTTP 方法 |
| 给角色授权 | 在角色管理中勾选菜单和按钮权限 |

## 排错清单

| 现象 | 答案 |
| --- | --- |
| 菜单不显示 | 用户不是管理员时，先查角色是否勾选该菜单，`list_menu_ids` 是否返回该 `menu_id`。 |
| 按钮不显示 | 查 `v-permission` 编码是否在 `/menu_permission/list` 返回值里。 |
| 按钮显示但接口 401 | 查 `menu_permission.uri`、`method` 是否与真实请求匹配，角色是否拥有该按钮权限。 |
| 管理员可见，普通角色不可见 | 这是正常差异；管理员前端和后端都会走放行逻辑。 |

## 检查题

问题：新增删除按钮，只在 Vue 页面加 `v-permission` 是否足够？

答案：不够。

为什么：`v-permission` 只控制按钮显示，接口访问还要靠 `menu_permission.uri + method` 和角色授权通过后端 `AuthFilter` 校验。

---

### Doc 4 技术实现 2 文件上传与富文本安全 (`doc/4-技术实现/2-文件上传与富文本安全.md`)

# 文件上传与富文本安全

当前文件上传主要由 `mall4cloud-biz` 的 OSS/MinIO 能力承接，富文本展示在前端通过 DOMPurify 做清洗。

## MinIO 上传链路

核心类：

- `OssController`
- `OssConfig`
- `MinioTemplate`

常见流程：

1. 前端请求 `/mall4cloud_biz/oss/info?fileNum=...` 获取上传地址。
2. 后端根据 `biz.oss.*` 配置创建 MinIO 预签名上传 URL。
3. 前端上传到 MinIO。
4. 前端展示图片时使用 `VITE_APP_RESOURCES_URL` 拼接资源路径。

也存在 `/oss/upload_minio` 由后端接收文件再上传 MinIO 的接口。

两种方式的区别：`/oss/info` 是前端拿预签名地址后直传 MinIO，服务端不转发文件内容；`/oss/upload_minio` 是文件先进业务服务再写入 MinIO，会多占一次服务端流量，适合少量或需要后端介入处理的文件。

## 必须对齐的配置

| 配置 | 示例 |
| --- | --- |
| Nacos `biz.oss.endpoint` | `http://<MinIO-IP>:9000` |
| Nacos `biz.oss.resources-url` | `http://<MinIO-IP>:9000/mall4cloud` |
| Nacos `biz.oss.bucket` | `mall4cloud` |
| Nacos `biz.oss.type` | `1` 表示 MinIO |
| 前端 `VITE_APP_RESOURCES_URL` | `http://<MinIO-IP>:9000/mall4cloud` |
| 前端 `VITE_APP_RESOURCES_TYPE` | `1` |

Compose 初始化 SQL 中的 `biz.oss.access-key-secret` 是 `80jpnH4.r5g`，与 `MINIO_ROOT_PASSWORD` 一致。如果 Docker Compose 中 MinIO root 密码改了，Nacos 里的 `biz.oss.access-key-secret` 也要同步。

## 富文本安全

当前平台端和商家端有 `src/directive/rich` 指令，使用 `DOMPurify.sanitize` 后再写入 HTML。商品详情等富文本展示应使用 `v-rich`，不要直接 `v-html` 展示未清洗内容。

后端没有在本轮核对到统一 XSS 过滤器，因此不要把“后端已统一清洗所有输入”写进文档或作为安全假设。

## 检查题

问题：图片上传成功但页面不显示，是否一定是后端上传失败？

答案：不一定。

为什么：上传成功只说明对象写入了 MinIO。展示还依赖桶策略、`resources-url`、前端 `VITE_APP_RESOURCES_URL` 和对象路径拼接。

问题：富文本展示能直接用 `v-html` 吗？

答案：不要直接用，优先使用项目已有 `v-rich`。

为什么：`v-rich` 会先经过 DOMPurify 清洗，直接 `v-html` 会绕开当前前端安全处理。

---

### Doc 4 技术实现 3 分页验证异常与事务 (`doc/4-技术实现/3-分页验证异常与事务.md`)

# 分页、验证、异常与事务

这几个机制是后台 CRUD 二开最常用的公共能力。

## 分页

核心类：

- `PageDTO`
- `PageVO`
- `PageUtil`

Controller 通常接收 `@Valid PageDTO pageDTO`。`PageDTO` 要求 `pageNum`、`pageSize` 非空，并把最大 `pageSize` 限制为 `500`。排序字段会校验字段名，只允许字母、数字、下划线，再把驼峰转下划线。

Service 里常见写法是：

```java
PageUtil.doPage(pageDTO, () -> mapper.list(...))
```

返回给前端的是 `PageVO`：`pages`、`total`、`list`。

常用对象命名按当前代码理解：

| 名称 | 当前用法 |
| --- | --- |
| `Model`/PO | 与数据库表结构对应，放在 `model` 目录 |
| `DTO` | 前端传入参数，例如 `PageDTO`、`SpuDTO` |
| `VO` | 返回给前端的数据，例如 `PageVO` |
| `BO` | 服务内部或跨服务传递的业务对象 |

当前前端是 Vue 3，不使用 Vue 2 的 `.sync` 写法；页面交互以现有 Vue 3 页面和组件为准。

## 验证

DTO 字段使用 Jakarta Validation 注解，例如 `@NotNull`、`@NotBlank`。Controller 参数上加 `@Valid` 后，验证失败会进入统一异常处理。

## 统一异常

`DefaultExceptionHandlerConfig` 处理：

| 异常 | 返回 |
| --- | --- |
| `MethodArgumentNotValidException`、`BindException` | `METHOD_ARGUMENT_NOT_VALID`，并返回字段错误列表 |
| `HttpMessageNotReadableException` | `HTTP_MESSAGE_NOT_READABLE` |
| `Mall4cloudException` | 使用异常自带枚举或直接显示消息 |
| 其他 `Exception` | `EXCEPTION`，并在存在 Seata XID 时尝试回滚全局事务 |

项目约定：请求到达服务并被业务处理后，大多数失败也返回 HTTP 200，前端按业务 `code` 判断。

## 分布式事务

跨服务写操作参考已有 `@GlobalTransactional`。典型位置包括订单、商品、平台用户、商家用户等服务。不要只在本地方法上加事务就认为跨服务一致性已经解决。

## Redis 锁

公共缓存模块提供 `RedisLockRegistry`，锁前缀为 `spring-cloud`。需要并发控制时优先看是否已有业务模式可复用。

## 检查题

问题：列表接口返回值应该是裸 `List<T>` 还是 `PageVO<T>`？

答案：后台分页列表应该返回 `PageVO<T>`。

为什么：当前分页公共模型包含 `pages`、`total`、`list`，前端分页组件依赖这些字段。

问题：`pageSize=10000` 会照单全查吗？

答案：不会按 10000 查，`PageDTO` 会把超过 `500` 的分页大小限制为 `500`。

为什么：`PageDTO.setPageSize` 中有 `MAX_PAGE_SIZE = 500` 的保护。

---

### Doc 4 技术实现 4 消息搜索与库存链路 (`doc/4-技术实现/4-消息搜索与库存链路.md`)

# 消息、搜索与库存链路

这部分用于理解消息、搜索和库存协作，先阅读再改造。

## RocketMQ

当前代码中可见的消息用途包括：

- 订单取消后解锁库存。
- 支付通知后触发订单和库存后续处理。
- Canal 变更消息驱动搜索索引更新。

注意：提交订单时的初次库存锁定不是 RocketMQ 触发，而是订单服务通过 `SkuStockLockFeignClient.lock` 调商品服务完成。RocketMQ 主要负责延迟取消、延迟解锁和支付成功后的后续通知。

相关入口：

- `mall4cloud-order` 的 RocketMQ 配置和订单监听器。
- `mall4cloud-product` 的库存锁定和解锁监听器。
- `mall4cloud-search` 的 Canal 监听器。

## Elasticsearch

搜索服务依赖 Elasticsearch。索引 mapping 在仓库根目录：

- [product mapping](../../es/product.md)
- [order mapping](../../es/order.md)

商品发布后列表或搜索没有数据时，不要只看商品库，还要检查 Canal、RocketMQ、Elasticsearch 三段是否正常。

Canal 当前监听配置在中间件目录的 `canal/conf` 下：`instance.properties` 指向 MySQL binlog，`canal.properties` 指向 RocketMQ namesrv，topic 是 `canal-topic`。搜索服务通过 `CanalListener` 消费 `RocketMqConstant.CANAL_TOPIC`，再分发到商品、店铺、分类、品牌、订单等监听器。

不要用 `update mall4cloud_product set update_time = now()` 触发同步；当前表结构没有这个表名。需要按 `canal.instance.filter.regex` 里的表名触发，例如 `spu`、`sku`、`sku_stock`、`order` 等。

## 库存锁定

核心表：

- `sku_stock`
- `sku_stock_lock`

下单链路会涉及订单服务和商品服务协作。`sku_stock_lock` 通过 `spu_id + sku_id + order_id` 保证同一订单同一 SKU 的锁定记录唯一。

## 检查题

问题：商品库里有商品，但搜索列表没有数据，最可能只查 MySQL 吗？

答案：不够。

为什么：搜索列表依赖 Elasticsearch 索引，索引更新又可能依赖 Canal 和 RocketMQ。MySQL 有数据只能证明源数据存在。

问题：为什么不要第一个二开功能就选择库存扣减？

答案：因为它横跨订单、商品、消息、事务和库存锁定，风险高。

为什么：学习分层时应先选后台 CRUD，避免一开始就碰交易一致性。

---

### Doc 5 二次开发 1 后台管理功能二开示例 (`doc/5-二次开发/1-后台管理功能二开示例.md`)

# 后台管理功能二开示例

本示例选择“平台公告管理”作为学习题材。它是后台 CRUD，不直接触碰订单、支付、库存、结算。本页只写教程和检查清单，不在仓库中新增业务代码。

## 目标

做一个平台端公告管理：

- 列表查询公告。
- 新增公告。
- 编辑公告。
- 删除或禁用公告。
- 平台端菜单可见。
- 普通角色只有授权后才能看到按钮和访问接口。

## 后端步骤

| 步骤 | 应该做什么 | 验收 |
| --- | --- | --- |
| 建表 | 在平台业务库新增公告表，包含主键、标题、内容、状态、排序、创建/更新时间 | 表能初始化，字段含义明确 |
| Model | 新增与表对应的 Model | 字段类型和数据库一致 |
| DTO | 新增保存/更新入参 DTO | 必填字段有验证注解 |
| VO | 新增列表和详情返回 VO | 不把不需要的内部字段暴露给前端 |
| Mapper/XML | 写分页列表、详情、新增、更新、删除或改状态 SQL | SQL 字段名和表字段一致 |
| Service | 封装业务逻辑 | Controller 不直接写 SQL |
| Controller | 提供 page、get、save、update、delete 接口 | 返回 `ServerResponseEntity` |
| 分页 | 列表使用 `PageDTO` 和 `PageVO` | 前端能拿到 `total` 和 `list` |

接口路径放在平台端语义下，例如 `/platform/notice`，或采用现有平台模块风格。落地前先参考 `HotSearchController`、`IndexImgController`、`SysUserController` 等后台接口。

## 前端步骤

| 步骤 | 应该做什么 | 验收 |
| --- | --- | --- |
| API | 在 `src/api` 下新增公告接口文件 | 请求都走网关前缀 |
| 列表页 | 在 `src/views/modules/platform/notice` 下新增页面 | 能查询、分页、重置 |
| 表单弹窗 | 新增或复用 add-or-update 组件 | 新增和编辑共用校验 |
| 按钮权限 | 新增、编辑、删除按钮加 `v-permission` | 无权限用户按钮不可见 |
| 环境变量 | 不新增 `VUE_APP_*` | 仍使用 `VITE_APP_BASE_API` |

## 菜单和按钮权限

需要在 rbac 中配置：

| 类型 | 示例 |
| --- | --- |
| 菜单 | `menu.path = notice`，`menu.component = platform/notice` |
| 新增按钮 | `platform:notice:save`，POST `/platform/notice` |
| 编辑按钮 | `platform:notice:update`，PUT `/platform/notice` |
| 删除按钮 | `platform:notice:delete`，DELETE `/platform/notice` |

编码只是示例，落地命名要和现有平台端权限风格保持一致。

## 验收清单

- 平台管理员能看到公告菜单。
- 普通角色未授权时看不到菜单。
- 普通角色授权菜单后能看到菜单。
- 未授权按钮权限时，新增/编辑/删除按钮不显示。
- 直接请求未授权接口会被后端拒绝。
- 列表分页返回 `PageVO`。
- 表单必填项缺失时返回统一验证错误。

## 检查题

问题：这个示例为什么选公告管理，而不是订单退款？

答案：公告管理是后台 CRUD，能覆盖分层、分页、表单、菜单和按钮权限；订单退款涉及交易一致性，不适合作为第一课。

为什么：学习的目标是先掌握项目模式，再进入高风险业务。

---

### Doc 5 二次开发 2 命名约定与验收清单 (`doc/5-二次开发/2-命名约定与验收清单.md`)

# 命名约定与验收清单

新功能要像项目原生功能，而不是单独长出来的一套风格。

## 命名约定

| 对象 | 约定 |
| --- | --- |
| 数据库表 | 下划线命名，如 `notice`、`notice_item` |
| 主键 | `xxx_id` 或同业务已有风格 |
| Java 类 | `NoticeController`、`NoticeService`、`NoticeMapper`、`NoticeDTO`、`NoticeVO` |
| 前端目录 | 放到 `src/views/modules/<业务域>/<功能>` |
| API 文件 | 放到 `src/api/<业务域>` |
| 权限编码 | `<业务域>:<功能>:<动作>` |
| 接口路径 | 跟当前 Controller 风格一致，不混用多个前缀 |

## 后端验收

- Controller 返回 `ServerResponseEntity`。
- 新增/更新入参使用 DTO，并对必填字段加验证。
- 列表使用 `PageDTO`、`PageVO`。
- Service 负责业务编排，Mapper 只做数据访问。
- 涉及跨服务写操作时评估是否需要 `@GlobalTransactional`。
- 接口路径和 HTTP 方法能与 `menu_permission` 对上。

## 前端验收

- 请求走 `VITE_APP_BASE_API` 指向网关。
- 页面路径能被 `menu.component` 动态加载。
- 按钮使用 `v-permission`。
- 列表、表单、删除确认、加载态、错误提示与现有页面风格一致。
- 不新增 `VUE_APP_*` 或 Vue 2/Vue CLI 配置。

## 权限验收

- 管理员可见全部菜单和按钮。
- 普通角色只在授权后可见菜单。
- 普通角色只在授权后可见按钮。
- 直接调用未授权接口也不能成功。

## 检查题

问题：权限编码写成 `notice:add` 可以吗？

答案：不要，除非当前业务域已有这种风格。

为什么：项目现有编码多是 `<业务域>:<功能>:<动作>`，例如 `product:category:save`。统一风格方便维护和排查。

---

### Doc 6 核心业务 1 商品订单库存阅读路线 (`doc/6-核心业务/1-商品订单库存阅读路线.md`)

# 商品、订单、库存阅读路线

核心业务先读请求链路，不急着改交易逻辑。

## 商品

从 `mall4cloud-product` 开始：

1. 分类和品牌决定商品基础归属。
2. `spu` 保存商品主信息。
3. `sku` 保存具体规格和价格。
4. `sku_stock` 保存库存。
5. 商品搜索还会进入 `mall4cloud-search` 和 Elasticsearch。

## 订单

从 `mall4cloud-order` 开始：

1. `/a/order/confirm` 根据立即购买或购物车结算组装购物项，按店铺拆分并计算金额，同时把确认结果和防重复提交标记写入 Redis。
2. `/a/order/submit` 从缓存取确认结果，校验未过期且未重复提交后保存订单、订单项和收货地址快照。
3. 提交订单后通过 `SkuStockLockFeignClient.lock` 锁定库存，并发送 `ORDER_CANCEL_TOPIC` 延迟消息；`CANCEL_ORDER_DELAY_LEVEL = 16`，对应 RocketMQ 30 分钟延迟级别。
4. `sku_stock_lock` 保存锁定记录，商品服务还会发送比取消订单晚一级的库存解锁延迟消息。
5. 支付入口是 `/mall4cloud_payment/pay/order`。当前代码会创建 `pay_info`，再直接调用 `PayNoticeController.submit` 模拟支付成功，然后通过 `ORDER_NOTIFY_TOPIC` 通知订单服务、通过 `ORDER_NOTIFY_STOCK_TOPIC` 通知商品服务扣减实际库存。

这里有一个代码风险：`PayController` 里仍硬编码了 `http://192.168.1.17:8126/mall4cloud_payment` 作为回调前缀，Nacos 里也存在 `application.domainUrl` 配置项。接入第三方支付前，要先把回调域名配置收口，不能直接当成生产可用配置。

## 库存

库存不是简单减字段。当前项目有 `sku_stock_lock` 锁定记录，订单、商品服务、RocketMQ、Seata 都可能参与。

## 检查题

问题：学习二开时，为什么不先改订单状态流？

答案：订单状态流牵涉支付、库存、消息和事务，风险比普通后台配置功能高很多。

为什么：一个状态字段变化可能要求多个服务和多张表保持一致，适合作为后续专题，不适合作为第一天练习。

---

### Doc 7 数据模型 1 数据库阅读路线 (`doc/7-数据模型/1-数据库阅读路线.md`)

# 数据库阅读路线

读数据库不要从所有表平铺开始。先读能解释“登录、菜单、商品、订单”的表。

## 第一步：登录和权限

先读：

- `mall4cloud_auth.auth_account`
- `mall4cloud_rbac.menu`
- `mall4cloud_rbac.menu_permission`
- `mall4cloud_rbac.role`
- `mall4cloud_rbac.role_menu`
- `mall4cloud_rbac.user_role`
- `mall4cloud_platform.sys_user`
- `mall4cloud_multishop.shop_user`

这组表能解释：谁登录、属于平台还是商家、看见哪些菜单、拥有哪些按钮权限。

## 第二步：商品和 SKU

再读：

- `mall4cloud_product.spu`
- `mall4cloud_product.sku`
- `mall4cloud_product.sku_stock`
- `mall4cloud_product.sku_stock_lock`
- `mall4cloud_product.spu_attr_value`
- `mall4cloud_product.spu_sku_attr_value`
- `mall4cloud_product.category`
- `mall4cloud_product.brand`

这组表能解释：商品主信息、具体 SKU、库存、销售属性、分类和品牌。

## 第三步：订单

再读：

- `mall4cloud_order.order`
- `mall4cloud_order.order_item`
- `mall4cloud_order.order_addr`

这组表能解释：订单头、订单明细和收货地址快照。

## 第四步：二开建表习惯

新表优先沿用这些习惯：

- 主键使用 `bigint`，字段名用下划线。
- 常见字段包含 `create_time`、`update_time`。
- 多端或多店铺数据要考虑 `shop_id`、`tenant_id`、`user_id`。
- 状态字段要明确枚举值，例如 `1:enable, 0:disable, -1:deleted`。
- 软删除不是全表统一字段，需先看同业务域已有表如何处理。
- Seata 参与的库有 `undo_log`。

## 检查题

问题：为什么权限表应该先读，而不是最后读？

答案：后台二开离不开菜单、按钮和接口权限，权限表决定新页面能不能被看见、按钮能不能显示、接口能不能访问。

为什么：前端动态路由、`v-permission`、后端 `AuthFilter` 都依赖 rbac 表。

---

### Doc 7 数据模型 2 权限商品订单核心表 (`doc/7-数据模型/2-权限商品订单核心表.md`)

# 权限、商品、订单核心表速查

本页只讲启动和二开最常用的表。

## 权限表

| 表 | 作用 | 关键字段 |
| --- | --- | --- |
| `auth_account` | 统一账号 | `username`、`password`、`sys_type`、`user_id`、`tenant_id`、`is_admin` |
| `menu` | 后台菜单和动态路由 | `menu_id`、`parent_id`、`biz_type`、`path`、`component` |
| `menu_permission` | 按钮和接口权限 | `permission`、`uri`、`method`、`menu_id`、`biz_type` |
| `role` | 角色 | `role_id`、`biz_type`、`tenant_id` |
| `role_menu` | 角色拥有的菜单和按钮 | `role_id`、`menu_id`、`menu_permission_id` |
| `user_role` | 用户拥有的角色 | `user_id`、`role_id` |

`sys_type` 和 `biz_type` 的常见含义：`1` 商家端，`2` 平台端。

## 商品表

| 表 | 作用 | 关键字段 |
| --- | --- | --- |
| `spu` | 商品主信息 | `spu_id`、`brand_id`、`category_id`、`shop_id`、`name`、`status` |
| `sku` | 具体销售 SKU | `sku_id`、`spu_id`、`sku_name`、`attrs`、`price_fee`、`status` |
| `sku_stock` | SKU 库存 | `sku_id`、`actual_stock`、`lock_stock`、`stock` |
| `sku_stock_lock` | 下单库存锁定 | `spu_id`、`sku_id`、`order_id`、`status`、`count` |
| `spu_detail` | 商品详情 | `spu_id`、`detail` |
| `spu_sku_attr_value` | SKU 销售属性 | `spu_id`、`sku_id`、`attr_name`、`attr_value_name` |

价格字段如 `price_fee` 用整数保存，二开时不要随意改成浮点金额。

## 订单表

| 表 | 作用 | 关键字段 |
| --- | --- | --- |
| `order` | 订单主表 | `order_id`、`shop_id`、`user_id`、`total`、`status`、`is_payed` |
| `order_item` | 订单商品明细 | `order_id`、`spu_id`、`sku_id`、`count`、`price`、`spu_total_amount` |
| `order_addr` | 收货地址快照 | `order_addr_id`、`user_id`、`consignee`、`mobile`、`addr` |
| `pay_info` | 支付记录 | `pay_id`、`user_id`、`pay_amount`、`pay_status`、`order_ids`、`version` |

订单状态当前 SQL 注释中说明：`1` 待付款，`2` 待发货，`3` 待收货，`5` 成功，`6` 失败。

订单主表中以下字段在读订单状态时需要一起看：

| 字段 | 含义 |
| --- | --- |
| `close_type` | 订单关闭原因，SQL 注释含 `1` 超时未支付、`4` 买家取消、`15` 货到付款交易 |
| `delete_status` | 用户侧删除状态：`0` 未删除、`1` 回收站、`2` 永久删除 |
| `version` | 订单版本号，SQL 注释为每处理一次订单版本号加一 |
| `order_addr_id` | 指向 `order_addr`，保存下单时的收货地址快照 |

## 关系速记

- 一个账号 `auth_account` 通过 `sys_type + user_id` 指向平台用户或商家用户。
- 一个用户通过 `user_role` 拥有多个角色。
- 一个角色通过 `role_menu` 拥有菜单和按钮权限。
- 一个商品 `spu` 有多个 `sku`，每个 `sku` 对应库存。
- 一个订单 `order` 有多个 `order_item`，每个明细记录当时的 `spu_id`、`sku_id`、价格和数量。
- 一个订单保存一个 `order_addr_id`，避免用户后续修改地址影响已下单记录展示。

## 检查题

问题：前端按钮权限编码应该去哪个表找？

答案：`mall4cloud_rbac.menu_permission.permission`。

为什么：前端 `v-permission` 使用的就是这个编码，后端接口权限也用它匹配用户权限。

---

### Doc 7 数据模型 3 带答案的表结构检查题 (`doc/7-数据模型/3-带答案的表结构检查题.md`)

# 带答案的表结构检查题

这些题不是考试，是帮助读者确认自己已经读懂关键关系。

## 权限

问题：一个普通平台用户登录后，为什么可能看不到某个菜单？

答案：因为用户不是管理员时，前端会根据 `/menu/list_menu_ids` 返回的菜单 ID 过滤动态路由；如果角色没有关联该菜单，菜单就不会显示。

为什么：用户通过 `user_role` 找到角色，角色通过 `role_menu.menu_id` 拥有菜单，前端再用这些菜单 ID 过滤 `menu` 路由。

问题：按钮显示和接口放行是不是同一件事？

答案：不是。

为什么：按钮显示由前端 `v-permission` 判断 `menu_permission.permission`；接口放行由后端根据 `menu_permission.uri + method` 和用户权限判断。

## 商品

问题：SPU 和 SKU 的区别是什么？

答案：SPU 是商品主信息，SKU 是具体可销售规格。

为什么：`spu` 保存商品名称、店铺、分类、上下架状态；`sku` 保存 `spu_id`、规格属性、价格和 SKU 状态。

问题：库存为什么不只放在 `sku` 表？

答案：库存独立放在 `sku_stock`，锁定记录放在 `sku_stock_lock`。

为什么：库存会被下单、取消、支付等链路频繁改变，独立表和锁定表便于处理可售库存、实际库存、锁定库存和订单锁定记录。

## 订单

问题：订单明细为什么要保存商品名称、SKU 名称和价格？

答案：因为订单需要保留下单时的商品快照。

为什么：商品后续可能改名、改价或下架，`order_item` 仍要展示和结算下单时的信息。

问题：订单表里的 `total` 和订单项里的 `spu_total_amount` 可以直接改成小数吗？

答案：不要。

为什么：当前金额字段以整数保存，随意改成小数会影响支付、结算、前后端展示和已有数据。

## 二开

问题：新增“公告管理”表是否一定要有 `shop_id`？

答案：不一定，要看公告是平台全局还是店铺独立。

为什么：平台全局公告可以归平台端管理；店铺独立公告才需要 `shop_id` 或租户维度。建表前先明确数据归属。

问题：新表一定要有 `undo_log` 吗？

答案：新业务表本身不需要 `undo_log` 字段；参与 Seata 分布式事务的数据库需要有独立的 `undo_log` 表。

为什么：`undo_log` 是 Seata AT 模式使用的事务回滚表，不是每张业务表的字段。

---

### Doc 8 部署运维 1 部署路径与上线检查 (`doc/8-部署运维/1-部署路径与上线检查.md`)

# 部署路径与上线检查

部署前先区分三种路径。

## 路径一：本地开发

适合二开：

1. 本地或服务器启动中间件。
2. IDEA 启动后端服务。
3. `pnpm dev` 启动平台端或商家端。
4. 前端 `VITE_APP_BASE_API` 指向网关 `8000`。

## 路径二：单机服务器中间件 + 本地代码

适合团队共享中间件：

1. 服务器用 [中间件 Docker Compose](./2-中间件-docker-compose/README.md) 启动 MySQL、Redis、Nacos 等。
2. 本地服务通过 `NACOS_HOST` 指向服务器。
3. 前端资源地址指向服务器 MinIO。

## 路径三：生产部署

生产部署需要额外确认：

- Nacos、Redis、MySQL、MinIO、RocketMQ、Elasticsearch 不应直接暴露公网。
- RocketMQ dashboard 当前未配置登录保护，不能裸露。
- 前端构建后由 Nginx 提供静态资源。
- 前端 API 地址要指向生产网关域名。
- MinIO 桶策略和资源域名要按生产安全要求配置。

## Nginx 注意点

平台端和商家端当前仓库内的 `nginx.conf` 只配置了静态资源根目录，没有代理后端 API。生产环境更常见做法是：

- 静态资源由 Nginx 服务。
- API 通过 `VITE_APP_BASE_API` 指向网关域名。
- 或在外层 Nginx 单独配置 API 反向代理。

不要假设前端工程里的 Nginx 配置已经覆盖生产代理。

## 上线检查

| 检查项 | 正确结果 |
| --- | --- |
| 网关 | `8000` 或生产域名可访问 |
| Nacos | 服务已注册，配置可读取 |
| MySQL | 业务库、Nacos 库、Seata 库存在 |
| Redis | 密码和 Nacos 配置一致 |
| MinIO | 桶存在，资源地址可访问 |
| 前端 | `VITE_APP_BASE_API` 指向网关 |
| 登录 | 平台端或商家端能登录 |
| 权限 | 普通角色权限符合预期 |
| 日志 | 后端错误能在对应服务日志中定位 |

## 检查题

问题：生产环境只改前端 `.env.production` 就够了吗？

答案：不够。

为什么：还要核对 Nacos 中数据源、Redis、OSS、RocketMQ、Seata、网关路由，以及中间件自身的网络和安全暴露。

---

### Doc 8 部署运维 3 已知风险与待核对项 (`doc/8-部署运维/3-已知风险与待核对项.md`)

# 已知风险与待核对项

这页记录不能直接按生产配置使用的内容。

## 已知风险

| 风险 | 当前处理 |
| --- | --- |
| Docker 安装教程过于通用 | 本文不展开，按 Docker 官方文档安装 |
| 部分截图可能和当前 UI 不完全一致 | 只作为操作界面参考，不作为验证依据 |
| 前端环境变量 | 当前必须使用 `VITE_APP_*` |
| RocketMQ dashboard 未配置登录保护 | 生产环境不要公网暴露 |
| Seata 控制台 | 当前 Seata 2.6 compose 只使用 `8091` |
| Nacos 默认账号 | 本目录初始化 SQL 写入 `nacos / 80jpnH4.r5g`，而部分服务 bootstrap 默认值仍写着 `nacos/nacos`；如果启用 Nacos 鉴权，必须把两边统一 |
| 搜索同步 SQL | `update mall4cloud_product set update_time = now()`、`update mall4cloud_order set update_time = now()` 不匹配当前表名，不能照搬 |
| 支付回调地址 | `PayController` 当前仍硬编码网关地址，支付专题前不能把它写成生产可用配置 |

## 当前未展开的内容

- 移动端/小程序源码结构。
- 订单退款、支付、结算、库存扣减的二开教程。
- 生产级高可用部署。
- 商业授权和企业版本差异的技术交付细节。

这些内容需要单独设计和验证，不放在启动文档里。

## 检查题

问题：未经验证的命令，可以直接照搬吗？

答案：不可以。

为什么：当前项目使用 Spring Boot 4、Vue 3、Vite、pnpm，命令必须和当前仓库配置一致。

---

### Doc 9 故障排查 1 启动登录接口常见问题 (`doc/9-故障排查/1-启动登录接口常见问题.md`)

# 启动、登录、接口常见问题

先按现象排查，不要一上来改代码。

## 服务连不上 Nacos

答案：先核对 `NACOS_HOST`、`NACOS_PORT`，再看 Nacos 容器、控制台和客户端端口。

为什么：各服务默认 Nacos 地址是 `${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848}`，如果你的环境不是这个 IP，就要改环境变量或启动配置。当前 Compose 的 Nacos v3 控制台是 `8080`，服务客户端主要看 `8848`，同时要放开 `9848`、`9849`。

排查顺序：

1. 在服务器执行 `docker ps -a`，确认 `mall4cloud-nacos` 没有反复重启。
2. 用 `docker logs -f mall4cloud-nacos` 看是否 MySQL 连接、账号或初始化库错误。
3. 打开 `http://<IP>:8080/index.html`，确认能登录控制台。
4. 在配置列表里确认 `application.yml`、`mall4cloud-gateway.yml` 等配置存在。
5. 如果 Nacos 配置已改但服务仍读到缓存地址，重启 Nacos 容器后再重启 Java 服务。

## 前端登录请求 404

答案：先核对 `VITE_APP_BASE_API` 是否指向网关 `8000`，不是某个业务服务端口。

为什么：前端接口带 `/mall4cloud_auth`、`/mall4cloud_rbac` 等前缀，网关负责按前缀转发。

## 前端请求 503

答案：先查网关路由目标服务是否启动并注册到 Nacos。

为什么：503 常见于网关能收到请求，但后面的 auth、rbac、platform、multishop、biz 等服务没有可用实例。

## 前端登录返回未授权

答案：检查账号密码、auth 服务、Redis、数据库初始化是否一致。

为什么：登录依赖 `mall4cloud_auth.auth_account`，Token 校验依赖 auth 服务和 Redis。

## 滑动验证码加载或校验失败

答案：先用浏览器 F12 的 Network 看验证码接口请求，再查 Redis。

为什么：验证码校验依赖 Redis。Redis 未启动、密码不一致，或 Nacos `application.yml` 中 Redis 地址仍指向错误 IP，都会导致验证码正常显示但校验失败。

## 登录后菜单不显示

答案：先判断用户是否管理员；非管理员再查角色是否拥有菜单。

为什么：非管理员会通过 `list_menu_ids` 获取菜单 ID，前端只显示这些 ID 对应的路由。

## 按钮不显示

答案：查按钮上的 `v-permission` 编码是否存在于用户权限列表。

为什么：前端 `src/directive/permission` 会从用户 store 的 `roles` 里查编码，没有就移除按钮。

## 接口 401 或权限失败

答案：查 `Authorization` 请求头、Token 是否过期、`menu_permission.uri` 和 `method` 是否匹配。

为什么：后端 `AuthFilter` 先校验 Token，再校验平台端/商家端 RBAC 权限。

## 图片不显示

答案：核对 MinIO 桶、桶策略、Nacos `biz.oss.resources-url`、前端 `VITE_APP_RESOURCES_URL`。

为什么：上传、返回路径、前端拼接地址、桶读取权限任何一处错都会导致图片不可见。

## 商品发布后搜索不到

答案：除 MySQL 外，还要查 Canal、RocketMQ、Elasticsearch。

为什么：搜索列表依赖 ES 索引，索引更新链路不等于商品表写入成功。

排查顺序：

1. MySQL 执行 `SHOW MASTER STATUS`，确认 binlog 可读。
2. 检查 `canal/conf/example/instance.properties` 中 MySQL 地址、`mysql-binlog.*` 文件名、`canal` 账号密码。
3. 检查 `canal/conf/canal.properties` 中 `rocketmq.namesrv.addr`。
4. 看 `canal/logs/example/example.log` 是否还在报错。
5. 看 RocketMQ dashboard 是否收到 `canal-topic`。
6. 看 `mall4cloud-search` 是否消费 `canal-topic`，ES 中是否已创建 `product`、`order` mapping。

## RocketMQ 无法发送 topic

答案：遇到 `No route info of this topic: stock-unlock-topic`，先查 RocketMQ broker 配置挂载和目录权限。

为什么：一种常见情况是 broker 没读到挂载的配置，导致 topic 路由不符合项目预期。当前中间件目录要求启动前给 `rocketmq/` 目录足够权限。

## Seata 连接失败

答案：先核对 `seata/application.yml` 的 MySQL 地址和 `application.yml` 中 `seata.service.grouplist.default`。

为什么：当前 Seata 2.6 compose 只暴露 `8091`，本仓库没有使用 `7091` 控制台配置。

## 检查题

问题：跨域报错时，第一反应是改业务 Controller 吗？

答案：不是，先查网关和前端 API 地址。

为什么：跨域主要发生在浏览器访问网关时，Nacos 中网关配置包含 CORS 规则，前端也必须请求正确网关地址。

---

### Doc 8 部署运维 2 中间件 Docker Compose Docker Compose Native.Yaml (`doc/8-部署运维/2-中间件-docker-compose/docker-compose-native.yaml`)

version: "3.5"

services:
  mall4cloud-mysql:
    image: mysql:8.0
    container_name: mall4cloud-mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=80jpnH4.r5g
    network_mode: "host"
    expose:
      - 3306
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d
      - ./mysql/initdb:/docker-entrypoint-initdb.d

  mall4cloud-minio:
    image: minio/minio:RELEASE.2024-04-18T19-09-19Z
    container_name: mall4cloud-minio
    restart: always
    command: server /data --console-address ":9001"
    network_mode: "host"
    expose:
      - 9000
      - 9001
    volumes:
      - ./minio/data:/data
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=80jpnH4.r5g
      - MINIO_BROWSER_REDIRECT_URL=http://192.168.1.46:9001

  mall4cloud-redis:
    image: redis:7.0
    container_name: mall4cloud-redis
    restart: always
    network_mode: "host"
    command: redis-server --requirepass 80jpnH4.r5g
    expose:
      - 6379


  mall4cloud-nacos:
    image: nacos/nacos-server:v3.1.1
    container_name: mall4cloud-nacos
    restart: always
    depends_on:
      - mall4cloud-mysql
    network_mode: "host"
    expose:
      - 8848
      - 9848
      - 9849
      - 8080
    environment:
      - JVM_XMS=256m
      - JVM_XMX=256m
      - MODE=standalone
      - PREFER_HOST_MODE=hostname
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=192.168.1.46
      - MYSQL_SERVICE_DB_NAME=mall4cloud_nacos
      - MYSQL_SERVICE_USER=root
      - MYSQL_SERVICE_PASSWORD=80jpnH4.r5g
      - NACOS_AUTH_ENABLE=true
      - NACOS_AUTH_TOKEN=TWFsbDRqTWFsbDRjbG91ZE1hbGw0ak1hbGw0Y2xvdWRNYWxsNGpNYWxsNGNsb3Vk
      - NACOS_AUTH_IDENTITY_KEY=mall4jmall4jmall4jmall4jmall4jmall4jmall4jmall4j
      - NACOS_AUTH_IDENTITY_VALUE=mall4cloudmall4cloudmall4cloudmall4cloudmall4cloudmall4cloudmall4cloud
    volumes:
      - ./nacos/logs:/home/nacos/logs

  mall4cloud-seata:
    image: apache/seata-server:2.6.0
    container_name: mall4cloud-seata
    restart: always
    network_mode: "host"
    expose:
      - 8091
    environment:
      - TZ
      - STORE_MODE=db
      - SEATA_IP=192.168.1.46
      - SEATA_PORT=8091
    volumes:
      - ./seata/application.yml:/seata-server/resources/application.yml

  mall4cloud-elasticsearch:
    image: elasticsearch:7.17.21
    container_name: mall4cloud-elasticsearch
    restart: always
    network_mode: "host"
    expose:
      - 9200
      - 9300
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
      - ELASTICSEARCH_USERNAME=elastic
      - ELASTIC_PASSWORD=80jpnH4.r5g
      - xpack.security.enabled=true
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
      - ./elasticsearch/data:/usr/share/elasticsearch/data
      - ./elasticsearch/plugins:/usr/share/elasticsearch/plugins

  mall4cloud-canal:
    image: canal/canal-server:v1.1.7
    container_name: mall4cloud-canal
    restart: always
    network_mode: "host"
    expose:
      - 11111
    volumes:
      - ./canal/conf/example:/home/admin/canal-server/conf/example
      - ./canal/conf/canal.properties:/home/admin/canal-server/conf/canal.properties
      - ./canal/logs:/home/admin/canal-server/logs


  mall4cloud-rocketmq-namesrv:
    image: apache/rocketmq:5.2.0
    container_name: mall4cloud-rocketmq-namesrv
    restart: always
    network_mode: "host"
    expose:
      - 9876
    volumes:
      - ./rocketmq/namesrv/logs:/home/rocketmq/logs
      - ./rocketmq/namesrv/store:/home/rocketmq/store
    environment:
      JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128M"
    command: ["sh","mqnamesrv"]

  mall4cloud-rocketmq-broker:
    image: apache/rocketmq:5.2.0
    container_name: mall4cloud-rocketmq-broker
    restart: always
    network_mode: "host"
    expose:
      - 10909
      - 10911
    volumes:
      - ./rocketmq/broker/logs:/home/rocketmq/logs
      - ./rocketmq/broker/store:/home/rocketmq/store
      - ./rocketmq/broker/conf/broker.conf:/etc/rocketmq/broker.conf
    environment:
      JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128M -XX:-AssumeMP"
    command: ["sh","mqbroker","-c","/etc/rocketmq/broker.conf","-n","192.168.1.46:9876","autoCreateTopicEnable=true"]
    depends_on:
      - mall4cloud-rocketmq-namesrv


  mall4cloud-rocketmq-dashboard:
    image: fsckzy01/rocketmq-dashboard:1.0.1
    container_name: mall4cloud-rocketmq-dashboard
    restart: always
    ports:
      - 8180:8082
    environment:
      JAVA_OPTS: "-Drocketmq.namesrv.addr=192.168.1.46:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false"
    depends_on:
      - mall4cloud-rocketmq-namesrv

---

### Doc 8 部署运维 2 中间件 Docker Compose Docker Compose.Yaml (`doc/8-部署运维/2-中间件-docker-compose/docker-compose.yaml`)

version: "3.5"

services:
  mall4cloud-mysql:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/mysql:8.0.35
    container_name: mall4cloud-mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=80jpnH4.r5g
    network_mode: "host"
    expose:
      - 3306
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d
      - ./mysql/initdb:/docker-entrypoint-initdb.d

  mall4cloud-minio:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/minio:RELEASE.2024-04-18T19-09-19Z
    container_name: mall4cloud-minio
    restart: always
    command: server /data --console-address ":9001"
    network_mode: "host"
    expose:
      - 9000
      - 9001
    volumes:
      - ./minio/data:/data
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=80jpnH4.r5g
      - MINIO_BROWSER_REDIRECT_URL=http://192.168.1.46:9001

  mall4cloud-redis:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/redis:7.0
    container_name: mall4cloud-redis
    restart: always
    network_mode: "host"
    command: redis-server --requirepass 80jpnH4.r5g
    expose:
      - 6379


  mall4cloud-nacos:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/nacos-server:v3.1.1
    container_name: mall4cloud-nacos
    restart: always
    depends_on:
      - mall4cloud-mysql
    network_mode: "host"
    expose:
      - 8848
      - 9848
      - 9849
      - 8080
    environment:
      - JVM_XMS=256m
      - JVM_XMX=256m
      - MODE=standalone
      - PREFER_HOST_MODE=hostname
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=192.168.1.46
      - MYSQL_SERVICE_DB_NAME=mall4cloud_nacos
      - MYSQL_SERVICE_USER=root
      - MYSQL_SERVICE_PASSWORD=80jpnH4.r5g
      - NACOS_AUTH_ENABLE=true
      - NACOS_AUTH_TOKEN=TWFsbDRqTWFsbDRjbG91ZE1hbGw0ak1hbGw0Y2xvdWRNYWxsNGpNYWxsNGNsb3Vk
      - NACOS_AUTH_IDENTITY_KEY=mall4jmall4jmall4jmall4jmall4jmall4jmall4jmall4j
      - NACOS_AUTH_IDENTITY_VALUE=mall4cloudmall4cloudmall4cloudmall4cloudmall4cloudmall4cloudmall4cloud
    volumes:
      - ./nacos/logs:/home/nacos/logs

  mall4cloud-seata:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/seata-server:2.6.0
    container_name: mall4cloud-seata
    restart: always
    network_mode: "host"
    expose:
      - 8091
    environment:
      - TZ
      - STORE_MODE=db
      - SEATA_IP=192.168.1.46
      - SEATA_PORT=8091
    volumes:
      - ./seata/application.yml:/seata-server/resources/application.yml

  mall4cloud-elasticsearch:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/elasticsearch:7.17.21
    container_name: mall4cloud-elasticsearch
    restart: always
    network_mode: "host"
    expose:
      - 9200
      - 9300
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
      - ELASTICSEARCH_USERNAME=elastic
      - ELASTIC_PASSWORD=80jpnH4.r5g
      - xpack.security.enabled=true
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
      - ./elasticsearch/data:/usr/share/elasticsearch/data
      - ./elasticsearch/plugins:/usr/share/elasticsearch/plugins

  mall4cloud-canal:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/canal-server:v1.1.7
    container_name: mall4cloud-canal
    restart: always
    network_mode: "host"
    expose:
      - 11111
    volumes:
      - ./canal/conf/example:/home/admin/canal-server/conf/example
      - ./canal/conf/canal.properties:/home/admin/canal-server/conf/canal.properties
      - ./canal/logs:/home/admin/canal-server/logs


  mall4cloud-rocketmq-namesrv:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/rocketmq:5.2.0
    container_name: mall4cloud-rocketmq-namesrv
    restart: always
    network_mode: "host"
    expose:
      - 9876
    volumes:
      - ./rocketmq/namesrv/logs:/home/rocketmq/logs
      - ./rocketmq/namesrv/store:/home/rocketmq/store
    environment:
      JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128M"
    command: ["sh","mqnamesrv"]

  mall4cloud-rocketmq-broker:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/rocketmq:5.2.0
    container_name: mall4cloud-rocketmq-broker
    restart: always
    network_mode: "host"
    expose:
      - 10909
      - 10911
    volumes:
      - ./rocketmq/broker/logs:/home/rocketmq/logs
      - ./rocketmq/broker/store:/home/rocketmq/store
      - ./rocketmq/broker/conf/broker.conf:/etc/rocketmq/broker.conf
    environment:
      JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128M -XX:-AssumeMP"
    command: ["sh","mqbroker","-c","/etc/rocketmq/broker.conf","-n","192.168.1.46:9876","autoCreateTopicEnable=true"]
    depends_on:
      - mall4cloud-rocketmq-namesrv


  mall4cloud-rocketmq-dashboard:
    image: registry.cn-hongkong.aliyuncs.com/mall4j-images/rocketmq-dashboard:1.0.1
    container_name: mall4cloud-rocketmq-dashboard
    restart: always
    ports:
      - 8180:8082
    environment:
      JAVA_OPTS: "-Drocketmq.namesrv.addr=192.168.1.46:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false"
    depends_on:
      - mall4cloud-rocketmq-namesrv

---

### Doc 8 部署运维 2 中间件 Docker Compose Seata Application.Yml (`doc/8-部署运维/2-中间件-docker-compose/seata/application.yml`)

spring:
  application:
    name: seata-server
  main:
    web-application-type: none


logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: file
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: file
  store:
    # support: file 、 db 、 redis
    mode: db
    db:
      datasource: druid
      dbType: mysql
      # 需要根据mysql的版本调整driverClassName
      # mysql8及以上版本对应的driver：com.mysql.cj.jdbc.Driver
      # mysql8以下版本的driver：com.mysql.jdbc.Driver
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.1.46:3306/mall4cloud_seata?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&rewriteBatchedStatements=true
      user: root
      password: 80jpnH4.r5g

  #  server:
  #    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

---

--- METRICS ---
- Files Extracted: 43
- Estimated Token Budget: ~17065 tokens
- Canonical Reference: https://codewiki.google/github.com/gz-yami/mall4cloud
