Sfoglia il codice sorgente

暂存修改版的打票机打印 动态样式

liudongzhi 1 settimana fa
parent
commit
7f7972c5f2

+ 85 - 5
alien-store/src/main/java/shop/alien/store/controller/PrintController.java

@@ -3,15 +3,21 @@ package shop.alien.store.controller;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 import shop.alien.entity.store.PrinterDO;
 import shop.alien.entity.store.PrinterDO;
+import shop.alien.entity.store.StoreReceiptTemplateConfig;
 import shop.alien.mapper.PrinterMapper;
 import shop.alien.mapper.PrinterMapper;
+import shop.alien.store.service.StoreReceiptTemplateConfigService;
 import shop.alien.store.service.impl.PrintService;
 import shop.alien.store.service.impl.PrintService;
 import shop.alien.store.service.impl.PrintTemplate;
 import shop.alien.store.service.impl.PrintTemplate;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 @RestController
 @RestController
 @Slf4j
 @Slf4j
@@ -25,18 +31,92 @@ public class PrintController {
     @Resource
     @Resource
     private PrintService printService;
     private PrintService printService;
 
 
+    // 门店票据模板服务
+    @Resource
+    private StoreReceiptTemplateConfigService storeReceiptTemplateConfigService;
+
     /**
     /**
      * 一键打印所有打印机
      * 一键打印所有打印机
      * 访问地址:http://localhost:8080/print/all
      * 访问地址:http://localhost:8080/print/all
      */
      */
     @GetMapping("/all")
     @GetMapping("/all")
-    public String printAll() {
+    public String printAll(@RequestParam(value = "storeId", required = false) Integer storeId,
+                           @RequestParam(value = "receiptType", required = false) Integer receiptType) {
+        // 默认店铺与票据类型(1:客单, 2:结账单)
+        if (storeId == null) storeId = 1;
+        if (receiptType == null) receiptType = 2;
+
         // 查询数据库所有打印机
         // 查询数据库所有打印机
         List<PrinterDO> list = printerMapper.selectList(new LambdaQueryWrapper<PrinterDO>().isNotNull(PrinterDO::getSn));
         List<PrinterDO> list = printerMapper.selectList(new LambdaQueryWrapper<PrinterDO>().isNotNull(PrinterDO::getSn));
-        // 生成小票内容
-        String content = PrintTemplate.buildOrder("ORDER20260406001",
-                "可乐 3.00 x2 6.00\n方便面 5.00 x1 5.00\n",
-                "11.00");
+
+        // 获取当前启用的模板(无启用则回落到默认模板)
+        List<StoreReceiptTemplateConfig> templates = storeReceiptTemplateConfigService.listByStoreAndReceiptType(storeId, receiptType);
+        StoreReceiptTemplateConfig enabled = null;
+        for (StoreReceiptTemplateConfig t : templates) {
+            if (t != null && t.getEnabled() != null && t.getEnabled() == 1) {
+                enabled = t;
+                break;
+            }
+        }
+        if (enabled == null) {
+            enabled = storeReceiptTemplateConfigService.getDetail(storeId, receiptType, 1);
+        }
+
+        // 组装渲染所需数据(示例数据,可替换为真实订单数据)
+        Map<String, Object> ctx = new HashMap<>();
+        ctx.put("ticketName", receiptType != null && receiptType == 2 ? "结账单" : "客单");
+        ctx.put("storeName", "测试门店");
+        ctx.put("orderNo", "ORDER20260406001");
+        ctx.put("tableNo", "A01");
+        ctx.put("peopleCount", 4);
+        ctx.put("diningTime", "2026-04-06 10:00");
+        ctx.put("cashierTime", "2026-04-06 13:00");
+        ctx.put("name", "张大大");
+        ctx.put("phone", "18000000000");
+        ctx.put("beginRemark", "无");
+
+        // 品项
+        List<Map<String, Object>> items = new ArrayList<>();
+        Map<String, Object> i1 = new HashMap<>();
+        i1.put("name", "可乐");
+        i1.put("unitPrice", "3.00");
+        i1.put("quantity", 2);
+        i1.put("subtotal", "6.00");
+        items.add(i1);
+        Map<String, Object> i2 = new HashMap<>();
+        i2.put("name", "方便面");
+        i2.put("unitPrice", "5.00");
+        i2.put("quantity", 1);
+        i2.put("subtotal", "5.00");
+        items.add(i2);
+        ctx.put("items", items);
+
+        // 订单价格合计
+        ctx.put("dishPriceTotal", "11.00");
+        ctx.put("serviceFeeTotal", "0.00");
+        ctx.put("otherServiceFeeTotal", "0.00");
+        ctx.put("chargeReason", "");
+        ctx.put("orderTotal", "11.00");
+
+        // 支付与结算信息
+        ctx.put("coupon", "0.00");
+        ctx.put("manualReduction", "0.00");
+        ctx.put("reductionReason", "");
+        ctx.put("settlementMethod", "在线支付");
+        ctx.put("paymentMethod", "微信支付");
+        ctx.put("paymentTotal", "11.00");
+
+        // 底栏信息
+        ctx.put("orderBy", "路通");
+        ctx.put("orderAt", "2026-04-06 10:00");
+        ctx.put("printBy", "系统");
+        ctx.put("printAt", String.valueOf(java.time.LocalDateTime.now()));
+        ctx.put("storeAddress", "XX市XX区XX路100号");
+        ctx.put("storeTel", "020-12345678");
+
+        // 动态渲染小票
+        String content = PrintTemplate.buildFromConfig(enabled != null ? enabled.getTemplateConfigJson() : "{}", ctx);
+
         // 批量打印
         // 批量打印
         log.info("打印内容:"+ content );
         log.info("打印内容:"+ content );
         printService.printAll(list, content);
         printService.printAll(list, content);

+ 165 - 6
alien-store/src/main/java/shop/alien/store/service/impl/PrintTemplate.java

@@ -1,18 +1,31 @@
 package shop.alien.store.service.impl;
 package shop.alien.store.service.impl;
 
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 
 public class PrintTemplate {
 public class PrintTemplate {
 
 
     /**
     /**
-     * 生成订单小票
-     * @param orderNo 订单号
-     * @param items 商品列表
-     * @param total 总金额
-     * @return 打印文本
+     * 兼容旧的静态示例生成方法(保留)
      */
      */
     public static String buildOrder(String orderNo, String items, String total) {
     public static String buildOrder(String orderNo, String items, String total) {
-        return "========== 测试门店 ==========\n" +
+        return  "            结账单           \n" +
+                "           测试门店           \n" +
+                "============================\n" +
+                "桌号:" + 1 + "\n" +
+                "单号:" + 2 + "\n" +
+                "人数:" + 4 + "\n" +
+                "就餐时间:" + 2026 + "-04-06 10:00" + "\n" +
+                "姓名:" + "张大大" + "\n" +
+                "电话:" + 1800000000 + "\n" +
+                "结账时间:" + 2026 + "-04-06 13:00" + "\n" +
+                "收银员:" + "路通" + "\n" +
                 "订单号:" + orderNo + "\n" +
                 "订单号:" + orderNo + "\n" +
                 "时间:" + LocalDateTime.now() + "\n" +
                 "时间:" + LocalDateTime.now() + "\n" +
                 "----------------------------\n" +
                 "----------------------------\n" +
@@ -20,7 +33,153 @@ public class PrintTemplate {
                 "----------------------------\n" +
                 "----------------------------\n" +
                 "合计:" + total + " 元\n" +
                 "合计:" + total + " 元\n" +
                 "支付方式:微信支付\n" +
                 "支付方式:微信支付\n" +
+                "打印时间:"+ 2026 + "-04-06 13:00" + "\n" +
                 "----------------------------\n" +
                 "----------------------------\n" +
                 "欢迎再次光临\n\n\n";
                 "欢迎再次光临\n\n\n";
     }
     }
+
+    /**
+     * 基于 store_receipt_template_config 的 JSON 配置动态渲染小票
+     * @param templateConfigJson 模板配置JSON(来自表 store_receipt_template_config.template_config_json)
+     * @param context 业务数据上下文(key 需与模板字段 key 对应;品项请放在 key = \"items\" 的 List<Map> 中)
+     * @return 打印文本
+     */
+    public static String buildFromConfig(String templateConfigJson, Map<String, Object> context) {
+        if (templateConfigJson == null || templateConfigJson.trim().isEmpty()) {
+            return "";
+        }
+        JSONObject root = JSON.parseObject(templateConfigJson);
+        if (root == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        JSONArray sections = root.getJSONArray("sections");
+        if (sections == null) {
+            return "";
+        }
+
+        // 辅助:横线与安全取值
+        String line = "----------------------------\n";
+        String thickLine = "============================\n";
+
+        for (int i = 0; i < sections.size(); i++) {
+            JSONObject section = sections.getJSONObject(i);
+            if (section == null) {
+                continue;
+            }
+            JSONArray fields = section.getJSONArray("fields");
+            if (fields == null) {
+                continue;
+            }
+            // section 之间用粗分割
+            if (i == 0) {
+                // 首段上方打印抬头分割线(可选)
+                sb.append(thickLine);
+            } else {
+                sb.append(line);
+            }
+
+            for (int j = 0; j < fields.size(); j++) {
+                JSONObject field = fields.getJSONObject(j);
+                if (field == null) {
+                    continue;
+                }
+                boolean visible = field.getBooleanValue("visible");
+                if (!visible) {
+                    continue;
+                }
+                String key = field.getString("key");
+                String titleName = nvl(field.getString("titleName"));
+                boolean divider = field.getBooleanValue("divider");
+                int blankLines = field.getIntValue("blankLines");
+
+                if ("item".equals(key)) {
+                    // 品项区:打印表头 + 列表
+                    JSONArray children = field.getJSONArray("children");
+                    boolean showUnitPrice = childVisible(children, "unitPrice");
+                    boolean showQuantity = childVisible(children, "quantity");
+                    boolean showSubtotal = childVisible(children, "subtotal");
+
+                    // 表头
+                    sb.append(titleName).append("\n");
+                    // 列标题(简洁处理)
+                    List<String> headers = new ArrayList<>();
+                    headers.add("品名");
+                    if (showUnitPrice) headers.add("单价");
+                    if (showQuantity) headers.add("数量");
+                    if (showSubtotal) headers.add("小计");
+                    sb.append(String.join(" ", headers)).append("\n");
+
+                    // 数据
+                    @SuppressWarnings("unchecked")
+                    List<Map<String, Object>> items = (List<Map<String, Object>>) context.getOrDefault("items", new ArrayList<>());
+                    for (Map<String, Object> item : items) {
+                        String name = str(item.get("name"));
+                        List<String> cols = new ArrayList<>();
+                        cols.add(name);
+                        if (showUnitPrice) cols.add(str(item.get("unitPrice")));
+                        if (showQuantity) cols.add("x" + str(item.get("quantity")));
+                        if (showSubtotal) cols.add(str(item.get("subtotal")));
+                        sb.append(String.join(" ", cols)).append("\n");
+                    }
+                } else if ("orderPrice".equals(key) || "paymentDiscount".equals(key) || "paymentInfo".equals(key)) {
+                    // 具有子项的分组型文本
+                    JSONArray children = field.getJSONArray("children");
+                    if (children != null && !children.isEmpty()) {
+                        sb.append(titleName).append("\n");
+                        for (int k = 0; k < children.size(); k++) {
+                            JSONObject child = children.getJSONObject(k);
+                            if (child == null || !child.getBooleanValue("visible")) continue;
+                            String subKey = nvl(child.getString("key"));
+                            String subTitle = nvl(child.getString("titleName"));
+                            if ("divider".equals(subKey)) {
+                                sb.append(line);
+                                continue;
+                            }
+                            Object val = context.get(subKey);
+                            if (val != null && !"".equals(String.valueOf(val).trim())) {
+                                sb.append(subTitle).append(":").append(val).append("\n");
+                            }
+                        }
+                    }
+                } else {
+                    // 普通文本字段
+                    Object value = context.get(key);
+                    if (value != null && !"".equals(String.valueOf(value).trim())) {
+                        sb.append(titleName).append(":").append(value).append("\n");
+                    }
+                }
+
+                if (divider) {
+                    sb.append(line);
+                }
+                for (int b = 0; b < blankLines; b++) {
+                    sb.append("\n");
+                }
+            }
+        }
+        // 末尾多留空行,部分打印机需要
+        sb.append("\n\n");
+        return sb.toString();
+    }
+
+    private static boolean childVisible(JSONArray children, String key) {
+        if (children == null) return false;
+        for (int i = 0; i < children.size(); i++) {
+            JSONObject c = children.getJSONObject(i);
+            if (c == null) continue;
+            if (key.equals(c.getString("key"))) {
+                return c.getBooleanValue("visible");
+            }
+        }
+        return false;
+    }
+
+    private static String nvl(String s) {
+        return s == null ? "" : s;
+    }
+
+    private static String str(Object v) {
+        return v == null ? "" : String.valueOf(v);
+    }
 }
 }