# Mall — User Guide

## 1. Quick Start

Mall is a full-featured e-commerce platform built on Spring Boot, providing both a back-office admin system and a customer-facing shopping portal as REST APIs. It covers product management, order processing, promotions, member management, and full-text product search.

**Key features:**

- Complete product lifecycle management (categories, brands, attributes, SKUs)
- Shopping cart with promotion calculation (ladders, full-reduction, coupons)
- Order placement with automatic timeout cancellation via RabbitMQ
- Alipay payment integration (desktop, mobile, async notification)
- Elasticsearch-powered product search with aggregation
- JWT-based stateless authentication with dynamic URL-level authorization
- MinIO and Aliyun OSS file storage
- Swagger UI auto-generated API documentation

**Prerequisites:**

- JDK 8+
- Maven 3.x
- MySQL 5.7+, Redis 7+, MongoDB 4+, RabbitMQ 3.9+, Elasticsearch 7.17.x

**Quickest path to running:**

```bash
# Start infrastructure services
docker compose -f document/docker/docker-compose-env.yml up -d

# Import the database schema
mysql -u root -proot < document/sql/mall.sql

# Build all modules
mvn clean package -DskipTests

# Run the admin backend
java -jar mall-admin/target/mall-admin-1.0-SNAPSHOT.jar --spring.profiles.active=dev
```

The admin API is now accessible at `http://localhost:8080`. Swagger UI is at `http://localhost:8080/swagger-ui/`.

> [!TIP]
> Use the included Postman collections (`document/postman/`) for quick API exploration. Import `mall-admin.postman_collection.json` and `mall-portal.postman_collection.json` into Postman.

**What to try next:**

- Log in via `POST /admin/login` with `{"username": "admin", "password": "123456"}`
- Browse products via `GET /product/list`
- Start the portal module for the customer-facing API on port 8085

---

## 2. Installation

### System Requirements

| Component | Minimum Version | Purpose |
|-----------|----------------|---------|
| JDK | 8 | Runtime |
| Maven | 3.x | Build |
| MySQL | 5.7 | Primary database |
| Redis | 7 | Caching, session |
| MongoDB | 4 | Member browsing history, collections |
| RabbitMQ | 3.9 | Order timeout cancellation |
| Elasticsearch | 7.17.x | Product search |
| MinIO | Latest | Object storage (product images) |

### Infrastructure Setup with Docker Compose

The recommended approach uses the provided Docker Compose file to run all infrastructure:

```bash
docker compose -f document/docker/docker-compose-env.yml up -d
```

This starts:

| Service | Port | Purpose |
|---------|------|---------|
| MySQL | 3306 | Database (root password: `root`) |
| Redis | 6379 | Cache with AOF persistence |
| Nginx | 80 | Static assets / frontend proxy |
| RabbitMQ | 5672 / 15672 | Message queue / management UI |
| Elasticsearch | 9200 / 9300 | Search engine |
| Logstash | 4560-4563 | Log collection |
| Kibana | 5601 | Log visualization |
| MongoDB | 27017 | Document store |
| MinIO | 9090 / 9001 | Object storage (API / console) |

> [!IMPORTANT]
> After starting MySQL, import the database schema before running any application module:
> ```bash
> mysql -u root -proot < document/sql/mall.sql
> ```
> The SQL file creates the `mall` database with all required tables (~70 tables).

### Building the Project

```bash
mvn clean package -DskipTests
```

This produces executable JARs in each module's `target/` directory.

### Running Application Modules

Start modules in this order (admin is independent; portal requires RabbitMQ and MongoDB; search requires Elasticsearch):

```bash
# Admin back-office API (port 8080)
java -jar mall-admin/target/mall-admin-1.0-SNAPSHOT.jar --spring.profiles.active=dev

# Portal customer API (port 8085)
java -jar mall-portal/target/mall-portal-1.0-SNAPSHOT.jar --spring.profiles.active=dev

# Search service (port 8081)
java -jar mall-search/target/mall-search-1.0-SNAPSHOT.jar --spring.profiles.active=dev
```

### Docker Deployment (Production)

Build Docker images via Maven:

```bash
mvn clean package -DskipTests -Pdocker
```

Or use the provided deployment scripts:

```bash
# Deploy mall-admin container
bash document/sh/mall-admin.sh

# Deploy mall-portal container
bash document/sh/mall-portal.sh

# Deploy mall-search container
bash document/sh/mall-search.sh
```

> [!WARNING]
> The deployment scripts use `--link` for container networking (legacy Docker feature). They also hard-delete dangling images. Ensure your container names match: `mysql`, `redis`, `mongo`, `rabbitmq`, `elasticsearch`.

### Post-Installation Verification

```bash
# Check admin API health
curl http://localhost:8080/actuator/health
```

```json
{"status":"UP"}
```

Access Swagger UI:
- Admin: `http://localhost:8080/swagger-ui/`
- Portal: `http://localhost:8085/swagger-ui/`
- Search: `http://localhost:8081/swagger-ui/`

---

## 3. Configuration

Mall uses Spring Boot profiles for environment-specific configuration. Each module has:
- `application.yml` — shared settings
- `application-dev.yml` — development overrides
- `application-prod.yml` — production overrides

Activate a profile at startup:

```bash
java -jar mall-admin.jar --spring.profiles.active=prod
```

### Key Configuration Areas

| Setting | Location | Purpose |
|---------|----------|---------|
| Database | `spring.datasource.*` | MySQL connection (Druid pool) |
| Redis | `spring.redis.*` | Cache host/port |
| MongoDB | `spring.data.mongodb.*` | Member data store |
| RabbitMQ | `spring.rabbitmq.*` | Message broker |
| Elasticsearch | `spring.elasticsearch.*` | Search cluster |
| JWT | `jwt.secret`, `jwt.expiration`, `jwt.tokenHead` | Auth token config |
| Security whitelist | `secure.ignored.urls` | Paths that skip authentication |
| Aliyun OSS | `aliyun.oss.*` | Object storage credentials |
| MinIO | `minio.*` | MinIO endpoint and credentials |
| Alipay | `alipay.*` | Payment gateway config |
| Swagger | `spring.mvc.pathmatch.matching-strategy` | Must be `ant_path_matcher` |

> [!TIP]
> The `secure.ignored.urls` list controls which endpoints bypass JWT auth. In development, Swagger paths (`/swagger-ui/**`, `/v2/api-docs`, `/swagger-resources/**`) are whitelisted here.

For the full configuration reference (every key, every module), see the Admin Guide.

---

## 4. Core Usage

Mall exposes three REST API services. All responses use a standard envelope:

```json
{
  "code": 200,
  "message": "操作成功",
  "data": { }
}
```

| Code | Meaning |
|------|---------|
| `200` | Success |
| `401` | Not logged in / token expired |
| `403` | Insufficient permissions |
| `404` | Validation failed |
| `500` | Operation failed |

### Authentication

All protected endpoints require a JWT token in the `Authorization` header:

```
Authorization: Bearer <token>
```

**Admin login:**

```bash
curl -X POST http://localhost:8080/admin/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "123456"}'
```

```json
{
  "code": 200,
  "message": "操作成功",
  "data": {
    "token": "eyJhbGciOiJIUzUxMiJ9...",
    "tokenHead": "Bearer "
  }
}
```

**Member login (portal):**

```bash
curl -X POST http://localhost:8085/sso/login \
  -d "username=test&password=123"
```

**Token refresh:**

```bash
curl http://localhost:8080/admin/token/refresh \
  -H "Authorization: Bearer <current-token>"
```

> [!NOTE]
> Tokens have a configurable expiration (default varies by environment). The refresh endpoint re-issues a token only if the current one was issued more than 30 minutes ago — calling it within that window returns the same token.

### Paginated Responses

List endpoints return paginated data:

```json
{
  "code": 200,
  "data": {
    "pageNum": 1,
    "pageSize": 5,
    "totalPage": 10,
    "total": 48,
    "list": [...]
  }
}
```

Use `pageNum` and `pageSize` query parameters to control pagination.

---

### 4.1 Product Management (Admin)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/product/list` | GET | List products (paginated, filterable) |
| `/product/create` | POST | Create a new product |
| `/product/update/{id}` | POST | Update product details |
| `/product/update/deleteStatus` | POST | Batch soft-delete products |
| `/brand/listAll` | GET | List all brands |
| `/brand/list` | GET | Paginated brand list |
| `/brand/create` | POST | Create a brand |
| `/productCategory/list/withChildren` | GET | Category tree |
| `/productCategory/list/{parentId}` | GET | Subcategories |
| `/productAttribute/list/{cid}` | GET | Attributes for a category |
| `/skuStock/update/{pid}` | POST | Update SKU stock |

### 4.2 Order Management (Admin)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/order/list` | GET | List orders with filters |
| `/order/{id}` | GET | Order detail |
| `/order/update/delivery` | POST | Batch ship orders |
| `/order/update/close` | POST | Close orders |
| `/order/update/moneyInfo` | POST | Modify order amounts |
| `/order/update/receiverInfo` | POST | Update shipping address |
| `/returnApply/list` | GET | Return applications |
| `/returnApply/update/status` | POST | Process return |
| `/orderSetting/update/{id}` | POST | Update order settings |

### 4.3 Promotions (Admin)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/coupon/add` | POST | Create coupon |
| `/coupon/update/{id}` | POST | Update coupon |
| `/coupon/delete/{id}` | POST | Delete coupon |
| `/coupon/list` | GET | List coupons |
| `/flash/create` | POST | Create flash sale event |
| `/flash/update/{id}` | POST | Update flash sale |
| `/flashSession/list` | GET | List flash sale sessions |
| `/flashProductRelation/create` | POST | Add product to flash sale |
| `/home/advertise/list` | GET | Homepage ads |
| `/home/brand/list` | GET | Homepage brand recommendations |
| `/home/newProduct/list` | GET | Homepage new arrivals |
| `/home/recommendProduct/list` | GET | Homepage recommended products |

### 4.4 User & Permission Management (Admin)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/admin/list` | GET | List admin users |
| `/admin/register` | POST | Register new admin |
| `/admin/update/{id}` | POST | Update admin user |
| `/admin/updatePassword` | POST | Change password |
| `/role/list` | GET | List roles |
| `/role/create` | POST | Create role |
| `/role/allocResource` | POST | Assign resources to role |
| `/role/allocMenu` | POST | Assign menus to role |
| `/resource/list` | GET | List resources |
| `/resource/create` | POST | Create resource |
| `/menu/treeList` | GET | Menu tree |

### 4.5 Shopping (Portal)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/cart/add` | POST | Add item to cart |
| `/cart/list` | GET | Get cart items |
| `/cart/list/promotion` | GET | Cart with promotion calculations |
| `/cart/update/quantity` | GET | Update item quantity |
| `/cart/update/attr` | POST | Change item specifications |
| `/cart/delete` | POST | Remove items from cart |
| `/cart/clear` | POST | Empty cart |
| `/cart/getProduct/{productId}` | GET | Get product specs for re-selection |

### 4.6 Orders (Portal)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/order/confirmOrder` | POST | Generate order confirmation |
| `/order/generateOrder` | POST | Place order |
| `/order/paySuccess` | POST | Payment success callback |
| `/order/cancelOrder` | POST | Cancel a single order |
| `/order/cancelTimeOutOrder` | POST | Cancel all timed-out orders |

> [!WARNING]
> Orders that are not paid within the configured timeout are automatically cancelled via RabbitMQ dead-letter queue. The timeout is set in the order settings (managed via admin API).

### 4.7 Member Features (Portal)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/sso/register` | POST | Member registration |
| `/sso/login` | POST | Member login |
| `/member/address/list` | GET | Shipping addresses |
| `/member/address/add` | POST | Add address |
| `/member/address/update/{id}` | POST | Update address |
| `/member/address/delete/{id}` | POST | Delete address |
| `/member/coupon/list` | GET | Member's coupons |
| `/member/coupon/add/{couponId}` | POST | Claim a coupon |
| `/member/coupon/list/cart/{type}` | GET | Coupons applicable to cart |
| `/member/attention/add` | POST | Follow a brand |
| `/member/attention/list` | GET | Brand follows |
| `/member/productCollection/add` | POST | Collect a product |
| `/member/productCollection/list` | GET | Product collections |
| `/member/readHistory/list` | GET | Browse history |

### 4.8 Homepage Content (Portal)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/home/content` | GET | Homepage aggregated content |
| `/home/recommendProductList` | GET | Recommended products |
| `/home/productCateList/{parentId}` | GET | Product categories |
| `/home/subjectList` | GET | Subject/topic list |
| `/home/hotProductList` | GET | Hot products |
| `/home/newProductList` | GET | New products |

### 4.9 Payment (Portal)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/alipay/pay` | GET | Desktop Alipay payment page |
| `/alipay/webPay` | GET | Mobile Alipay payment page |
| `/alipay/notify` | POST | Alipay async notification callback |
| `/alipay/query` | GET | Query payment status |

### 4.10 Product Search

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/esProduct/importAll` | POST | Import all products to ES |
| `/esProduct/search` | GET | Search products (keyword, filters) |
| `/esProduct/create/{id}` | POST | Index a single product |
| `/esProduct/delete/{id}` | GET | Remove product from index |
| `/esProduct/delete/batch` | POST | Batch remove from index |

> [!TIP]
> After initial setup, run `POST /esProduct/importAll` to populate the Elasticsearch index with product data from MySQL.

---

## 5. Common Tasks

### Register an Admin and Assign Permissions

1. Log in as the default admin (`admin`/`123456`)
2. `POST /admin/register` with name, username, password, email
3. `GET /role/list` to find the target role ID
4. `POST /admin/role/update?adminId={id}&roleIds={roleId}` to assign roles

### Create a Product with SKU Variants

1. Ensure product categories exist (`GET /productCategory/list/withChildren`)
2. Ensure a brand exists (`GET /brand/listAll`)
3. `POST /product/create` with full product JSON including `skuStockList` array (each entry has `price`, `stock`, `sp1/sp2/sp3` for specification values, `skuCode`)

### Process a Customer Order

1. Customer adds items to cart (`POST /cart/add`)
2. Customer confirms order (`POST /order/confirmOrder`) — returns available addresses, coupons, cart totals
3. Customer places order (`POST /order/generateOrder`) with `memberReceiveAddressId`, `couponId`, `payType`
4. Customer pays (Alipay redirects back; async `/alipay/notify` updates order status)
5. Admin ships order (`POST /order/update/delivery`) with tracking info

### Set Up a Flash Sale Promotion

1. `POST /flash/create` — create flash promotion event (name, start/end dates)
2. `GET /flashSession/list` — list time sessions
3. `POST /flashProductRelation/create` — bind products to session with flash prices

### Import Products to Elasticsearch

```bash
curl -X POST http://localhost:8081/esProduct/importAll \
  -H "Authorization: Bearer <token>"
```

```json
{"code": 200, "message": "操作成功", "data": 48}
```

This syncs all products from MySQL to Elasticsearch for full-text search.

### Handle a Return Application

1. Customer submits return request via portal API
2. Admin lists returns: `GET /returnApply/list`
3. Admin reviews and updates status: `POST /returnApply/update/status` with approval/rejection details

---

## 6. Troubleshooting

### Error: Swagger UI returns 404

**Cause:** Spring Boot 2.6+ defaults to `PathPatternParser` which conflicts with Springfox 3.0.

**Solution:**
Ensure this is set in `application.yml`:
```yaml
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
```

**Prevention:** This is already configured in the project. If upgrading Spring Boot, verify this setting remains.

### Error: `401 Unauthorized` on all endpoints

**Cause:** JWT token missing, expired, or malformed.

**Solution:**
1. Obtain a fresh token via `/admin/login` or `/sso/login`
2. Include it in requests: `Authorization: Bearer <token>`
3. If the token expired, use `/admin/token/refresh` (within token lifetime)

**Prevention:** Implement token refresh logic in your client before expiration.

### Error: `Cannot connect to MySQL` on startup

**Cause:** MySQL container not running or connection credentials wrong.

**Solution:**
1. Verify MySQL is running: `docker ps | grep mysql`
2. Check `application-dev.yml` datasource URL, username, and password match your setup
3. Ensure the `mall` database exists: `mysql -u root -proot -e "SHOW DATABASES;"`

### Error: Orders not being auto-cancelled

**Cause:** RabbitMQ not running or queue configuration issue.

**Solution:**
1. Confirm RabbitMQ is running: `docker ps | grep rabbitmq`
2. Check the management UI at `http://localhost:15672` (guest/guest) — verify `mall.order.cancel` and `mall.order.cancel.ttl` queues exist
3. Verify the portal module started successfully (it registers the queues on boot)

**Prevention:** Always start RabbitMQ before `mall-portal`.

> [!NOTE]
> The order timeout cancellation works via a dead-letter queue. Orders are sent to a TTL queue; when the message expires, RabbitMQ routes it to the cancel queue where `CancelOrderReceiver` processes the cancellation. (Community issue #499)

### Error: Elasticsearch search returns empty results

**Cause:** Product index not populated.

**Solution:**
```bash
curl -X POST http://localhost:8081/esProduct/importAll
```

**Prevention:** Run the import after database changes or on first setup.

### Error: MinIO upload fails with access denied

**Cause:** MinIO credentials or bucket policy not configured.

**Solution:**
1. Access MinIO console at `http://localhost:9001` (minioadmin/minioadmin)
2. Create the required bucket
3. Verify `minio.*` config in `application-dev.yml` matches your MinIO setup

### Error: `NumberFormatException` on Swagger page load

**Cause:** Known Springfox 3.0 issue with certain model annotations.

**Solution:** The project includes `swagger-models:1.6.0` and `swagger-annotations:1.6.0` which fix this. If you see the error after dependency updates, re-pin these versions.

> [!WARNING]
> If running with JDK 11+, the `javax.xml.bind:jaxb-api` dependency is required (already included in the parent POM). Removing it causes `ClassNotFoundException` on JWT operations.

### Error: Flash sale time returns incorrect values

**Cause:** Timezone mismatch between application and database. (Community issue #532)

**Solution:** Ensure both application (`TZ=Asia/Shanghai`) and MySQL (`character-set-server=utf8mb4`) are configured consistently. The Docker Compose files set this via environment variables.

### Error: Coupon discount calculation appears wrong for edge cases

**Cause:** Floating-point precision issues in promotion amount apportionment across order items. (Community issue #356)

**Solution:** This is a known limitation of the current algorithm — fractional cent differences may occur when splitting coupon amounts across multiple items.

---

## 7. FAQ

**Q: Is mall a monolithic application or microservices?**

This repository is the monolithic version. Each module (`mall-admin`, `mall-portal`, `mall-search`) runs as a separate Spring Boot JAR but shares the same database. The author maintains a separate `mall-swarm` repository for the microservices (Spring Cloud) version.

**Q: What is the default admin account?**

Username `admin`, password `123456`. This is seeded in the `mall.sql` database schema.

**Q: Can I use this without Docker?**

Yes. Install MySQL, Redis, MongoDB, RabbitMQ, and Elasticsearch natively, then configure connection details in the `application-dev.yml` files for each module.

**Q: How do I add new API endpoints?**

Create a controller in the appropriate module's `controller` package with `@Controller`, `@Api`, and `@RequestMapping` annotations. The endpoint will automatically appear in Swagger UI.

**Q: Is there English language support?**

The codebase and default data are in Chinese. Community issue #35 requests English translation but it has not been implemented. API responses use Chinese messages by default (e.g., "操作成功" for success).

**Q: How do I customize the JWT secret and expiration?**

Set `jwt.secret` and `jwt.expiration` (in seconds) in the module's `application.yml`. See the Admin Guide for security recommendations.

> [!CAUTION]
> The default JWT secret in the codebase is a weak placeholder. In production, always override `jwt.secret` with a strong, unique value. (See security issue #948)

**Q: How does the dynamic authorization system work?**

Admin module registers all URL resources in the `ums_resource` table. Roles are assigned specific resources. At runtime, `DynamicSecurityFilter` intercepts requests and checks whether the current user's role(s) grant access to the requested URL pattern.

**Q: Can I run just the admin module without portal/search?**

Yes. `mall-admin` only requires MySQL and Redis. MongoDB, RabbitMQ, and Elasticsearch are only needed by `mall-portal` and `mall-search`.

**Q: What is the `mall-demo` module for?**

It's a demonstration/learning module showing Spring Security integration patterns (different from the main `mall-security` module approach). It's not needed for production.

**Q: How do I integrate with a different payment gateway?**

The Alipay integration lives entirely in `mall-portal`'s `AlipayController` and `AlipayService`. Implement a new service following the same pattern and add a controller with your gateway's SDK.

---

_Generated May 2026 from macrozheng/mall at d9501e9. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
