plan.md 11 KB

夜市摊位管理 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 为夜市用户(userType=3)提供摊位管理能力,包括创建摊位、管理摊位主账号。

Architecture: 在现有 PosStore 和 InfoUser 模型上扩展,PosStore 新增 isStall 字段标识摊位,InfoUser 新增 storeId 字段关联摊位主到摊位。后端新增摊位管理 Controller,前端商家端新增摊位管理菜单页面。

Tech Stack: Java / Spring Boot / MyBatis / MySQL / Vue 2 / Element UI


Technical Context

Language/Version: Java 8+, Vue 2 Primary Dependencies: RuoYi framework, MyBatis, Element UI Storage: MySQL Testing: 手动测试(项目无自动化测试框架) Target Platform: Web (商家管理后台) Project Type: Web application (前后端分离)

注意事项:

  • PosStore 已有 isNightMarket(Integer) 和 nightMarketId(Long) 字段,可用于关联夜市
  • InfoUser 已有 addresslongitudelatitude 字段,已有夜市地址支持
  • 项目使用 RuoYi 菜单系统(sys_menu 表),菜单通过数据库管理

Project Structure

Documentation (this feature)

specs/001-food-stall/
├── spec.md              # 需求文档
└── plan.md              # 本文件(实施计划)

Source Code

foodie_server/
├── ruoyi-admin/src/main/java/com/ruoyi/app/
│   ├── mendian/PosStoreController.java          # [修改] 现有店铺 Controller
│   ├── stall/StallController.java               # [新建] 摊位管理 Controller
│   └── user/InfoUserController.java             # [修改] 用户注册(可能复用)
├── ruoyi-system/src/main/java/com/ruoyi/system/
│   ├── domain/
│   │   ├── PosStore.java                        # [修改] 新增 isStall 字段
│   │   └── InfoUser.java                        # [修改] 新增 storeId 字段
│   ├── mapper/
│   │   ├── PosStoreMapper.java                  # [修改] 查询方法
│   │   ├── PosStoreMapper.xml                   # [修改] SQL 语句
│   │   ├── InfoUserMapper.java                  # [修改] 查询方法
│   │   └── InfoUserMapper.xml                   # [修改] SQL 语句
│   └── service/
│       ├── IPosStoreService.java                # [修改] 接口方法
│       ├── impl/PosStoreServiceImpl.java        # [修改] 实现方法
│       ├── IInfoUserService.java                # [修改] 接口方法
│       └── impl/InfoUserServiceImpl.java        # [修改] 实现方法

foodie-store/                                     # 商家前端
├── src/
│   ├── api/stall.js                             # [新建] 摊位管理 API
│   ├── views/
│   │   └── StallManage.vue                      # [新建] 摊位管理页面
│   ├── components/
│   │   └── Aside.vue                            # [修改] 侧边栏菜单
│   └── router/index.js                          # [修改] 路由配置

Tasks

Task 1: 数据库变更 — PosStore 新增 isStall 字段

Files:

  • Modify: ruoyi-system/src/main/resources/mapper/chanting/PosStoreMapper.xml
  • Modify: ruoyi-system/src/main/java/com/ruoyi/system/domain/PosStore.java

  • [ ] Step 1: 执行 SQL 添加字段

    ALTER TABLE pos_store ADD COLUMN is_stall TINYINT DEFAULT 0 COMMENT '是否摊位 0=店铺 1=摊位';
    
  • [ ] Step 2: PosStore.java 新增 isStall 字段

在 PosStore.java 中添加:

/** 是否摊位 0=店铺 1=摊位 */
private Integer isStall;

添加 getter/setter:

public Integer getIsStall() { return isStall; }
public void setIsStall(Integer isStall) { this.isStall = isStall; }
  • Step 3: PosStoreMapper.xml 更新

PosStoreResult resultMap 中添加:

<result property="isStall" column="is_stall"/>

selectPosStoreVo 中添加 is_stall 字段。

insertPosStore 中添加:

<if test="isStall != null">is_stall,</if>
...
<if test="isStall != null">#{isStall},</if>

updatePosStore 中添加:

<if test="isStall != null">is_stall = #{isStall},</if>

selectPosStoreList 的 where 中添加:

<if test="isStall != null"> and is_stall = #{isStall}</if>
  • Step 4: 验证

启动项目,确认 PosStore 相关接口正常工作。


Task 2: 数据库变更 — InfoUser 新增 storeId 字段

Files:

  • Modify: ruoyi-system/src/main/resources/mapper/system/InfoUserMapper.xml
  • Modify: ruoyi-system/src/main/java/com/ruoyi/system/domain/InfoUser.java

  • [ ] Step 1: 执行 SQL 添加字段

    ALTER TABLE info_user ADD COLUMN store_id BIGINT DEFAULT NULL COMMENT '关联摊位ID(摊位主使用)';
    
  • [ ] Step 2: InfoUser.java 新增 storeId 字段

    /** 关联摊位ID(摊位主使用) */
    private Long storeId;
    

添加 getter/setter。

  • Step 3: InfoUserMapper.xml 更新

在 resultMap 中添加 store_id 映射,在 insert/update/select 中添加 storeId 字段支持。

  • Step 4: 验证

启动项目,确认 InfoUser 相关接口正常工作。


Task 3: 后端 — 摊位管理 Controller

Files:

  • Create: ruoyi-admin/src/main/java/com/ruoyi/app/stall/StallController.java

  • [ ] Step 1: 创建 StallController.java

提供以下接口:

@RestController
@RequestMapping("/stall")
public class StallController extends BaseController {

    /** 获取当前夜市用户的摊位列表(isStall=1) */
    @GetMapping("/list")
    public AjaxResult getStallList(@Auth InfoUser user) {
        PosStore query = new PosStore();
        query.setUserId(user.getUserId());
        query.setIsStall(1);
        return AjaxResult.success(posStoreService.selectPosStoreList(query));
    }

    /** 创建摊位(复用 PosStore,isStall=1) */
    @PostMapping("/add")
    @Transactional
    public AjaxResult addStall(@Auth InfoUser user, @RequestBody PosStore posStore) {
        posStore.setUserId(user.getUserId());
        posStore.setIsStall(1);
        posStoreService.insertPosStore(posStore);
        return AjaxResult.success(posStore);
    }

    /** 为摊位创建摊位主账号 */
    @PostMapping("/addOwner")
    public AjaxResult addStallOwner(@Auth InfoUser user, @RequestBody InfoUser stallOwner) {
        // 校验摊位属于当前夜市用户
        // 设置 userType=4, storeId, 加密密码
        // 创建用户
        return AjaxResult.success();
    }

    /** 获取摊位下的摊位主列表 */
    @GetMapping("/owners")
    public AjaxResult getStallOwners(@RequestParam Long storeId) {
        // 查询 userType=4, storeId=storeId 的用户列表
        return AjaxResult.success();
    }

    /** 删除/禁用摊位主 */
    @PutMapping("/disableOwner")
    public AjaxResult disableStallOwner(@RequestParam Long userId) {
        // 设置 status=1(禁用)
        return AjaxResult.success();
    }
}
  • Step 2: 在 InfoUserService/Mapper 中添加按 storeId 和 userType 查询的方法

InfoUserMapper.xml 中添加:

<select id="selectStallOwnersByStoreId" parameterType="Long" resultMap="InfoUserResult">
    <include refid="selectInfoUserVo"/>
    where store_id = #{storeId} and user_type = '4' and del_flag = '0'
</select>
  • Step 3: 验证

使用 Postman/浏览器测试各接口。


Task 4: 前端 — 摊位管理 API 层

Files:

  • Create: E:\QtwCode\foodie\foodie-store\src\api\stall.js

  • [ ] Step 1: 创建 stall.js

    import request from '@/router/request'
    
    // 获取摊位列表
    export function getStallList() {
    return request({ url: '/stall/list', method: 'get' })
    }
    
    // 创建摊位
    export function addStall(data) {
    return request({ url: '/stall/add', method: 'post', data })
    }
    
    // 获取摊位主列表
    export function getStallOwners(storeId) {
    return request({ url: '/stall/owners', method: 'get', params: { storeId } })
    }
    
    // 创建摊位主
    export function addStallOwner(data) {
    return request({ url: '/stall/addOwner', method: 'post', data })
    }
    
    // 禁用摊位主
    export function disableStallOwner(userId) {
    return request({ url: '/stall/disableOwner', method: 'put', params: { userId } })
    }
    

Task 5: 前端 — 摊位管理页面

Files:

  • Create: E:\QtwCode\foodie\foodie-store\src\views\StallManage.vue
  • Modify: E:\QtwCode\foodie\foodie-store\src\router\index.js
  • Modify: E:\QtwCode\foodie\foodie-store\src\components\Aside.vue

  • [ ] Step 1: 创建 StallManage.vue 页面

页面包含:

  1. 摊位列表(表格形式,显示摊位名称、地址、状态、操作)
  2. 新建摊位按钮 → 弹窗表单(复用现有店铺创建字段)
  3. 每个摊位的「管理摊位主」按钮 → 展开摊位主列表
  4. 添加摊位主按钮 → 弹窗表单(手机号、密码、昵称)
  5. 禁用摊位主按钮
  • [ ] Step 2: 修改 router/index.js 添加摊位管理路由

    {
    path: '/stall-manage',
    name: 'StallManage',
    component: () => import('@/views/StallManage.vue')
    }
    
  • [ ] Step 3: 修改 Aside.vue 添加「摊位管理」菜单项

在侧边栏菜单中添加摊位管理入口,仅夜市用户(userType=3)可见。

  • Step 4: 验证

启动前端项目,登录夜市用户账号,验证:

  • 摊位管理菜单可见
  • 创建摊位功能正常
  • 创建/管理摊位主功能正常

Task 6: 数据库变更 — UserWallet 新增 storeId 字段

Files:

  • Modify: ruoyi-system/src/main/resources/mapper/system/UserWalletMapper.xml
  • Modify: ruoyi-system/src/main/java/com/ruoyi/system/domain/UserWallet.java
  • Modify: ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserWalletServiceImpl.java

  • [ ] Step 1: 执行 SQL 添加字段

    ALTER TABLE user_wallet ADD COLUMN store_id BIGINT DEFAULT NULL COMMENT '关联摊位id,非摊位钱包为NULL';
    
  • [ ] Step 2: UserWallet.java 新增 storeId 字段

    /** 关联摊位id,非摊位钱包为NULL */
    private Long storeId;
    
  • [ ] Step 3: UserWalletMapper.xml 更新

在 resultMap 中添加 store_id 映射,在 insert/update/select 中添加 storeId 字段支持。

  • Step 4: 修改 StallController 创建摊位逻辑

addStall 方法中,创建摊位后自动创建摊位钱包:

// 创建摊位后,自动创建摊位钱包
UserWallet wallet = new UserWallet();
wallet.setStoreId(posStore.getId());
wallet.setPointsWallet(0L);
wallet.setBalanceWallet(BigDecimal.ZERO);
wallet.setBlockedFunds(BigDecimal.ZERO);
userWalletService.insertUserWallet(wallet);
  • Step 5: 修改现有钱包查询逻辑

所有通过 user_id 查钱包的地方加上 store_id IS NULL 条件,避免查到摊位钱包。新增通过 store_id 查摊位钱包的方法。

  • Step 6: 验证

创建摊位后确认摊位钱包自动创建,查询用户钱包不包含摊位钱包。


Task 7: 提交与收尾

  • Step 1: 更新 spec.md 待确认项

根据实现过程中确认的细节更新需求文档。

  • [ ] Step 2: 提交代码

    git add -A
    git commit -m "feat: 夜市摊位管理功能 — 创建摊位、管理摊位主账号"