# Web端商户重置到刚注册状态接口文档

## 模块概述

本接口提供一键重置功能，可以将商户状态退回到刚注册时的初始状态，清理所有入住申请、订单、优惠券、验券、消息等相关数据。适用于测试环境或商户需要重新申请入住的场景。

---

## 接口信息

### 重置商户到刚注册状态

#### 接口详情

- **接口名称**: 重置商户到刚注册状态
- **接口路径**: `POST /merchantUser/resetToInitialStatus`
- **请求方式**: POST
- **接口描述**: 一键清理入住申请、订单、优惠券、验券、消息等数据，解绑店铺，将商户退回到刚注册的初始状态
- **登录验证**: ✅ 需要（通过 JWT token 自动获取用户ID）
- **事务保护**: ✅ 使用 @Transactional 保证原子性

---

## 业务逻辑说明

### 重置操作内容

在商户申请入住及业务运营时，系统会进行以下数据变更：

#### **入住申请相关**:
1. ✅ **store_info 表** - 新增店铺记录
2. ✅ **store_user 表** - 更新 storeId、name、idCard
3. ✅ **store_img 表** - 新增图片记录（营业执照、合同、经营许可证）
4. ✅ **tag_store_relation 表** - 新增店铺标签关系
5. ✅ **life_notice 表** - 新增入住申请通知

#### **业务运营相关**:
6. ✅ **life_user_order 表** - 订单记录（用户购买团购券、代金券的订单）
7. ✅ **order_coupon_middle 表** - 验券记录（订单-优惠券中间表）
8. ✅ **life_coupon 表** - 团购券（店铺发布的团购券）
9. ✅ **life_discount_coupon 表** - 代金券（店铺发布的代金券）
10. ✅ **life_discount_coupon_user 表** - 用户领取的优惠券
11. ✅ **life_notice 表** - 所有通知消息（订单、优惠券、系统通知等）

#### **其他**:
12. ⚠️ **Redis** - 地理位置信息（需手动清理）

**重置接口会清理以上所有数据，恢复到刚注册状态。**

---

### 重置流程

```
┌─────────────────────────────────────────────┐
│  1. 获取当前登录用户                          │
│  userId = LoginUserUtil.getCurrentUserId()  │
│  storeUser = selectById(userId)              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  2. 检查是否有关联店铺                        │
│  IF storeId == null                          │
│    返回成功（无需重置）                       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  3. 删除店铺图片                              │
│  DELETE FROM store_img                       │
│  WHERE store_id = ? AND img_type IN (14,15,25)│
│  - 14: 营业执照                              │
│  - 15: 合同图片                              │
│  - 25: 经营许可证                            │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  4. 删除店铺标签关系                          │
│  DELETE FROM tag_store_relation              │
│  WHERE store_id = ?                          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  5. 删除店铺信息                              │
│  DELETE FROM store_info                      │
│  WHERE id = ?                                │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  6. 清理入住申请通知                          │
│  DELETE FROM life_notice                     │
│  WHERE receiver_id = 'store_手机号'          │
│    AND title = '入住店铺申请'                │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  7. 查询该店铺的所有订单ID                    │
│  SELECT id FROM life_user_order              │
│  WHERE store_id = ?                          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  8. 删除订单-优惠券中间表（验券记录）         │
│  DELETE FROM order_coupon_middle             │
│  WHERE order_id IN (订单ID列表)              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  9. 删除该店铺的所有订单记录                  │
│  DELETE FROM life_user_order                 │
│  WHERE store_id = ?                          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  10. 删除该店铺发布的团购券                   │
│  DELETE FROM life_coupon                     │
│  WHERE store_id = ?                          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  11. 删除该店铺发布的代金券                   │
│  DELETE FROM life_discount_coupon            │
│  WHERE store_id = ?                          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  12. 删除用户领取的优惠券                     │
│  DELETE FROM life_discount_coupon_user       │
│  WHERE user_id = ?                           │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  13. 清理所有通知消息                         │
│  DELETE FROM life_notice                     │
│  WHERE receiver_id = 'store_手机号'          │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  14. 重置用户信息                             │
│  UPDATE store_user SET                       │
│    store_id = NULL,                          │
│    name = NULL,                              │
│    id_card = NULL                            │
│  WHERE id = ?                                │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  15. 返回结果                                 │
│  返回成功："已退回到刚注册状态"              │
└─────────────────────────────────────────────┘
```

---

## 请求参数

**无参数**

从 JWT token 自动获取当前登录用户的 userId。

---

## 请求示例

```http
POST /merchantUser/resetToInitialStatus
Headers:
  Authorization: Bearer YOUR_JWT_TOKEN
  Content-Type: application/json
```

```bash
curl -X POST "http://localhost:8080/merchantUser/resetToInitialStatus" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json"
```

---

## 响应参数

### 成功响应

```json
{
    "code": 200,
    "success": true,
    "data": true,
    "msg": "重置成功，已退回到刚注册状态"
}
```

### 响应字段说明

| 字段名 | 类型 | 说明 |
|--------|------|------|
| code | Integer | 状态码（200-成功） |
| success | Boolean | 是否成功 |
| data | Boolean | 重置结果（true-成功） |
| msg | String | 提示信息 |

---

### 失败响应

#### 1. 未登录或 token 无效

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "请先登录"
}
```

#### 2. 用户不存在

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "重置失败：用户不存在"
}
```

#### 3. 数据库操作失败

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "重置失败：{异常信息}"
}
```

---

## 数据库操作详情

### 1. 删除店铺图片

```sql
DELETE FROM store_img
WHERE store_id = ?
  AND img_type IN (14, 15, 25)
```

**img_type 说明**:
- `14`: 营业执照图片
- `15`: 合同图片
- `25`: 经营许可证图片

---

### 2. 删除店铺标签关系

```sql
DELETE FROM tag_store_relation
WHERE store_id = ?
```

---

### 3. 删除店铺信息

```sql
DELETE FROM store_info
WHERE id = ?
```

**影响范围**:
- 店铺名称、地址、联系方式
- 经营板块、经营种类
- 审批状态、店铺状态
- 抽成比例、经纬度等

---

### 4. 清理入住申请通知

```sql
DELETE FROM life_notice
WHERE receiver_id = ?
  AND title = '入住店铺申请'
```

**receiver_id 格式**: `store_{手机号}`

---

### 5. 查询店铺的所有订单ID

```sql
SELECT id
FROM life_user_order
WHERE store_id = ?
```

**用途**: 获取订单ID列表，用于后续删除验券记录

---

### 6. 删除验券记录（订单-优惠券中间表）

```sql
DELETE FROM order_coupon_middle
WHERE order_id IN (订单ID列表)
```

**说明**: 
- 必须先查询订单ID，因为 `order_coupon_middle` 表没有 `store_id` 字段
- 通过 `order_id` 关联删除

---

### 7. 删除订单记录

```sql
DELETE FROM life_user_order
WHERE store_id = ?
```

**影响范围**:
- 团购券订单
- 代金券订单
- 会员卡订单
- 所有订单状态（待支付、已支付、已使用、已退款等）

---

### 8. 删除团购券

```sql
DELETE FROM life_coupon
WHERE store_id = ?
```

**影响范围**:
- 店铺发布的所有团购券
- 包括上架、下架、已售罄等所有状态

---

### 9. 删除代金券

```sql
DELETE FROM life_discount_coupon
WHERE store_id = ?
```

**影响范围**:
- 店铺发布的所有代金券
- 包括所有状态的代金券

---

### 10. 删除用户领取的优惠券

```sql
DELETE FROM life_discount_coupon_user
WHERE user_id = ?
```

**说明**: 删除商户用户本人领取的优惠券

---

### 11. 清理所有通知消息

```sql
DELETE FROM life_notice
WHERE receiver_id = ?
```

**receiver_id 格式**: `store_{手机号}`

**影响范围**:
- 入住申请通知
- 订单通知
- 优惠券通知
- 系统通知
- 所有其他通知

---

### 12. 重置用户信息

```sql
UPDATE store_user
SET store_id = NULL,
    name = NULL,
    id_card = NULL,
    alipay_account = NULL
WHERE id = ?
```

**技术实现**:
```java
// 使用 LambdaUpdateWrapper 显式设置为 null
LambdaUpdateWrapper<StoreUser> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(StoreUser::getId, userId)
        .set(StoreUser::getStoreId, null)
        .set(StoreUser::getName, null)
        .set(StoreUser::getIdCard, null)
        .set(StoreUser::getAlipayAccount, null);
storeUserMapper.update(null, updateWrapper);
```

**为什么不能用 `updateById`？**
- ❌ `updateById(entity)` 默认会忽略 null 值
- ✅ 必须使用 `UpdateWrapper.set()` 显式设置为 null
- ✅ 调用 `update(null, wrapper)` 来执行更新

**字段说明**:
- `store_id`: 解绑店铺关联
- `name`: 清空联系人姓名（入住时填写）
- `id_card`: 清空身份证号（入住时填写）
- `alipay_account`: 清空支付宝账号（入住时填写）

**保留字段**:
- `phone`: 手机号（注册信息）
- `head_img`: 头像
- `money`: 账户余额
- `pay_password`: 支付密码

---

## 业务场景

### 场景 1: 正常重置（已申请入住）

**前置条件**:
- 用户已注册
- 已提交入住申请
- storeId 不为空

**请求**:
```http
POST /merchantUser/resetToInitialStatus
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": true,
    "msg": "重置成功，已退回到刚注册状态"
}
```

**数据库变化**:
- ❌ 删除 store_info 记录
- ❌ 删除 store_img 记录
- ❌ 删除 tag_store_relation 记录
- ❌ 删除 life_notice 记录（入住申请）
- ❌ 删除 life_user_order 记录（所有订单）
- ❌ 删除 order_coupon_middle 记录（验券记录）
- ❌ 删除 life_coupon 记录（团购券）
- ❌ 删除 life_discount_coupon 记录（代金券）
- ❌ 删除 life_discount_coupon_user 记录（用户优惠券）
- ❌ 删除所有 life_notice 记录（所有通知）
- ✅ store_user.store_id = NULL
- ✅ store_user.name = NULL
- ✅ store_user.id_card = NULL
- ✅ store_user.alipay_account = NULL

---

### 场景 2: 重置（未申请入住）

**前置条件**:
- 用户已注册
- 未提交入住申请
- storeId 为空

**请求**:
```http
POST /merchantUser/resetToInitialStatus
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": true,
    "msg": "重置成功，已退回到刚注册状态"
}
```

**说明**: 用户未关联店铺，无需清理，直接返回成功。

---

### 场景 3: 未登录访问

**请求**:
```http
POST /merchantUser/resetToInitialStatus
```

**响应**:
```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "请先登录"
}
```

---

## 前端集成示例

### Vue.js 示例

```javascript
export default {
    methods: {
        async resetToInitialStatus() {
            // 1. 确认提示
            const confirmed = await this.$confirm(
                '此操作将删除所有入住申请数据，恢复到刚注册状态，是否继续？',
                '确认重置',
                {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }
            ).catch(() => false);
            
            if (!confirmed) {
                return;
            }
            
            // 2. 发送请求
            try {
                const response = await this.$axios.post('/merchantUser/resetToInitialStatus');
                
                if (response.data.success) {
                    this.$message.success('重置成功');
                    // 刷新页面或跳转
                    this.$router.push('/register-success');
                } else {
                    this.$message.error(response.data.msg);
                }
            } catch (error) {
                console.error('重置失败:', error);
                this.$message.error('重置失败，请稍后重试');
            }
        }
    }
}
```

---

### React 示例

```javascript
import { useState } from 'react';
import axios from 'axios';
import { Modal } from 'antd';

function ResetButton() {
    const [loading, setLoading] = useState(false);
    
    const handleReset = () => {
        Modal.confirm({
            title: '确认重置',
            content: '此操作将删除所有入住申请数据，恢复到刚注册状态，是否继续？',
            okText: '确定',
            cancelText: '取消',
            okType: 'danger',
            onOk: async () => {
                setLoading(true);
                try {
                    const response = await axios.post('/merchantUser/resetToInitialStatus');
                    
                    if (response.data.success) {
                        alert('重置成功');
                        // 刷新或跳转
                        window.location.href = '/register-success';
                    } else {
                        alert(response.data.msg);
                    }
                } catch (error) {
                    console.error('重置失败:', error);
                    alert('重置失败，请稍后重试');
                } finally {
                    setLoading(false);
                }
            }
        });
    };
    
    return (
        <button onClick={handleReset} disabled={loading}>
            {loading ? '重置中...' : '重置到刚注册状态'}
        </button>
    );
}
```

---

## 测试用例

### 测试场景1: 正常重置

**前置条件**:
- 用户已登录
- 已申请入住
- store_id 不为空

**执行步骤**:
1. 提交重置请求
2. 检查响应

**预期结果**:
- 返回成功
- store_info 记录被删除
- store_img 记录被删除
- tag_store_relation 记录被删除
- life_notice 相关记录被删除
- store_user.store_id = NULL
- store_user.name = NULL
- store_user.id_card = NULL

---

### 测试场景2: 未入住用户重置

**前置条件**:
- 用户已登录
- 未申请入住
- store_id 为空

**执行步骤**:
1. 提交重置请求

**预期结果**:
- 返回成功
- 无数据删除操作

---

### 测试场景3: 事务回滚测试

**前置条件**:
- 模拟数据库异常

**执行步骤**:
1. 提交重置请求
2. 在删除过程中触发异常

**预期结果**:
- 返回失败
- 所有数据操作回滚
- 数据库状态不变

---

## 常见问题

### Q1: 重置后哪些数据会被保留？

**答案**: 
- ✅ **保留**: 手机号、头像、账户余额、支付密码
- ❌ **清除**: 店铺关联、入住申请数据、店铺图片、订单、优惠券、验券记录、通知消息、支付宝账号

---

### Q2: 重置操作会影响账户余额吗？

**答案**: 不会。账户余额、支付密码等财务相关信息不受影响。

---

### Q3: 为什么 `updateById` 无法将字段设置为 null？

**答案**: 
- ❌ MyBatis-Plus 的 `updateById(entity)` 默认会**忽略 null 值**
- ✅ 必须使用 `LambdaUpdateWrapper.set(field, null)` 显式设置
- ✅ 正确用法：
```java
LambdaUpdateWrapper<StoreUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(StoreUser::getId, userId)
       .set(StoreUser::getStoreId, null)
       .set(StoreUser::getName, null);
storeUserMapper.update(null, wrapper);
```

---

### Q4: 重置后可以重新申请入住吗？

**答案**: 可以。重置后用户状态恢复到刚注册时，可以重新提交入住申请。

---

### Q5: 重置操作是否支持事务回滚？

**答案**: 支持。使用 `@Transactional` 注解，任何异常都会导致所有操作回滚。

---

### Q6: Redis 中的地理位置信息会被清理吗？

**答案**: 不会自动清理。如需清理 Redis 数据，需要额外调用相关清理接口。

---

### Q7: 多次重置会有问题吗？

**答案**: 不会。第一次重置后，storeId 已为空，后续重置会直接返回成功，不会执行删除操作。

---

### Q8: 支付宝账号会被清除吗？

**答案**: 
- ✅ **v1.2 及以上**: 会被清除（入住时可能填写的支付宝账号）
- ❌ **v1.1 及以下**: 不会被清除

---

## 注意事项

### 1. 数据不可恢复

- ⚠️ **重要**: 重置操作会物理删除数据，不可恢复
- ⚠️ **建议**: 仅在测试环境或确需重新申请时使用
- ⚠️ **生产环境**: 谨慎使用，建议增加二次确认

### 2. 事务保护

- ✅ 使用 `@Transactional` 保证原子性
- ✅ 任何步骤失败都会回滚
- ✅ 保证数据一致性

### 3. 权限控制

- ⚠️ 需要登录验证
- ⚠️ 只能重置自己的数据
- ⚠️ 无法重置他人账户

### 4. MyBatis-Plus 注意事项

- ⚠️ **不能使用 `updateById` 设置 null 值**
- ✅ 必须使用 `LambdaUpdateWrapper.set(field, null)`
- ✅ 调用 `update(null, wrapper)` 执行更新

### 5. Redis 数据

- ⚠️ Redis 地理位置信息需额外清理
- ⚠️ 可能需要调用其他清理接口

### 6. 业务影响

- ⚠️ 清理所有入住申请数据
- ⚠️ 清理所有订单、优惠券、验券记录
- ⚠️ 解绑店铺关联
- ⚠️ 清理个人信息（姓名、身份证、支付宝账号）

---

## 安全建议

### 1. 二次确认

```java
// 前端实现二次确认
const confirmed = await this.$confirm('确认重置？', '警告', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
});
```

### 2. 操作日志

```java
// 建议记录重置操作日志
OperationLog log = new OperationLog();
log.setUserId(userId);
log.setOperation("RESET_TO_INITIAL_STATUS");
log.setDetails("重置商户到刚注册状态");
log.setIp(request.getRemoteAddr());
operationLogMapper.insert(log);
```

### 3. 权限限制

```java
// 生产环境建议添加角色限制
@PreAuthorize("hasRole('ADMIN') or hasRole('TEST_USER')")
public boolean resetToInitialStatus()
```

---

## 清理范围汇总

### 会被清理的数据

| 表名 | 清理条件 | 说明 |
|------|---------|------|
| store_info | id = storeId | 店铺信息 |
| store_img | store_id = storeId AND img_type IN (14,15,25) | 入住图片 |
| tag_store_relation | store_id = storeId | 店铺标签 |
| life_notice | receiver_id = 'store_{phone}' | 所有通知消息 |
| life_user_order | store_id = storeId | 所有订单记录 |
| order_coupon_middle | order_id IN (订单ID列表) | 验券记录 |
| life_coupon | store_id = storeId | 团购券 |
| life_discount_coupon | store_id = storeId | 代金券 |
| life_discount_coupon_user | user_id = userId | 用户优惠券 |
| store_user | store_id/name/id_card/alipay_account 设为NULL | 解绑并清空 |

### 不会被清理的数据

| 表名 | 保留字段 | 说明 |
|------|---------|------|
| store_user | phone | 手机号 |
| store_user | head_img | 头像 |
| store_user | money | 账户余额 |
| store_user | pay_password | 支付密码 |
| Redis | geo:stores | 地理位置（需手动清理） |

---

## 更新日志

### 2025-11-21 (v1.2)

**Bug 修复**:
- 🐛 修复 `updateById` 无法将字段设置为 null 的问题
- ✅ 改用 `LambdaUpdateWrapper.set(field, null)` 显式设置
- ✅ 正确清空 storeId、name、idCard、alipayAccount

**功能优化**:
- ✅ 新增清空支付宝账号（alipayAccount）
- ✅ 确保所有字段都能正确设置为 null

**技术说明**:
```java
// ❌ 错误用法（不生效）
StoreUser user = new StoreUser();
user.setId(userId);
user.setStoreId(null);
storeUserMapper.updateById(user);  // null 值会被忽略

// ✅ 正确用法
LambdaUpdateWrapper<StoreUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(StoreUser::getId, userId)
       .set(StoreUser::getStoreId, null);  // 显式设置为 null
storeUserMapper.update(null, wrapper);
```

---

### 2025-11-21 (v1.1)

**功能增强**:
- ✅ 新增订单数据清理（life_user_order）
- ✅ 新增验券记录清理（order_coupon_middle）
- ✅ 新增团购券清理（life_coupon）
- ✅ 新增代金券清理（life_discount_coupon）
- ✅ 新增用户优惠券清理（life_discount_coupon_user）
- ✅ 增强通知清理（清理所有通知，不仅限入住申请）

**清理范围扩展**:
- 从原来的 **5 个表** 扩展到 **10 个表**
- 覆盖入住申请 + 业务运营全流程数据

**技术优化**:
- ✅ 先查询订单ID列表，再删除验券记录（避免直接关联删除错误）
- ✅ 优化删除顺序，避免外键约束问题
- ✅ 详细日志记录每个步骤的删除数量

---

### 2025-11-20 (v1.0)

**新增接口**:
- ✅ `POST /merchantUser/resetToInitialStatus` - 重置商户到刚注册状态

**核心功能**:
- ✅ 删除店铺信息（store_info）
- ✅ 删除店铺图片（store_img: 营业执照、合同、经营许可证）
- ✅ 删除店铺标签（tag_store_relation）
- ✅ 清理入住通知（life_notice）
- ✅ 重置用户信息（store_user: storeId、name、idCard）
- ✅ 事务保护（@Transactional）
- ✅ 详细日志记录
- ✅ 完善异常处理

**适用场景**:
- ✅ 测试环境快速重置
- ✅ 商户需要重新申请入住
- ✅ 入住申请数据清理

**涉及文件**:
- `MerchantUserController.java` - 新增 `resetToInitialStatus` 接口
- `MerchantUserService.java` - 新增方法定义
- `MerchantUserServiceImpl.java` - 新增方法实现

**代码质量**:
- ✅ Linter检查：无错误
- ✅ 日志记录：详细
- ✅ 异常处理：完善
- ✅ 代码注释：完整
- ✅ 事务管理：正确

**开发人员**: ssk

---

**文档版本**: v1.2  
**最后更新**: 2025-11-21  
**维护人员**: ssk

