Browse Source

基本上把order的前期需要用到的接口撸了一下,下午开始接慧用工

skyfffire 1 tháng trước cách đây
mục cha
commit
29fdbc940f

+ 173 - 3
src/main/java/modules/order/OrderController.java

@@ -5,10 +5,14 @@ import com.jfinal.aop.Before;
 import com.jfinal.aop.Inject;
 import com.jfinal.kit.StrKit;
 import common.interceptor.LoginInterceptor;
+import common.interceptor.empty.EmptyInterface;
+import common.interceptor.role.RequiredRoleInterface;
 import common.model.Nftt;
+import common.model.Order;
 import common.model.User;
 import common.utils.http.MyController;
 import common.utils.http.MyRet;
+import modules.user.UserController;
 import modules.user.UserService;
 
 public class OrderController extends MyController {
@@ -17,7 +21,11 @@ public class OrderController extends MyController {
     @Inject
     private UserService userService;
 
+    /**
+     * 创建订单调用的接口,会检查库存什么的
+     */
     @Before(LoginInterceptor.class)
+    @EmptyInterface({"nftt_id"})
     public void create() {
         // --- 从 JSON 请求体中获取参数 ---
         JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
@@ -37,7 +45,7 @@ public class OrderController extends MyController {
         
         // 先判断实名认证过没过
         User user = userService.findUserByMobileNumber(this.<String>getSessionAttr("mobile_number"));
-        if (user == null || StrKit.isBlank(user.getHygWorkerId())) {
+        if (StrKit.isBlank(user.getHygWorkerId())) {
             renderJson(MyRet.fail("请先进行实名认证"));
             return;
         }
@@ -60,10 +68,171 @@ public class OrderController extends MyController {
         // 执行抢购操作
         renderJson(service.create(nfttId.longValue(), user.getId(), orderType));
     }
+
+    /**
+     * 用户点击支付时调用的接口
+     */
+    @Before(LoginInterceptor.class)
+    @EmptyInterface({"order_id"})
+    public void paid() {
+        // --- 从 JSON 请求体中获取参数 ---
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+        Integer orderId;
+        // orderId
+        try {
+            orderId = requestBodyJson.getInteger("order_id");
+        } catch (Exception e) {
+            renderJson(MyRet.fail("你传入的 order_id 有些问题:" + e.getMessage()).setData(requestBodyJson));
+
+            return;
+        }
+        if (orderId == null) {
+            renderJson(MyRet.fail("id(order_id) 有问题"));
+            return;
+        }
+        User user = userService.findUserByMobileNumber(this.<String>getSessionAttr("mobile_number"));
+
+        // 判断订单归属权
+        if (service.isOrderBelongToUser(user.getId(), orderId)) {
+            renderJson(MyRet.fail("订单信息错误,再违规请求将封禁ip"));
+            return;
+        }
+
+        // 判断订单状态
+        Order order = Order.dao.findById(orderId);
+        if (order.getOrderStatus() != OrderStatus.PENDING_PAY.code) {
+            renderJson(MyRet.fail("该订单不需要支付,状态:" + order.getOrderStatus()));
+            return;
+        }
+        
+        // 调用支付接口
+        renderJson(MyRet.ok("模拟支付完成"));
+    }
+
+    /**
+     * 慧用工支付回调接口
+     */
+    public void hygPaid() {
+        // 慧用工支付完回调接口,无论成败都要调用该接口
+    }
+
+    /**
+     * 用户点击取消时调用的接口
+     */
+    @Before(LoginInterceptor.class)
+    @EmptyInterface({"order_id"})
+    public void cancel() {
+        // --- 从 JSON 请求体中获取参数 ---
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+        Integer orderId;
+        // orderId
+        try {
+            orderId = requestBodyJson.getInteger("order_id");
+        } catch (Exception e) {
+            renderJson(MyRet.fail("你传入的 order_id 有些问题:" + e.getMessage()).setData(requestBodyJson));
+
+            return;
+        }
+        if (orderId == null) {
+            renderJson(MyRet.fail("id(order_id) 有问题"));
+            return;
+        }
+        User user = userService.findUserByMobileNumber(this.<String>getSessionAttr("mobile_number"));
+        if (user == null) {
+            renderJson(MyRet.fail("获取用户信息失败,请重新登录"));
+            return;
+        }
+        
+        // 判断订单归属权
+        if (service.isOrderBelongToUser(user.getId(), orderId)) {
+            renderJson(MyRet.fail("订单信息错误,再违规请求将封禁ip"));
+            return;
+        }
+        
+        // 判断订单状态
+        Order order = Order.dao.findById(orderId);
+        if (order.getOrderStatus() == OrderStatus.COMPLETED.code 
+                || order.getOrderStatus() == OrderStatus.PAID.code // 如果取消的已支付订单就要进入退款流程,等客服审批,这里先不做这个
+                || order.getOrderStatus() == OrderStatus.CANCELED.code) {
+            renderJson(MyRet.fail("无法取消已完成的订单,请刷新后尝试"));
+            return;
+        }
+
+        renderJson(service.cancelByUser(user.getId(), orderId));
+    }
+
+    /**
+     * 用户调用的订单列表
+     */
+    @Before(LoginInterceptor.class)
+    public void orderList() {
+        User user = userService.findUserByMobileNumber(this.<String>getSessionAttr("mobile_number"));
+        
+        renderJson(service.findOrderByUser(user.getId()));
+    }
+
+    /**
+     * 管理员调用的订单列表,包含查询条件,这个得有
+     */
+    @Before(LoginInterceptor.class)
+    @RequiredRoleInterface({UserController.ROLE_SUPER_ADMIN})
+    @EmptyInterface({"page_size", "page_number"})
+    public void orderListByAdmin() {
+        // --- 从 JSON 请求体中获取参数 ---
+        JSONObject requestBodyJson = MyController.getJsonModelByRequestAndType(getRequest(), JSONObject.class);
+        Integer orderStatus;
+        try {
+            orderStatus = requestBodyJson.getInteger("order_status");
+        } catch (Exception e) {
+            renderJson(MyRet.fail("你传入的 order_status 有些问题:" + e.getMessage()).setData(requestBodyJson));
+
+            return;
+        }
+
+        // 页面大小
+        String pageSizeStr = requestBodyJson.getString("page_size");
+        int pageSizeInt;
+        try {
+            pageSizeInt = Integer.parseInt(pageSizeStr);
+
+            if (pageSizeInt <= 0) {
+                renderJson(MyRet.fail("页面大小(page_size)期待是正整数,你传的是: " + pageSizeStr));
+                return;
+            }
+        } catch (Exception e) {
+            renderJson(MyRet.fail("页面大小(page_size)格式不正确: " + e.getMessage()));
+            return;
+        }
+
+        // 页码
+        String pageNumberStr = requestBodyJson.getString("page_number");
+        int pageNumberInt;
+        try {
+            pageNumberInt = Integer.parseInt(pageNumberStr);
+
+            if (pageNumberInt <= 0) {
+                renderJson(MyRet.fail("页码(page_number)期待是正整数,你传的是: " + pageNumberStr));
+                return;
+            }
+        } catch (Exception e) {
+            renderJson(MyRet.fail("页码(page_number)格式不正确: " + e.getMessage()));
+            return;
+        }
+        
+        renderJson(service.findForAdmin(pageNumberInt, pageSizeInt, orderStatus));
+    }
+
+    /**
+     * 管理员更新状态,先不做
+     */
+    @Before(LoginInterceptor.class)
+    public void updateByAdmin() {
+        renderJson(MyRet.fail("后端还没做"));
+    }
     
     // 订单状态枚举 (建议定义在单独的类或枚举中)
     public enum OrderStatus {
-        CANCELED(0, "已取消"),
+        INIT(0, "已取消"),
         PENDING_PAY(10, "待支付"),
         PREORDER_PENDING_CONFIRM(20, "预购待确认"), // 预购已支付,等待转换为正式销售
         PREORDER_SUCCESS(30, "预购成功"), // 预购已成功,等待正式销售
@@ -72,7 +241,8 @@ public class OrderController extends MyController {
         COMPLETED(60, "已完成"),
         REFUNDING(70, "退款中"),
         REFUNDED(80, "退款成功"),
-        REFUND_FAILED(90, "退款失败");
+        REFUND_FAILED(90, "退款失败"),
+        CANCELED(100, "已取消");
 
         public final int code;
         public final String desc;

+ 244 - 2
src/main/java/modules/order/OrderService.java

@@ -1,12 +1,19 @@
 package modules.order;
 
+import com.jfinal.kit.StrKit;
 import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Page;
 import com.jfinal.plugin.activerecord.Record;
 import common.model.Order;
 import common.model.OrderLog;
 import common.utils.http.MyRet;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 public class OrderService {
     public MyRet create(long nfttId, long userId, int orderType) {
@@ -49,7 +56,7 @@ public class OrderService {
                 order.set("delivery_status", 0);
                 // ... nft铸造状态 ...
                 order.set("nft_mint_status", 0);
-                // 其它
+                // 退款状态
                 order.set("refund_status", 0);
                 // 相关时间戳
                 order.set("create_time", System.currentTimeMillis());
@@ -63,7 +70,7 @@ public class OrderService {
                 // 3. 记录订单创建日志
                 OrderLog log = new OrderLog();
                 log.set("order_id", order.getId());
-                log.set("from_status", 0); // 从无状态到待支付
+                log.set("from_status", OrderController.OrderStatus.INIT.code);
                 log.set("to_status", OrderController.OrderStatus.PENDING_PAY.code);
                 log.set("operator_id", userId); // 由用户创建
                 log.set("operator_type", 1);
@@ -81,7 +88,242 @@ public class OrderService {
             return MyRet.fail(e.getMessage());
         }
     }
+    
+    public MyRet cancelByUser(long userId, long orderId) {
+        try {
+            Db.tx(() -> {
+                // 1. 获取order和nfttId
+                Order order = Order.dao.findById(orderId);
+                long nfttId = order.getNfttId();
+                
+                // 2. 更新库存(在锁定的行上进行)
+                if (Db.update("UPDATE purchased_quantity SET purchased_quantity = purchased_quantity - 1 WHERE id = ?", nfttId) != 1) {
+                    throw new RuntimeException("更新库存失败导致的取消失败");
+                }
+
+                // 3. 取消订单
+                order.set("order_status", OrderController.OrderStatus.CANCELED.code);
+                order.set("update_time", System.currentTimeMillis());
+                
+                if (!order.update()) {
+                    throw new RuntimeException("订单更新失败导致的取消失败");
+                }
+
+                // 4. 记录订单创建日志
+                OrderLog log = new OrderLog();
+                log.set("order_id", order.getId());
+                log.set("from_status", OrderController.OrderStatus.PENDING_PAY.code);
+                log.set("to_status", OrderController.OrderStatus.CANCELED.code);
+                log.set("operator_id", userId); // 由用户取消
+                log.set("operator_type", 1);
+                log.set("change_reason", "用户手动取消");
+                log.set("create_time", System.currentTimeMillis());
+                if (!log.save()) {
+                    throw new RuntimeException("订单日志创建失败"); // 抛出异常触发回滚
+                }
+                
+                return true;
+            });
+
+            return MyRet.ok("取消成功");
+        } catch (Exception e) {
+            return MyRet.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 根据用户ID查询订单列表,并封装订单流转状态
+     * @param userId 用户ID
+     * @return 包含订单列表及状态流转信息的 MyRet 对象
+     */
+    public MyRet findOrderByUser(long userId) {
+        List<Order> orderList = Order.dao.find("SELECT * FROM t_order WHERE user_id = ? ORDER BY create_time DESC", userId);
+
+        if (orderList.isEmpty()) {
+            return MyRet.ok("查询成功").setData(new ArrayList<Map<String, Object>>());
+        }
+
+        // 调用重用方法封装日志
+        List<Map<String, Object>> resultList = encapsulateOrdersWithLogs(orderList);
+
+        return MyRet.ok("查询成功").setData(resultList);
+    }
+
+    /**
+     * 管理员查询订单列表(分页,按更新时间倒序,根据订单状态筛选)
+     * 按照用户提供的分页逻辑计算 total_row 和 total_page
+     * @param pageNumber 页码
+     * @param pageSize 页面大小
+     * @param orderStatus 订单状态码 (用于筛选,可选,如果为null或小于0则不筛选)
+     * @return 包含分页订单列表及状态流转信息的 MyRet 对象
+     */
+    public MyRet findForAdmin(int pageNumber, int pageSize, Integer orderStatus) { // 接收 Integer 类型的 orderStatus
+        // 构建用于查询当前页订单列表的 SQL
+        String columns = "id, order_sn, user_id, nftt_id, order_status, order_type, quantity, unit_price, total_price, payment_method, payment_sn, pay_time, delivery_status, delivery_time, cancel_time, closed_time, refund_status, refund_amount, refund_reason, create_time, update_time, is_deleted, memo";
+        String select = "SELECT " + columns;
+        String fromWhere = "FROM t_order"; // 基础 from 子句
+        String orderBy = "ORDER BY update_time DESC";
+
+        List<Object> paramsForList = new ArrayList<>(); // 用于 SELECT 列表查询的参数
+
+        // ✅ 根据 orderStatus 筛选
+        if (orderStatus != null && orderStatus >= 0) { // 确保 orderStatus 有效,0 通常表示“已取消”,也作为有效状态
+            fromWhere += " WHERE order_status = ?";
+            paramsForList.add(orderStatus);
+        }
+
+        // 计算 LIMIT 的 offset (偏移量)
+        int offset = (pageNumber - 1) * pageSize;
+
+        // 构建查询当前页订单列表的最终 SQL (手动添加 LIMIT)
+        String listSql = select + " " + fromWhere + " " + orderBy + " LIMIT ?, ?";
+        paramsForList.add(offset);
+        paramsForList.add(pageSize);
 
+        // 获取当前页的订单列表
+        List<Order> orderList = Order.dao.find(listSql, paramsForList.toArray());
+
+        // 如果列表为空,直接返回
+        if (orderList.isEmpty()) {
+            Map<String, Object> response = new HashMap<>(); // 准备返回结构
+            response.put("list", new ArrayList<>());
+            response.put("total_row", 0);
+            response.put("total_page", 0);
+            response.put("page_size", pageSize);
+            response.put("page_number", pageNumber);
+            response.put("total_order_count", 0); // 与你的 total_user_count 对应
+            return MyRet.ok("查询成功").setData(response);
+        }
+
+        // 重用封装日志的逻辑
+        List<Map<String, Object>> encapsulatedOrderList = encapsulateOrdersWithLogs(orderList);
+
+        // 获取符合搜索条件的总行数 (total_row)
+        // 调用新的 countOrders 方法,传入 orderStatus 进行筛选计数
+        long totalRowLong = countOrders(orderStatus);
+
+        // 手动计算 total_page
+        int totalPage = (int) Math.ceil((double) totalRowLong / pageSize);
+        if (totalPage == 0 && totalRowLong > 0) { // 解决总行数大于0但总页数为0(pageSize大于totalRow)的问题
+            totalPage = 1;
+        }
+
+        // 封装最终响应
+        Map<String, Object> response = new HashMap<>();
+        response.put("list", encapsulatedOrderList);
+        response.put("total_row", totalRowLong); // 符合条件的订单总数
+        response.put("total_page", totalPage); // 手动计算的总页数
+        response.put("page_size", pageSize);
+        response.put("page_number", pageNumber);
+        response.put("total_order_count", totalRowLong); // 对应 total_user_count,这里是符合订单状态筛选的总数
+
+        return MyRet.ok("查询成功").setData(response);
+    }
+
+    /**
+     * 【重用方法】封装订单列表及其状态流转日志
+     * @param orderList 原始订单列表
+     * @return 封装了日志和描述信息的订单列表
+     */
+    private List<Map<String, Object>> encapsulateOrdersWithLogs(List<Order> orderList) {
+        if (orderList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 2. 获取所有订单的ID,用于批量查询订单日志
+        List<Long> orderIds = orderList.stream()
+                .map(order -> order.getLong("id"))
+                .collect(Collectors.toList());
+
+        // 确保 orderIds 不为空,避免 SQL 错误
+        if (orderIds.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 3. 一次性查询所有订单相关的状态日志
+        StringBuilder inClause = new StringBuilder();
+        for (int i = 0; i < orderIds.size(); i++) {
+            inClause.append("?");
+            if (i < orderIds.size() - 1) {
+                inClause.append(",");
+            }
+        }
+        String sql = "SELECT * FROM t_order_log WHERE order_id IN (" + inClause.toString() + ") ORDER BY order_id, create_time ASC";
+        List<OrderLog> orderLogs = OrderLog.dao.find(sql, orderIds.toArray());
+
+        // 4. 将日志按 order_id 分组
+        Map<Long, List<Map<String, Object>>> orderLogsMap = new HashMap<>();
+        for (OrderLog log : orderLogs) {
+            long orderId = log.getLong("order_id");
+            orderLogsMap.computeIfAbsent(orderId, k -> new ArrayList<>())
+                    .add(convertOrderLogToMap(log));
+        }
+
+        // 5. 遍历订单列表,封装日志和状态描述
+        List<Map<String, Object>> resultList = new ArrayList<>();
+        for (Order order : orderList) {
+            Map<String, Object> orderData = order.toMap();
+
+            orderData.put("order_status_desc", getStatusDescByCode(order.getInt("order_status")));
+
+            List<Map<String, Object>> currentOrderLogs = orderLogsMap.getOrDefault(order.getLong("id"), new ArrayList<>());
+            orderData.put("status_history", currentOrderLogs);
+
+            resultList.add(orderData);
+        }
+        return resultList;
+    }
+
+    public long countOrders(Integer orderStatus) { // 接收 Integer 类型的 orderStatus
+        String sql = "SELECT COUNT(id) FROM t_order"; // COUNT(id) 更清晰
+        List<Object> params = new ArrayList<>();
+
+        // ✅ 根据 orderStatus 筛选
+        if (orderStatus != null && orderStatus >= 0) {
+            sql += " WHERE order_status = ?";
+            params.add(orderStatus);
+        }
+        return Db.queryLong(sql, params.toArray());
+    }
+    
+    public boolean isOrderBelongToUser(long userId, long orderId) {
+        String sql = "SELECT COUNT(1) FROM t_order" +
+                " WHERE id=? and user_id=?"; 
+        
+        return Db.queryInt(sql, orderId, userId) != 1;
+    }
+
+    /**
+     * 辅助方法:将 OrderLog Model 转换为 Map,并添加状态描述
+     * @param log OrderLog 对象
+     * @return 包含描述信息的 Map
+     */
+    private Map<String, Object> convertOrderLogToMap(OrderLog log) {
+        Map<String, Object> logMap = log.toMap(); // 获取原始日志属性
+
+        // 添加状态描述
+        logMap.put("from_status_desc", getStatusDescByCode(log.getInt("from_status")));
+        logMap.put("to_status_desc", getStatusDescByCode(log.getInt("to_status")));
+
+        return logMap;
+    }
+
+    /**
+     * 根据状态码获取状态描述
+     * 假设你已经定义了 OrderStatus 枚举
+     *
+     * @param statusCode 状态码
+     * @return 状态描述字符串
+     */
+    private String getStatusDescByCode(int statusCode) {
+        for (OrderController.OrderStatus status : OrderController.OrderStatus.values()) {
+            if (status.code == statusCode) {
+                return status.desc;
+            }
+        }
+        return "未知状态"; // 如果找不到对应的状态码
+    }
+    
     private String generateOrderSn() {
         return System.currentTimeMillis() + "" + (int)(Math.random() * 10000);
     }