# 订单操作日志 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:** 新建 pos_order_log 表，在所有订单状态变更处自动记录日志，并在平台管理端提供查询/导出页面。

**Architecture:** 跟随 SysOperLog 的模式——Entity + Mapper(XML) + Service + Controller。日志写入封装为一个工具类 `OrderLogHelper`，供各 Controller 和定时任务调用。前端新增独立的日志查询页面挂到"订单管理"菜单下。

**Tech Stack:** Java (Spring Boot, MyBatis XML), Vue.js (Element UI), MySQL

---

## File Structure

| 操作 | 文件 | 职责 |
|------|------|------|
| Create | `ruoyi-system/.../domain/PosOrderLog.java` | 实体类 |
| Create | `ruoyi-system/.../mapper/PosOrderLogMapper.java` | Mapper 接口 |
| Create | `ruoyi-system/.../mapper/PosOrderLogMapper.xml` (resources/mapper/system/) | XML mapper |
| Create | `ruoyi-system/.../service/IPosOrderLogService.java` | Service 接口 |
| Create | `ruoyi-system/.../service/impl/PosOrderLogServiceImpl.java` | Service 实现 |
| Create | `ruoyi-admin/.../controller/system/PosOrderLogController.java` | 后台管理接口 |
| Create | `ruoyi-system/.../utils/OrderLogHelper.java` | 日志写入工具类 |
| Modify | `ruoyi-admin/.../app/order/PosOrderShOprateController.java` | 商家操作插入日志 |
| Modify | `ruoyi-admin/.../app/order/PosOrderQsOprateController.java` | 骑手操作插入日志 |
| Modify | `ruoyi-admin/.../app/order/UserOrderController.java` | 用户操作插入日志 |
| Modify | `ruoyi-admin/.../app/order/PosOrderController.java` | 平台操作插入日志 |
| Modify | `ruoyi-admin/.../app/order/TestTask.java` | 系统定时任务插入日志 |
| Modify | `ruoyi-admin/.../app/pay/PayController.java` | 支付回调插入日志 |
| Create | `foodie-admin-vue/src/api/system/orderLog.js` | 前端 API |
| Create | `foodie-admin-vue/src/views/system/order/log.vue` | 前端页面 |

---

### Task 1: 创建 PosOrderLog 实体类

**Files:**
- Create: `ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrderLog.java`

- [ ] **Step 1: 创建实体类**

```java
package com.ruoyi.system.domain;

import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;

/**
 * 订单操作日志表 pos_order_log
 */
public class PosOrderLog extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 主键 */
    @Excel(name = "主键ID", cellType = ColumnType.NUMERIC)
    private Long id;

    /** 订单号 */
    @Excel(name = "订单号")
    private String ddId;

    /** 操作人类型：0系统,1平台管理员,2商家,3骑手,4用户 */
    @Excel(name = "操作人类型", readConverterExp = "0=系统,1=平台管理员,2=商家,3=骑手,4=用户")
    private Integer operatorType;

    /** 操作人ID */
    @Excel(name = "操作人ID", cellType = ColumnType.NUMERIC)
    private Long operatorId;

    /** 操作人名称 */
    @Excel(name = "操作人名称")
    private String operatorName;

    /** 操作内容 */
    @Excel(name = "操作内容")
    private String content;

    /** 操作时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date logTime;

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getDdId() { return ddId; }
    public void setDdId(String ddId) { this.ddId = ddId; }
    public Integer getOperatorType() { return operatorType; }
    public void setOperatorType(Integer operatorType) { this.operatorType = operatorType; }
    public Long getOperatorId() { return operatorId; }
    public void setOperatorId(Long operatorId) { this.operatorId = operatorId; }
    public String getOperatorName() { return operatorName; }
    public void setOperatorName(String operatorName) { this.operatorName = operatorName; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public Date getLogTime() { return logTime; }
    public void setLogTime(Date logTime) { this.logTime = logTime; }
}
```

注意：`logTime` 使用 `logTime` 而非 `createTime`，避免与 BaseEntity 的 `createTime` 冲突。BaseEntity 提供 `params` map 用于查询参数传递。

- [ ] **Step 2: Commit**

```bash
git add ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrderLog.java
git commit -m "feat(order-log): add PosOrderLog entity"
```

---

### Task 2: 创建 MyBatis Mapper 接口和 XML

**Files:**
- Create: `ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderLogMapper.java`
- Create: `ruoyi-system/src/main/resources/mapper/system/PosOrderLogMapper.xml`

- [ ] **Step 1: 创建 Mapper 接口**

```java
package com.ruoyi.system.mapper;

import java.util.List;
import com.ruoyi.system.domain.PosOrderLog;

/**
 * 订单操作日志 数据层
 */
public interface PosOrderLogMapper
{
    public void insertOrderLog(PosOrderLog orderLog);

    public List<PosOrderLog> selectOrderLogList(PosOrderLog orderLog);

    public PosOrderLog selectOrderLogById(Long id);
}
```

- [ ] **Step 2: 创建 XML Mapper**

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.PosOrderLogMapper">

    <resultMap type="PosOrderLog" id="PosOrderLogResult">
        <id     property="id"            column="id"             />
        <result property="ddId"          column="dd_id"          />
        <result property="operatorType"  column="operator_type"  />
        <result property="operatorId"    column="operator_id"    />
        <result property="operatorName"  column="operator_name"  />
        <result property="content"       column="content"        />
        <result property="logTime"       column="create_time"    />
    </resultMap>

    <sql id="selectOrderLogVo">
        select id, dd_id, operator_type, operator_id, operator_name, content, create_time
        from pos_order_log
    </sql>

    <insert id="insertOrderLog" parameterType="PosOrderLog">
        insert into pos_order_log(dd_id, operator_type, operator_id, operator_name, content, create_time)
        values (#{ddId}, #{operatorType}, #{operatorId}, #{operatorName}, #{content}, sysdate())
    </insert>

    <select id="selectOrderLogList" parameterType="PosOrderLog" resultMap="PosOrderLogResult">
        <include refid="selectOrderLogVo"/>
        <where>
            <if test="ddId != null and ddId != ''">
                AND dd_id like concat('%', #{ddId}, '%')
            </if>
            <if test="operatorId != null">
                AND operator_id = #{operatorId}
            </if>
            <if test="operatorName != null and operatorName != ''">
                AND operator_name like concat('%', #{operatorName}, '%')
            </if>
            <if test="operatorType != null">
                AND operator_type = #{operatorType}
            </if>
            <if test="params.beginTime != null and params.beginTime != ''">
                AND create_time &gt;= #{params.beginTime}
            </if>
            <if test="params.endTime != null and params.endTime != ''">
                AND create_time &lt;= #{params.endTime}
            </if>
        </where>
        order by id desc
    </select>

    <select id="selectOrderLogById" parameterType="Long" resultMap="PosOrderLogResult">
        <include refid="selectOrderLogVo"/>
        where id = #{id}
    </select>

</mapper>
```

- [ ] **Step 3: Commit**

```bash
git add ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderLogMapper.java ruoyi-system/src/main/resources/mapper/system/PosOrderLogMapper.xml
git commit -m "feat(order-log): add PosOrderLog mapper interface and XML"
```

---

### Task 3: 创建 Service 层

**Files:**
- Create: `ruoyi-system/src/main/java/com/ruoyi/system/service/IPosOrderLogService.java`
- Create: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PosOrderLogServiceImpl.java`

- [ ] **Step 1: 创建 Service 接口**

```java
package com.ruoyi.system.service;

import java.util.List;
import com.ruoyi.system.domain.PosOrderLog;

/**
 * 订单操作日志 服务层
 */
public interface IPosOrderLogService
{
    public void insertOrderLog(PosOrderLog orderLog);

    public List<PosOrderLog> selectOrderLogList(PosOrderLog orderLog);

    public PosOrderLog selectOrderLogById(Long id);
}
```

- [ ] **Step 2: 创建 Service 实现**

```java
package com.ruoyi.system.service.impl;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.domain.PosOrderLog;
import com.ruoyi.system.mapper.PosOrderLogMapper;
import com.ruoyi.system.service.IPosOrderLogService;

/**
 * 订单操作日志 服务层处理
 */
@Service
public class PosOrderLogServiceImpl implements IPosOrderLogService
{
    @Autowired
    private PosOrderLogMapper orderLogMapper;

    @Override
    public void insertOrderLog(PosOrderLog orderLog)
    {
        orderLogMapper.insertOrderLog(orderLog);
    }

    @Override
    public List<PosOrderLog> selectOrderLogList(PosOrderLog orderLog)
    {
        return orderLogMapper.selectOrderLogList(orderLog);
    }

    @Override
    public PosOrderLog selectOrderLogById(Long id)
    {
        return orderLogMapper.selectOrderLogById(id);
    }
}
```

- [ ] **Step 3: Commit**

```bash
git add ruoyi-system/src/main/java/com/ruoyi/system/service/IPosOrderLogService.java ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PosOrderLogServiceImpl.java
git commit -m "feat(order-log): add service interface and implementation"
```

---

### Task 4: 创建 OrderLogHelper 工具类

**Files:**
- Create: `ruoyi-system/src/main/java/com/ruoyi/system/utils/OrderLogHelper.java`

这个工具类封装日志构建逻辑，各调用方只需传入 ddId、operatorType、operatorId、operatorName、content 即可。

- [ ] **Step 1: 创建工具类**

```java
package com.ruoyi.system.utils;

import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.system.domain.PosOrderLog;
import com.ruoyi.system.service.IPosOrderLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.TimerTask;

/**
 * 订单操作日志工具类
 */
@Component
public class OrderLogHelper {

    @Autowired
    private IPosOrderLogService orderLogService;

    /**
     * 异步写入订单日志
     */
    public void log(String ddId, int operatorType, Long operatorId, String operatorName, String content) {
        PosOrderLog log = new PosOrderLog();
        log.setDdId(ddId);
        log.setOperatorType(operatorType);
        log.setOperatorId(operatorId);
        log.setOperatorName(operatorName);
        log.setContent(content);
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                orderLogService.insertOrderLog(log);
            }
        });
    }

    /**
     * 同步写入订单日志（用于定时任务等异步上下文）
     */
    public void logSync(String ddId, int operatorType, Long operatorId, String operatorName, String content) {
        PosOrderLog log = new PosOrderLog();
        log.setDdId(ddId);
        log.setOperatorType(operatorType);
        log.setOperatorId(operatorId);
        log.setOperatorName(operatorName);
        log.setContent(content);
        orderLogService.insertOrderLog(log);
    }
}
```

使用 `AsyncManager` 异步写入，避免影响主流程性能。`logSync` 给定时任务使用（定时任务已在异步线程中）。

- [ ] **Step 2: Commit**

```bash
git add ruoyi-system/src/main/java/com/ruoyi/system/utils/OrderLogHelper.java
git commit -m "feat(order-log): add OrderLogHelper utility for async log writing"
```

---

### Task 5: 创建后台管理 Controller

**Files:**
- Create: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/PosOrderLogController.java`

- [ ] **Step 1: 创建 Controller**

```java
package com.ruoyi.web.controller.system;

import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.PosOrderLog;
import com.ruoyi.system.service.IPosOrderLogService;

/**
 * 订单操作日志
 */
@RestController
@RequestMapping("/system/orderLog")
public class PosOrderLogController extends BaseController
{
    @Autowired
    private IPosOrderLogService orderLogService;

    @PreAuthorize("@ss.hasPermi('system:orderLog:list')")
    @GetMapping("/list")
    public TableDataInfo list(PosOrderLog orderLog)
    {
        startPage();
        List<PosOrderLog> list = orderLogService.selectOrderLogList(orderLog);
        return getDataTable(list);
    }

    @PreAuthorize("@ss.hasPermi('system:orderLog:query')")
    @GetMapping("/{id}")
    public AjaxResult getInfo(@PathVariable Long id)
    {
        return success(orderLogService.selectOrderLogById(id));
    }

    @Log(title = "订单操作日志", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:orderLog:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, PosOrderLog orderLog)
    {
        List<PosOrderLog> list = orderLogService.selectOrderLogList(orderLog);
        ExcelUtil<PosOrderLog> util = new ExcelUtil<PosOrderLog>(PosOrderLog.class);
        util.exportExcel(response, list, "订单操作日志");
    }
}
```

- [ ] **Step 2: Commit**

```bash
git add ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/PosOrderLogController.java
git commit -m "feat(order-log): add admin controller with list/detail/export"
```

---

### Task 6: 商家端操作插入日志

**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderShOprateController.java`

- [ ] **Step 1: 注入 OrderLogHelper**

在 `PosOrderShOprateController` 的成员变量区域添加：

```java
@Autowired
private OrderLogHelper orderLogHelper;
```

添加 import：
```java
import com.ruoyi.system.utils.OrderLogHelper;
```

- [ ] **Step 2: acceptOrder 方法添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后添加：

```java
InfoUser shUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String shName = shUser != null ? shUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 2, Long.valueOf(new JwtUtil().getusid(token)), shName, "商家" + shName + "已接单");
```

- [ ] **Step 3: dispatchOrder 方法添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后、`chuCan(order, update);` 之前添加：

```java
InfoUser shUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String shName = shUser != null ? shUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 2, Long.valueOf(new JwtUtil().getusid(token)), shName, "商家" + shName + "已出餐");
```

- [ ] **Step 4: completeOrder 方法添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后添加：

```java
InfoUser shUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String shName = shUser != null ? shUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 2, Long.valueOf(new JwtUtil().getusid(token)), shName, "商家" + shName + "已完成订单");
```

- [ ] **Step 5: cancelOrder 方法添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后添加：

```java
InfoUser shUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String shName = shUser != null ? shUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 2, Long.valueOf(new JwtUtil().getusid(token)), shName, "商家" + shName + "取消订单");
```

- [ ] **Step 6: Commit**

```bash
git add ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderShOprateController.java
git commit -m "feat(order-log): add order log to merchant operations"
```

---

### Task 7: 骑手端操作插入日志

**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderQsOprateController.java`

- [ ] **Step 1: 注入 OrderLogHelper**

在成员变量区域添加：

```java
@Autowired
private OrderLogHelper orderLogHelper;
```

添加 import：
```java
import com.ruoyi.system.utils.OrderLogHelper;
```

- [ ] **Step 2: acceptOrder 方法添加日志**

在 `return setOrderQsState(posOrder, qsId, push);` 之前添加：

```java
InfoUser qsUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(qsId)));
String qsName = qsUser != null ? qsUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 3, Long.valueOf(qsId), qsName, "骑手" + qsName + "接单成功");
```

- [ ] **Step 3: pickupOrder 方法添加日志**

在 `return setOrderQsState(posOrder, qsId, push);` 之前添加：

```java
InfoUser qsUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(qsId)));
String qsName = qsUser != null ? qsUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 3, Long.valueOf(qsId), qsName, "骑手" + qsName + "已取餐");
```

- [ ] **Step 4: deliverOrder 方法添加日志**

在 `return setOrderQsState(posOrder, qsId, push);` 之前添加：

```java
InfoUser qsUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(qsId)));
String qsName = qsUser != null ? qsUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 3, Long.valueOf(qsId), qsName, "骑手" + qsName + "已送达");
```

- [ ] **Step 5: Commit**

```bash
git add ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderQsOprateController.java
git commit -m "feat(order-log): add order log to rider operations"
```

---

### Task 8: 用户端和平台端操作插入日志

**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/order/UserOrderController.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderController.java`

- [ ] **Step 1: UserOrderController 注入 OrderLogHelper**

添加 import 和成员变量：

```java
import com.ruoyi.system.utils.OrderLogHelper;

@Autowired
private OrderLogHelper orderLogHelper;
```

- [ ] **Step 2: UserOrderController.createOrderChild 添加日志**

在订单创建成功后（`posOrderService.saveOrUpdate()` 之后）添加：

```java
InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
String uName = uUser != null ? uUser.getNickName() : "";
orderLogHelper.log(String.valueOf(posOrder.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "创建订单");
```

注意：`userId` 在 createOrderChild 方法中从 token 获取，变量名需确认是 `userId` 还是从 `jwtUtil.getusid(token)` 获取。根据实际方法中已有的变量名调整。

- [ ] **Step 3: UserOrderController.confirmPickup 添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后添加：

```java
InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String uName = uUser != null ? uUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(new JwtUtil().getusid(token)), uName, "用户" + uName + "确认取餐");
```

- [ ] **Step 4: UserOrderController.cancelOrder 添加日志**

在 `posOrderService.saveOrUpdate(update);` 之后添加：

```java
InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(new JwtUtil().getusid(token))));
String uName = uUser != null ? uUser.getNickName() : "";
orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(new JwtUtil().getusid(token)), uName, "用户" + uName + "取消订单");
```

- [ ] **Step 5: PosOrderController.setorderuzt 添加日志**

在 `PosOrderController` 中注入 OrderLogHelper。

在 `setorderuzt` 方法中，通用的 `posOrderService.saveOrUpdate(posOrder)` 成功分支添加：

```java
// 获取平台管理员信息
SysUser adminUser = SecurityUtils.getLoginUser().getUser();
String adminName = adminUser != null ? adminUser.getNickName() : "";
PosOrder beforeOrder = posOrderService.getById(posOrder.getId());
String ddId = beforeOrder != null ? String.valueOf(beforeOrder.getDdId()) : "";
orderLogHelper.log(ddId, 1, adminUser != null ? adminUser.getUserId() : null, adminName,
    "平台管理员" + adminName + "修改订单状态: " + (posOrder.getState() != null ? posOrder.getState() : ""));
```

注意：`PosOrderController.setorderuzt` 是平台管理端调用的接口（使用 Spring Security 认证），通过 `SecurityUtils.getLoginUser()` 获取当前管理员信息。需添加 import `com.ruoyi.common.utils.SecurityUtils` 和 `com.ruoyi.common.core.domain.entity.SysUser`。

- [ ] **Step 6: Commit**

```bash
git add ruoyi-admin/src/main/java/com/ruoyi/app/order/UserOrderController.java ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderController.java
git commit -m "feat(order-log): add order log to user and platform operations"
```

---

### Task 9: 定时任务和支付回调插入日志

**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/order/TestTask.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/pay/PayController.java`

- [ ] **Step 1: TestTask 注入 OrderLogHelper**

添加 import 和成员变量：

```java
import com.ruoyi.system.utils.OrderLogHelper;

@Autowired
private OrderLogHelper orderLogHelper;
```

- [ ] **Step 2: TestTask.testTiming 添加日志**

在 `posOrderService.saveOrUpdate(posOrder);` 之后添加：

```java
orderLogHelper.logSync(String.valueOf(ordlist.get(i).getDdId()), 0, null, "系统", "系统自动取消超时未接单订单");
```

使用 `logSync` 而非 `log`，因为定时任务已在异步上下文中。

- [ ] **Step 3: TestTask.zidwancheng 添加日志**

在 `posOrderService.saveOrUpdate(pos);` 之后添加：

```java
orderLogHelper.logSync(String.valueOf(ordlist.get(i).getDdId()), 0, null, "系统", "系统自动完成超时订单");
```

- [ ] **Step 4: TestTask.refundProcessing 添加日志**

在退款成功后（`posOrderService.saveOrUpdate(posOrder);` 之后）添加：

```java
orderLogHelper.logSync(String.valueOf(list.get(i).getDdId()), 0, null, "系统", "系统处理退款完成");
```

- [ ] **Step 5: PayController 注入 OrderLogHelper**

添加 import 和成员变量：

```java
import com.ruoyi.system.utils.OrderLogHelper;

@Autowired
private OrderLogHelper orderLogHelper;
```

- [ ] **Step 6: PayController 支付回调添加日志**

在支付成功设置 `payStatus=1` 之后添加：

```java
orderLogHelper.logSync(String.valueOf(order.getDdId()), 0, null, "系统", "系统收到支付成功回调");
```

- [ ] **Step 7: Commit**

```bash
git add ruoyi-admin/src/main/java/com/ruoyi/app/order/TestTask.java ruoyi-admin/src/main/java/com/ruoyi/app/pay/PayController.java
git commit -m "feat(order-log): add order log to scheduled tasks and payment callback"
```

---

### Task 10: 前端 - API 文件

**Files:**
- Create: `E:\QtwCode\foodie\foodie-admin-vue\src\api\system\orderLog.js`

- [ ] **Step 1: 创建 API 文件**

```javascript
import request from '@/utils/request'

// 查询订单操作日志列表
export function listOrderLog(query) {
  return request({
    url: '/system/orderLog/list',
    method: 'get',
    params: query
  })
}

// 查询订单操作日志详细
export function getOrderLog(id) {
  return request({
    url: '/system/orderLog/' + id,
    method: 'get'
  })
}
```

- [ ] **Step 2: Commit**

```bash
git add "E:/QtwCode/foodie/foodie-admin-vue/src/api/system/orderLog.js"
git commit -m "feat(order-log): add frontend API file"
```

---

### Task 11: 前端 - 日志查询页面

**Files:**
- Create: `E:\QtwCode\foodie\foodie-admin-vue\src\views\system\order\log.vue`

这是核心前端页面，参照截图中的 UI 设计。页面使用 Element UI 组件，包含搜索栏、数据表格、分页。

- [ ] **Step 1: 创建日志页面**

创建 `log.vue`，包含以下核心结构：

**搜索栏**：订单号输入框、操作人ID输入框、操作人名称输入框、搜索按钮、重置按钮、导出按钮

**数据表格列**：主键ID、订单号、操作人类型（格式化为中文）、操作人ID、操作人名称、操作内容、时间、操作（查看链接）

**操作人类型格式化方法**：

```javascript
operatorTypeFormat(type) {
  const map = { 0: '系统', 1: '平台', 2: '商家', 3: '骑手', 4: '用户' }
  return map[type] || type
}
```

**核心 data 属性**：

```javascript
data() {
  return {
    orderLogList: [],
    total: 0,
    queryParams: {
      pageNum: 1,
      pageSize: 10,
      ddId: undefined,
      operatorId: undefined,
      operatorName: undefined
    }
  }
}
```

**核心方法**：

```javascript
methods: {
  getList() {
    listOrderLog(this.queryParams).then(response => {
      this.orderLogList = response.rows
      this.total = response.total
    })
  },
  handleQuery() {
    this.queryParams.pageNum = 1
    this.getList()
  },
  resetQuery() {
    this.queryParams = { pageNum: 1, pageSize: 10, ddId: undefined, operatorId: undefined, operatorName: undefined }
    this.getList()
  },
  handleExport() {
    this.download('system/orderLog/export', { ...this.queryParams }, '订单操作日志.xlsx')
  }
}
```

查看按钮点击后弹窗显示该条日志详情（使用 el-dialog）。

由于前端文件使用 CRLF 换行，编写时使用 Python 脚本方式创建文件。

- [ ] **Step 2: Commit**

```bash
git add "E:/QtwCode/foodie/foodie-admin-vue/src/views/system/order/log.vue"
git commit -m "feat(order-log): add order log query page for admin"
```

---

### Task 12: 前端 - 路由和菜单配置

**Files:**
- Modify: `E:\QtwCode\foodie\foodie-admin-vue\src\router\index.js`

订单日志页面需要添加到路由配置中。在系统管理或订单管理路由组下添加。

由于本项目使用动态路由（菜单由后台数据库配置），路由代码不需要手动添加。需要在数据库的 `sys_menu` 表中插入菜单记录。

- [ ] **Step 1: 添加 SQL 到 updatesql/sql.md**

在 `updatesql/sql.md` 末尾追加菜单插入 SQL：

```sql
-- 2026-05-19 新增订单操作日志菜单
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, remark)
SELECT '订单日志', menu_id, 6, 'orderLog', 'system/order/log', 'C', '0', '0', 'system:orderLog:list', 'log', 'admin', NOW(), '订单操作日志菜单'
FROM sys_menu WHERE menu_name = '订单管理' AND parent_id = 0 LIMIT 1;

-- 日志查询按钮权限
SET @logMenuId = (SELECT menu_id FROM sys_menu WHERE perms = 'system:orderLog:list' LIMIT 1);
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES ('订单日志查询', @logMenuId, 1, '#', '', 'F', '0', '0', 'system:orderLog:query', '#', 'admin', NOW());
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES ('订单日志导出', @logMenuId, 2, '#', '', 'F', '0', '0', 'system:orderLog:export', '#', 'admin', NOW());
```

- [ ] **Step 2: Commit**

```bash
git add updatesql/sql.md
git commit -m "feat(order-log): add menu SQL for order log page"
```

---

### Task 13: 执行数据库建表 SQL

- [ ] **Step 1: 在数据库执行建表语句**

从 `updatesql/sql.md` 中复制 `CREATE TABLE pos_order_log` 和菜单插入 SQL，在 MySQL 中执行。

- [ ] **Step 2: 验证**

- 启动后端服务
- 使用商家端操作一个订单（接单→出餐→完成）
- 打开平台管理端，进入"订单管理 > 订单日志"页面
- 确认能看到操作日志记录
- 测试搜索和导出功能

---

## Self-Review Checklist

### 1. Spec Coverage
| Spec 要求 | 对应 Task |
|-----------|----------|
| pos_order_log 表 | Task 1-3 (Entity/Mapper/Service), SQL in updatesql/sql.md |
| 5 种操作人类型 | Task 1 (Entity), Task 4 (Helper), Task 6-9 (各调用方) |
| 日志写入时机（商家4种） | Task 6 |
| 日志写入时机（骑手3种） | Task 7 |
| 日志写入时机（用户3种） | Task 8 |
| 日志写入时机（平台1种） | Task 8 |
| 日志写入时机（系统3种） | Task 9 |
| 平台管理端查询页面 | Task 10-12 |
| 搜索条件（订单号/操作人ID/名称） | Task 2 (XML), Task 11 (前端) |
| 导出功能 | Task 5 (Controller), Task 11 (前端) |
| 菜单配置 | Task 12 |

### 2. Placeholder Scan
- 无 TBD/TODO/实现稍后
- 所有代码步骤都有完整代码

### 3. Type Consistency
- PosOrderLog 字段在 Entity/Mapper XML/Service/Controller/Helper 中保持一致
- `ddId` 类型 String, `operatorType` 类型 Integer, `operatorId` 类型 Long
- `logTime` 映射到数据库 `create_time` 列
