|
|
@@ -0,0 +1,899 @@
|
|
|
+# 订单操作日志 Implementation Plan (COMPLETED)
|
|
|
+
|
|
|
+> **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 >= #{params.beginTime}
|
|
|
+ </if>
|
|
|
+ <if test="params.endTime != null and params.endTime != ''">
|
|
|
+ AND create_time <= #{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` 列
|