Răsfoiți Sursa

Merge remote-tracking branch 'origin/sit-new-checkstand' into sit-new-checkstand

刘云鑫 1 săptămână în urmă
părinte
comite
781c5c1b68

+ 7 - 0
alien-entity/src/main/java/shop/alien/entity/store/PrinterBrand.java

@@ -0,0 +1,7 @@
+package shop.alien.entity.store;
+
+public enum PrinterBrand {
+    XINYE,    // 芯烨云打印机
+    JIAHANG,  // 佳航云打印机
+    SHANGPENG // 商鹏云打印机
+}

+ 0 - 21
alien-entity/src/main/java/shop/alien/entity/store/PrinterConfig.java

@@ -1,21 +0,0 @@
-package shop.alien.entity.store;
-
-import lombok.Data;
-
-@Data
-public class PrinterConfig {
-
-    private Integer id;
-    private String printerCode;
-    private String printerName;
-    private String brand;
-    private String connectType;
-    private String ip;
-    private Integer port;
-    private String comPort;
-    private Integer paperWidth;
-    private Integer enabled;
-    private String remark;
-
-
-}

+ 46 - 0
alien-entity/src/main/java/shop/alien/entity/store/PrinterDO.java

@@ -0,0 +1,46 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 打印机数据库实体
+ * 对应数据库表:printer
+ */
+@Data
+@TableName("printer") // 绑定数据库表名
+public class PrinterDO {
+
+    // 主键自增ID
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    // 品牌:XINYE/JIAHANG/SHANGPENG
+    private String brand;
+
+    // 打印机名称(前台/后厨)
+    private String printerName;
+
+    // 设备编号SN(每台打印机唯一)
+    private String sn;
+
+    // 开发者ID / 商户ID(芯烨=user,佳航=merchantId)
+    private String userId;
+
+    // API密钥 / UserKey / Token
+    private String apiKey;
+
+    // 备注
+    private String remark;
+
+    // 创建时间
+    private LocalDateTime createTime;
+    // 店铺ID
+    private  Integer storeId;
+    // 逻辑删除
+    private String delFlag;
+}

+ 0 - 18
alien-entity/src/main/java/shop/alien/entity/store/Receipt.java

@@ -1,18 +0,0 @@
-package shop.alien.entity.store;
-
-import lombok.Data;
-
-@Data
-public class Receipt {
-
-
-
-    private String orderNo;
-    private String createTime;
-    private double totalAmount;
-    private String payType;
-
-
-
-
-}

+ 13 - 0
alien-entity/src/main/java/shop/alien/mapper/PrinterMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.PrinterDO;
+
+/**
+ * 邀请活动配置mapper
+ */
+@Mapper
+public interface PrinterMapper extends BaseMapper<PrinterDO> {
+
+}

+ 7 - 26
alien-store/pom.xml

@@ -42,32 +42,6 @@
             <artifactId>spring-boot-starter-jdbc</artifactId>
         </dependency>
 
-        <!-- 唯一需要的打印依赖 -->
-        <dependency>
-            <groupId>com.github.anastaciocintra</groupId>
-            <artifactId>escpos-coffee</artifactId>
-            <version>4.1.0</version>
-        </dependency>
-
-
-            <!-- ESC/POS 小票打印 -->
-            <dependency>
-                <groupId>com.github.anastaciocintra</groupId>
-                <artifactId>escpos-coffee</artifactId>
-                <version>4.1.0</version>
-            </dependency>
-
-            <!-- USB/串口通信 -->
-            <dependency>
-                <groupId>com.fazecast</groupId>
-                <artifactId>jSerialComm</artifactId>
-                <version>2.9.2</version>
-            </dependency>
-
-
-
-
-
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
@@ -234,6 +208,13 @@
             <artifactId>fastjson</artifactId>
             <version>2.0.32</version>
         </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>com.alibaba.fastjson2</groupId>-->
+<!--            <artifactId>fastjson2</artifactId>-->
+<!--            <version>2.0.23</version>-->
+<!--        </dependency>-->
+
         <!-- 用于 HMAC-SHA1 签名 -->
         <dependency>
             <groupId>commons-codec</groupId>

+ 134 - 0
alien-store/src/main/java/shop/alien/store/controller/PrintController.java

@@ -0,0 +1,134 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.extern.slf4j.Slf4j;
+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.RestController;
+import shop.alien.entity.store.PrinterDO;
+import shop.alien.entity.store.StoreReceiptTemplateConfig;
+import shop.alien.mapper.PrinterMapper;
+import shop.alien.store.service.StoreReceiptTemplateConfigService;
+import shop.alien.store.service.impl.PrintService;
+import shop.alien.store.service.impl.PrintTemplate;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@Slf4j
+@RequestMapping("/print")
+public class PrintController {
+    // 注入数据库Mapper
+    @Resource
+    private PrinterMapper printerMapper;
+
+    // 注入打印服务
+    @Resource
+    private PrintService printService;
+
+    // 门店票据模板服务
+    @Resource
+    private StoreReceiptTemplateConfigService storeReceiptTemplateConfigService;
+
+    /**
+     * 一键打印所有打印机
+     * 访问地址:http://localhost:8080/print/all
+     */
+    @GetMapping("/all")
+    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).eq(PrinterDO::getStoreId, storeId).eq(PrinterDO::getDelFlag, 0));
+
+        // 获取当前启用的模板(无启用则回落到默认模板)
+        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 );
+        printService.printAll(list, content);
+        return "已下发打印指令,共" + list.size() + "台打印机";
+    }
+
+    /**
+     * 查询所有打印机
+     * 访问地址:http://localhost:8080/print/list
+     */
+    @GetMapping("/list")
+    public Object list() {
+        return printerMapper.selectList(null);
+    }
+}

+ 0 - 11
alien-store/src/main/java/shop/alien/store/service/ReceiptPrinter.java

@@ -1,11 +0,0 @@
-package shop.alien.store.service;
-
-import shop.alien.entity.store.Receipt;
-
-public interface ReceiptPrinter {
-
-    /**
-     * 打印收银小票
-     */
-    void print(Receipt receipt) throws Exception;
-}

+ 0 - 66
alien-store/src/main/java/shop/alien/store/service/impl/NetworkReceiptPrinter.java

@@ -1,66 +0,0 @@
-package shop.alien.store.service.impl;
-
-import com.github.anastaciocintra.escpos.EscPos;
-import com.github.anastaciocintra.escpos.Style;
-import shop.alien.entity.store.Receipt;
-import shop.alien.store.service.ReceiptPrinter;
-import java.net.Socket;
-import java.io.OutputStream;
-import java.text.SimpleDateFormat;
-
-
-public class NetworkReceiptPrinter implements ReceiptPrinter {
-    private final String ip;
-    private final int port;
-
-    public NetworkReceiptPrinter(String ip, int port) {
-        this.ip = ip;
-        this.port = port;
-    }
-
-
-
-
-    @Override
-    public void print(Receipt receipt) throws Exception {
-        try (Socket socket = new Socket(ip, port);
-             OutputStream out = socket.getOutputStream()) {
-
-            EscPos escpos = new EscPos(out);
-            doPrint(escpos, receipt);
-
-            escpos.cut(EscPos.CutMode.FULL);
-            escpos.flush();
-        }
-             
-    }
-
-    private void doPrint(EscPos escpos, Receipt receipt) throws Exception {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
-//        escpos.setAlign(Style.Align.CENTER)
-//                .setFontSize(Style.FontSize._2, Style.FontSize._2)
-//                .writeLn("XX便利店")
-//                .feed(1);
-//
-//        escpos.setFontSize(Style.FontSize._1, Style.FontSize._1)
-//                .setAlign(Style.Align.LEFT)
-//                .writeLn("单号:" + receipt.getOrderNo())
-//                .writeLn("时间:" + sdf.format(receipt.getCreateTime()))
-//                .writeLn("------------------------------");
-//
-//        for (ReceiptItem item : receipt.getItems()) {
-//            escpos.writeLn(item.getName() + " ×" + item.getQty());
-//            escpos.writeLn(String.format("          %s × %s = %s",
-//                    item.getPrice(), item.getQty(), item.getAmount()));
-//        }
-//
-//        escpos.writeLn("------------------------------")
-//                .setAlign(Style.Align.RIGHT)
-//                .writeLn("合计:" + receipt.getTotalAmount())
-//                .writeLn("支付:" + receipt.getPayType())
-//                .feed(3);
-    }
-
-
-}

+ 134 - 0
alien-store/src/main/java/shop/alien/store/service/impl/PrintService.java

@@ -0,0 +1,134 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.PrinterDO;
+import shop.alien.store.util.Base64Util;
+import shop.alien.store.util.ShaUtil;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class PrintService {
+    // HTTP请求工具
+    private final OkHttpClient client = new OkHttpClient();
+    // JSON请求头
+    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+
+    /**
+     * 批量打印:给所有打印机发打印指令
+     * @param list 打印机列表
+     * @param content 打印内容
+     */
+    public void printAll(List<PrinterDO> list, String content) {
+        for (PrinterDO p : list) {
+            // 异步打印:不阻塞,多打印机同时打印
+            new Thread(() -> printWithRetry(p, content, 3)).start();
+        }
+    }
+
+    /**
+     * 打印重试机制
+     * @param printer 打印机
+     * @param content 内容
+     * @param retry 重试次数
+     * @return 是否成功
+     */
+    public boolean printWithRetry(PrinterDO printer, String content, int retry) {
+        while (retry-- > 0) {
+            try {
+                boolean ok = printSingle(printer, content);
+                if (ok) return true;
+            } catch (Exception e) {
+                System.err.println("打印失败,剩余重试:" + retry);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 单台打印机打印
+     * 根据品牌自动分发
+     */
+    public boolean printSingle(PrinterDO printer, String content) {
+        String brand = printer.getBrand();
+        if ("XINYE".equalsIgnoreCase(brand)) {
+            return printXinYe(printer, content);
+        } else if ("JIAHANG".equalsIgnoreCase(brand)) {
+            return printJiaHang(printer, content);
+        } else if ("SHANGPENG".equalsIgnoreCase(brand)) {
+            return printShangPeng(printer, content);
+        }
+        return false;
+    }
+
+    // ==================== 芯烨打印实现 ====================
+    private boolean printXinYe(PrinterDO p, String content) {
+        // 时间戳(秒)
+        long ts = System.currentTimeMillis() / 1000;
+        // SHA1签名:user + userKey + timestamp
+        String sign = ShaUtil.sha1(p.getUserId() + p.getApiKey() + ts);
+
+        // 构造API参数
+        JSONObject req = new JSONObject();
+        req.put("user", p.getUserId());
+        req.put("sn", p.getSn());
+//        req.put("content", Base64Util.encode(content));
+        req.put("content", content);
+        req.put("copies", 1);
+        req.put("timestamp", ts);
+        req.put("sign", sign);
+        req.put("mode", 1);
+
+        // 发送请求
+        return post("https://open.xpyun.net/api/openapi/xprinter/print", req);
+    }
+
+    // ==================== 佳航打印实现 ====================
+    private boolean printJiaHang(PrinterDO p, String content) {
+        JSONObject req = new JSONObject();
+        req.put("merchantId", p.getUserId());
+        req.put("deviceNo", p.getSn());
+        req.put("key", p.getApiKey());
+        req.put("printContent", Base64Util.encode(content));
+        req.put("copy", 1);
+        return post("http://api.jhprt.com/api/print", req);
+    }
+
+    // ==================== 商鹏打印实现 ====================
+    private boolean printShangPeng(PrinterDO p, String content) {
+        JSONObject req = new JSONObject();
+        req.put("sn", p.getSn());
+        req.put("token", p.getApiKey());
+        req.put("content", Base64Util.encode(content));
+        req.put("times", 1);
+        return post("https://api.spyun.net/api/print", req);
+    }
+
+    /**
+     * 统一发送POST请求
+     */
+    private boolean post(String url, JSONObject req) {
+        try {
+            RequestBody body = RequestBody.create(JSON, req.toJSONString());
+            Request request = new Request.Builder().url(url).post(body).build();
+            try (Response res = client.newCall(request).execute()) {
+                String str = res.body().string();
+                System.out.println("打印响应:" + str);
+                JSONObject json = JSONObject.parseObject(str);
+                // 通用成功判断:code=0 或 success=true
+                return json.getIntValue("code") == 0 || json.getBooleanValue("success");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}

+ 185 - 0
alien-store/src/main/java/shop/alien/store/service/impl/PrintTemplate.java

@@ -0,0 +1,185 @@
+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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PrintTemplate {
+
+    /**
+     * 兼容旧的静态示例生成方法(保留)
+     */
+    public static String buildOrder(String orderNo, String items, String total) {
+        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" +
+                "时间:" + LocalDateTime.now() + "\n" +
+                "----------------------------\n" +
+                items +
+                "----------------------------\n" +
+                "合计:" + total + " 元\n" +
+                "支付方式:微信支付\n" +
+                "打印时间:"+ 2026 + "-04-06 13:00" + "\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);
+    }
+}

+ 17 - 0
alien-store/src/main/java/shop/alien/store/util/Base64Util.java

@@ -0,0 +1,17 @@
+package shop.alien.store.util;
+import org.apache.commons.codec.binary.Base64;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Base64编码工具
+ * 云打印API要求打印内容必须用Base64传输
+ */
+public class Base64Util {
+
+    /**
+     * 字符串转Base64
+     */
+    public static String encode(String str) {
+        return Base64.encodeBase64String(str.getBytes(StandardCharsets.UTF_8));
+    }
+}

+ 0 - 26
alien-store/src/main/java/shop/alien/store/util/PrinterFactory.java

@@ -1,26 +0,0 @@
-package shop.alien.store.util;
-
-import shop.alien.entity.store.PrinterConfig;
-import shop.alien.store.service.ReceiptPrinter;
-import shop.alien.store.service.impl.NetworkReceiptPrinter;
-
-
-public class PrinterFactory {
-
-
-
-    public static ReceiptPrinter getPrinter(PrinterConfig config) {
-        String type = config.getConnectType();
-
-        if ("NETWORK".equalsIgnoreCase(type)) {
-            return new NetworkReceiptPrinter(config.getIp(), config.getPort());
-        }
-
-//        if ("USB".equalsIgnoreCase(type) || "SERIAL".equalsIgnoreCase(type)) {
-//            return new SerialReceiptPrinter(config.getComPort());
-//        }
-
-        throw new RuntimeException("不支持的打印机类型:" + type);
-    }
-
-}

+ 29 - 0
alien-store/src/main/java/shop/alien/store/util/ShaUtil.java

@@ -0,0 +1,29 @@
+package shop.alien.store.util;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+public class ShaUtil {
+    /**
+     * SHA1加密
+     * @param s 待加密字符串
+     * @return 小写32位加密结果
+     */
+    public static String sha1(String s) {
+        try {
+            // 获取SHA1算法实例
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            // 加密
+            byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
+            // 转16进制
+            StringBuilder sb = new StringBuilder();
+            for (byte b : bytes) {
+                String hex = Integer.toHexString(0xff & b);
+                if (hex.length() == 1) sb.append('0');
+                sb.append(hex);
+            }
+            // 返回小写
+            return sb.toString().toLowerCase();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+}