liangwenxuan hace 1 mes
padre
commit
26adb72f04
Se han modificado 22 ficheros con 785 adiciones y 212 borrados
  1. 8 7
      configure/dev/application.properties
  2. 6 5
      configure/prod/application.properties
  3. 26 0
      db/update_for_20260903.sql
  4. 3 0
      fuint-application/src/main/java/com/fuint/common/dto/order/OrderGoodsDto.java
  5. 7 0
      fuint-application/src/main/java/com/fuint/common/param/PrinterParam.java
  6. 9 1
      fuint-application/src/main/java/com/fuint/common/service/OrderService.java
  7. 39 3
      fuint-application/src/main/java/com/fuint/common/service/PrinterService.java
  8. 4 5
      fuint-application/src/main/java/com/fuint/common/service/WeixinService.java
  9. 1 1
      fuint-application/src/main/java/com/fuint/common/service/impl/BalanceServiceImpl.java
  10. 12 0
      fuint-application/src/main/java/com/fuint/common/service/impl/CateServiceImpl.java
  11. 13 12
      fuint-application/src/main/java/com/fuint/common/service/impl/GoodsServiceImpl.java
  12. 15 17
      fuint-application/src/main/java/com/fuint/common/service/impl/OrderServiceImpl.java
  13. 402 88
      fuint-application/src/main/java/com/fuint/common/service/impl/PrinterServiceImpl.java
  14. 95 54
      fuint-application/src/main/java/com/fuint/common/service/impl/WeixinServiceImpl.java
  15. 52 0
      fuint-application/src/main/java/com/fuint/module/backendApi/controller/merchant/BackendPrinterController.java
  16. 17 7
      fuint-application/src/main/java/com/fuint/module/clientApi/controller/ClientSignController.java
  17. 4 4
      fuint-application/src/main/java/com/fuint/module/clientApi/controller/ClientUserController.java
  18. 0 6
      fuint-application/src/main/java/com/fuint/module/clientApi/request/MemberInfoRequest.java
  19. 2 2
      fuint-application/src/main/resources/application.properties
  20. 14 0
      fuint-repository/src/main/java/com/fuint/repository/mapper/MtPrinterCateMapper.java
  21. 3 0
      fuint-repository/src/main/java/com/fuint/repository/model/MtPrinter.java
  22. 53 0
      fuint-repository/src/main/java/com/fuint/repository/model/MtPrinterCate.java

+ 8 - 7
configure/dev/application.properties

@@ -13,7 +13,7 @@ spring.redis.host=47.107.245.0
 # Redis服务器连接端口
 spring.redis.port=6379
 # Redis服务器连接密码(默认为空)
-spring.redis.password=kyjYn@2020lsj
+spring.redis.password=kyjYn2020lsj
 # 连接池最大连接数(使用负值表示没有限制)
 spring.redis.pool.max-active=-1
 # 连接池最大阻塞等待时间(使用负值表示没有限制)
@@ -29,7 +29,7 @@ spring.redis.timeout=0
 system.name = fuint餐饮会员系统
 
 # 前端h5地址
-website.url=https://home.ynims.com:8881/
+website.url=https://home.ynims.com/funit
 
 # 上传图片本地地址
 images.root=E:/ideaWorkspace/fuintBackend/fuint-application/target/classes
@@ -87,11 +87,12 @@ weixin.official.appId=wxf4327ef05c27a0
 weixin.official.appSecret=1f55e8749332234d9a074873d8e6a3
 
 # 小程序配置
-wxpay.appId=wxb6af3741686162bc
-wxpay.appSecret=76a538bfa5b55a72de6214d5f0be5540
-wxpay.mchId=1636980848
-wxpay.apiV2=fuyuan20201030yanhe9999999999999
-wxpay.certPath=/usr/local/fuint/cert/apiclient_cert.p12
+wxpay.appId = wx602c7b6f1f00558f
+wxpay.appSecret = 3944d660349e35300800decb187cb4f8
+wxpay.mchId=1117146807
+wxpay.apiV2=K9mP2xQ5vR8nT4wY7zL1cF3hJ6bS0eAq
+wxpay.apiV3=X7rN5gH2wV9mP3sK8tL1yF6jR4cB0zDU
+wxpay.certPath=/work/projects/funit/configure/apiclient_cert.p12
 wxpay.domain=https://home.ynims.com:8881
 
 ################## 支付宝支付相关配置 ######################

+ 6 - 5
configure/prod/application.properties

@@ -13,7 +13,7 @@ spring.redis.host=47.107.245.0
 # Redis服务器连接端口
 spring.redis.port=6379
 # Redis服务器连接密码(默认为空)
-spring.redis.password=kyjYn@2020lsj
+spring.redis.password=kyjYn2020lsj
 # 连接池最大连接数(使用负值表示没有限制)
 spring.redis.pool.max-active=-1
 # 连接池最大阻塞等待时间(使用负值表示没有限制)
@@ -29,7 +29,7 @@ spring.redis.timeout=0
 system.name = fuint餐饮会员系统
 
 # 前端h5地址
-website.url=https://home.ynims.com:8881/
+website.url=https://home.ynims.com/funit
 
 # 上传图片本地地址
 images.root=/work/projects/funit
@@ -89,9 +89,10 @@ weixin.official.appSecret=1f55e8749332234d9a074873d8e6a3
 # 小程序配置
 wxpay.appId = wx602c7b6f1f00558f
 wxpay.appSecret = 3944d660349e35300800decb187cb4f8
-wxpay.mchId=1636980812
-wxpay.apiV2=34354320201030y323e432342343
-wxpay.certPath=/usr/local/fuint/cert/apiclient_cert.p12
+wxpay.mchId=1117146807
+wxpay.apiV2=K9mP2xQ5vR8nT4wY7zL1cF3hJ6bS0eAq
+wxpay.apiV3=X7rN5gH2wV9mP3sK8tL1yF6jR4cB0zDU
+wxpay.certPath=/work/projects/funit/configure/apiclient_cert.p12
 wxpay.domain=https://home.ynims.com:8881
 
 ################## 支付宝支付相关配置 ######################

+ 26 - 0
db/update_for_20260903.sql

@@ -0,0 +1,26 @@
+-- =====================================================
+-- 打印机与商品分类映射 - 数据库变更脚本
+-- 2026-09-03
+-- =====================================================
+
+-- 1. 打印机与商品分类映射表
+DROP TABLE IF EXISTS `mt_printer_cate`;
+CREATE TABLE `mt_printer_cate` (
+    `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+    `MERCHANT_ID` int(11) NOT NULL DEFAULT 0 COMMENT '所属商户ID',
+    `STORE_ID` int(11) NOT NULL DEFAULT 0 COMMENT '所属店铺ID',
+    `PRINTER_ID` int(11) NOT NULL COMMENT '打印机ID,关联mt_printer',
+    `CATE_ID` int(11) NOT NULL COMMENT '商品分类ID,关联mt_goods_cate',
+    `STATUS` char(1) DEFAULT 'A' COMMENT '状态:A=正常 D=删除',
+    `OPERATOR` varchar(64) DEFAULT '' COMMENT '最后操作人',
+    `CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
+    `UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',
+    PRIMARY KEY (`ID`),
+    UNIQUE KEY `uk_printer_cate` (`PRINTER_ID`, `CATE_ID`),
+    KEY `idx_printer_cate_printer_id` (`PRINTER_ID`),
+    KEY `idx_printer_cate_cate_id` (`CATE_ID`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打印机与商品分类映射表';
+
+-- 2. mt_printer 新增是否默认打印机字段
+ALTER TABLE `mt_printer`
+    ADD COLUMN `IS_DEFAULT` char(1) DEFAULT 'N' COMMENT '是否默认打印机:Y=是 N=否,默认打印机负责打印未分配商品';

+ 3 - 0
fuint-application/src/main/java/com/fuint/common/dto/order/OrderGoodsDto.java

@@ -22,6 +22,9 @@ public class OrderGoodsDto implements Serializable {
     @ApiModelProperty("商品ID")
     private Integer goodsId;
 
+    @ApiModelProperty("商品分类ID")
+    private Integer cateId;
+
     @ApiModelProperty("订单类型")
     private String type;
 

+ 7 - 0
fuint-application/src/main/java/com/fuint/common/param/PrinterParam.java

@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import java.io.Serializable;
 import java.util.Date;
+import java.util.List;
 
 /**
  * 打印机请求参数
@@ -56,4 +57,10 @@ public class PrinterParam implements Serializable {
     @ApiModelProperty("状态,A正常;D作废")
     private String status;
 
+    @ApiModelProperty("是否默认打印机,Y:是;N:否。默认打印机负责打印未被任何映射认领的商品")
+    private String isDefault;
+
+    @ApiModelProperty("关联的商品分类ID集合")
+    private List<Integer> cateIds;
+
 }

+ 9 - 1
fuint-application/src/main/java/com/fuint/common/service/OrderService.java

@@ -41,7 +41,15 @@ public interface OrderService extends IService<MtOrder> {
      * @throws BusinessCheckException
      * @return
      */
-    MtOrder saveOrder(OrderDto orderDto) throws BusinessCheckException;
+    /**
+     * 保存订单
+     *
+     * @param orderDto 订单信息
+     * @param newOrderGoodsIds 本次新增的订单商品行ID集合(用于加单时只打印本次新增的商品,可为null)
+     * @return
+     * @throws BusinessCheckException
+     */
+    MtOrder saveOrder(OrderDto orderDto, List<Integer> newOrderGoodsIds) throws BusinessCheckException;
 
     /**
      * 订单提交结算

+ 39 - 3
fuint-application/src/main/java/com/fuint/common/service/PrinterService.java

@@ -42,12 +42,22 @@ public interface PrinterService extends IService<MtPrinter> {
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @throws Exception
      * @return
      * */
     Boolean printOrder(UserOrderDto orderInfo, boolean autoPrint, boolean beforePay, boolean afterPay, List<Integer> goodsIds) throws Exception;
 
+    /**
+     * 向指定打印机打印整个订单的明细(不按商品分类分单,且不播报来单语音)
+     *
+     * @param orderInfo 订单信息
+     * @param mtPrinter 指定的打印机
+     * @throws Exception
+     * @return
+     */
+    Boolean printOrderToPrinter(UserOrderDto orderInfo, MtPrinter mtPrinter) throws Exception;
+
     /**
      * 打印标签
      *
@@ -55,7 +65,7 @@ public interface PrinterService extends IService<MtPrinter> {
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @throws Exception
      * @return
      * */
@@ -103,9 +113,35 @@ public interface PrinterService extends IService<MtPrinter> {
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @return 包含打印订单和标签结果的Map,key为"order"和"label"
      * @throws Exception
      */
     Boolean printOrderAndLabel(UserOrderDto orderInfo, boolean autoPrint, boolean beforePay, boolean afterPay, List<Integer> goodsIds) throws Exception;
+
+    /**
+     * 判断该订单是否已配置支付前(下单即打)的订单打印机
+     *
+     * @param orderInfo 订单信息
+     * @return true=下单时已打印过;false=下单时不会打印
+     */
+    boolean hasBeforePayPrinter(UserOrderDto orderInfo);
+
+    /**
+     * 查询打印机已配置的商品分类ID
+     *
+     * @param printerId 打印机ID
+     * @return 商品分类ID集合
+     */
+    List<Integer> queryCateIdsByPrinterId(Integer printerId);
+
+    /**
+     * 保存打印机与商品分类的映射(先清后插)
+     *
+     * @param mtPrinter 打印机信息
+     * @param cateIds 商品分类ID集合
+     * @param accountInfo 操作人
+     * @throws BusinessCheckException
+     */
+    void savePrinterCate(MtPrinter mtPrinter, List<Integer> cateIds, AccountInfo accountInfo) throws BusinessCheckException;
 }

+ 4 - 5
fuint-application/src/main/java/com/fuint/common/service/WeixinService.java

@@ -81,14 +81,13 @@ public interface WeixinService {
     JSONObject getWxOpenId(Integer merchantId, String code);
 
     /**
-     * 获取会员微信绑定的手机号
+     * 获取会员微信绑定的手机号(手机号快速验证组件)
      *
-     * @param encryptedData 加密的编码(前端返回)
-     * @param sessionKey
-     * @param iv
+     * @param merchantId 商户ID
+     * @param code 前端 getPhoneNumber 回调返回的动态令牌code,5分钟内有效且只能使用一次
      * @return
      * */
-    String getPhoneNumber(String encryptedData, String sessionKey, String iv);
+    String getPhoneNumberByCode(Integer merchantId, String code);
 
     /**
      * 发送订阅消息

+ 1 - 1
fuint-application/src/main/java/com/fuint/common/service/impl/BalanceServiceImpl.java

@@ -212,7 +212,7 @@ public class BalanceServiceImpl extends ServiceImpl<MtBalanceMapper, MtBalance>
             orderDto.setPayStatus(PayStatusEnum.WAIT.getKey());
             orderDto.setOperator(mtBalance.getOperator());
             orderDto.setUsePoint(0);
-            MtOrder mtOrder = orderService.saveOrder(orderDto);
+            MtOrder mtOrder = orderService.saveOrder(orderDto, null);
             if (mtOrder != null) {
                 orderService.setOrderPayed(mtOrder.getId(), null);
             }

+ 12 - 0
fuint-application/src/main/java/com/fuint/common/service/impl/CateServiceImpl.java

@@ -14,8 +14,10 @@ import com.fuint.framework.exception.BusinessCheckException;
 import com.fuint.framework.pagination.PaginationResponse;
 import com.fuint.repository.mapper.MtGoodsCateMapper;
 import com.fuint.repository.mapper.MtGoodsMapper;
+import com.fuint.repository.mapper.MtPrinterCateMapper;
 import com.fuint.repository.model.MtGoods;
 import com.fuint.repository.model.MtGoodsCate;
+import com.fuint.repository.model.MtPrinterCate;
 import com.fuint.repository.model.MtStore;
 import com.fuint.utils.StringUtil;
 import com.github.pagehelper.Page;
@@ -49,6 +51,11 @@ public class CateServiceImpl extends ServiceImpl<MtGoodsCateMapper, MtGoodsCate>
 
     private MtGoodsCateMapper cateMapper;
 
+    /**
+     * 打印机与商品分类映射 Mapper
+     * */
+    private MtPrinterCateMapper mtPrinterCateMapper;
+
     /**
      * 店铺服务接口
      * */
@@ -184,6 +191,11 @@ public class CateServiceImpl extends ServiceImpl<MtGoodsCateMapper, MtGoodsCate>
         cateInfo.setUpdateTime(new Date());
 
         this.updateById(cateInfo);
+
+        // 清理该分类与打印机的映射关系
+        LambdaQueryWrapper<MtPrinterCate> deleteWrapper = Wrappers.lambdaQuery();
+        deleteWrapper.eq(MtPrinterCate::getCateId, id);
+        mtPrinterCateMapper.delete(deleteWrapper);
     }
 
     /**

+ 13 - 12
fuint-application/src/main/java/com/fuint/common/service/impl/GoodsServiceImpl.java

@@ -40,6 +40,7 @@ import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -638,20 +639,20 @@ public class GoodsServiceImpl extends ServiceImpl<MtGoodsMapper, MtGoods> implem
         }
         Integer merchantId = mtStore.getMerchantId() == null ? 0 : mtStore.getMerchantId();
         Page<MtGoods> pageHelper = PageHelper.startPage(page, pageSize);
-        List<MtGoods> goodsList = new ArrayList<>();
+        List<MtGoods> goodsList;
         List<MtGoodsSku> skuList = new ArrayList<>();
-        if (StringUtil.isNotEmpty(keyword)) {
-            skuList = mtGoodsSkuMapper.getBySkuNo(keyword);
-        }
-        if (skuList != null && skuList.size() > 0) {
-            MtGoods goods = mtGoodsMapper.selectById(skuList.get(0).getGoodsId());
-            goodsList.add(goods);
+        if (keyword != null && StringUtil.isNotEmpty(keyword)) {
+            goodsList = mtGoodsMapper.searchStoreGoodsList(merchantId, storeId, keyword, platform);
         } else {
-            pageHelper = PageHelper.startPage(page, pageSize);
-            if (keyword != null && StringUtil.isNotEmpty(keyword)) {
-                goodsList = mtGoodsMapper.searchStoreGoodsList(merchantId, storeId, keyword, platform);
-            } else {
-                goodsList = mtGoodsMapper.getStoreGoodsList(merchantId, storeId, cateId, platform);
+            goodsList = mtGoodsMapper.getStoreGoodsList(merchantId, storeId, cateId, platform);
+        }
+        if(CollectionUtils.isEmpty(goodsList)){
+            if (StringUtil.isNotEmpty(keyword)) {
+                skuList = mtGoodsSkuMapper.getBySkuNo(keyword);
+            }
+            if (skuList != null && skuList.size() > 0) {
+                MtGoods goods = mtGoodsMapper.selectById(skuList.get(0).getGoodsId());
+                goodsList.add(goods);
             }
         }
         List<MtGoods> dataList = new ArrayList<>();

+ 15 - 17
fuint-application/src/main/java/com/fuint/common/service/impl/OrderServiceImpl.java

@@ -377,7 +377,7 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
     @Override
     @Transactional(rollbackFor = Exception.class)
     @OperationServiceLog(description = "提交订单信息")
-    public MtOrder saveOrder(OrderDto orderDto) throws BusinessCheckException {
+    public MtOrder saveOrder(OrderDto orderDto, List<Integer> newOrderGoodsIds) throws BusinessCheckException {
         logger.info("orderService.saveOrder orderDto = {}", JsonUtil.toJSONString(orderDto));
         MtOrder mtOrder = new MtOrder();
         if (null != orderDto.getId() && orderDto.getId() > 0) {
@@ -719,6 +719,10 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
                  orderGoods.setCreateTime(new Date());
                  orderGoods.setUpdateTime(new Date());
                  mtOrderGoodsMapper.insert(orderGoods);
+                 // 记录本次新增的订单商品行ID(加单打印时只打印本次新增的商品)
+                 if (newOrderGoodsIds != null) {
+                     newOrderGoodsIds.add(orderGoods.getId());
+                 }
                  // 扣减库存
                  MtGoods goodsInfo = mtGoodsMapper.selectById(cart.getGoodsId());
                  if (goodsInfo.getIsSingleSpec().equals(YesOrNoEnum.YES.getKey())) {
@@ -1095,18 +1099,8 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
         }
 
         // 商品订单
-        List<Integer> goodsIds = new ArrayList<>();
         if (orderDto.getType().equals(OrderTypeEnum.GOODS.getKey())) {
             orderDto.setCouponId(couponId);
-            Map<String, Object> cartParams = new HashMap<>();
-            cartParams.put("status", StatusEnum.ENABLED.getKey());
-            cartParams.put("ids", orderDto.getCartIds());
-            List<MtCart> cartList = cartService.queryCartListByParams(cartParams);
-            if (cartList != null && cartList.size() > 0) {
-                for (MtCart mtCart : cartList) {
-                     goodsIds.add(mtCart.getGoodsId());
-                }
-            }
         }
 
         // 商品订单且配送要加上配送费用
@@ -1144,10 +1138,11 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
             }
         }
 
-        // 首先生成订单,拿到订单ID
+        // 首先生成订单,拿到订单ID;同时收集本次新增的订单商品行ID(加单打印时只打印本次新增的商品)
         MtOrder orderInfo;
+        List<Integer> newOrderGoodsIds = new ArrayList<>();
         try {
-            orderInfo = saveOrder(orderDto);
+            orderInfo = saveOrder(orderDto, newOrderGoodsIds);
         } catch (BusinessCheckException e) {
             throw new BusinessCheckException(e.getMessage() == null ?  "生成订单失败" : e.getMessage());
         }
@@ -1371,7 +1366,7 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
         // 打印订单
         try {
             UserOrderDto userOrderDto = getOrderByOrderSn(orderInfo.getOrderSn());
-            printerService.printOrderAndLabel(userOrderDto, true, true, false, goodsIds);
+            printerService.printOrderAndLabel(userOrderDto, true, true, false, newOrderGoodsIds);
         } catch (Exception e) {
             logger.error("订单打印出错了:" + e.getMessage());
         }
@@ -1882,8 +1877,10 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
         }
 
         try {
-            // 打印订单
-            printerService.printOrderAndLabel(orderInfo, true,false, true, null);
+            // 打印订单(若下单时已打印过,支付成功后不再重复打印)
+            if (!printerService.hasBeforePayPrinter(orderInfo)) {
+                printerService.printOrderAndLabel(orderInfo, true,false, true, null);
+            }
 
             // 给商家发送通知短信
             MtStore mtStore = storeService.queryStoreById(mtOrder.getStoreId());
@@ -2069,6 +2066,7 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
                     orderGoodsDto.setPrice(orderGoods.getPrice().toString());
                     orderGoodsDto.setDiscount(orderGoods.getDiscount().toString());
                     orderGoodsDto.setGoodsId(orderGoods.getGoodsId());
+                    orderGoodsDto.setCateId(goodsInfo.getCateId());
                     if (orderGoods.getSkuId() > 0) {
                         List<GoodsSpecValueDto> specList = goodsService.getSpecListBySkuId(orderGoods.getSkuId());
                         orderGoodsDto.setSpecList(specList);
@@ -2756,7 +2754,7 @@ public class OrderServiceImpl extends ServiceImpl<MtOrderMapper, MtOrder> implem
         orderDto.setPlatform(platform);
         orderDto.setMerchantId(merchantId);
 
-        return saveOrder(orderDto);
+        return saveOrder(orderDto, null);
     }
 
     /**

+ 402 - 88
fuint-application/src/main/java/com/fuint/common/service/impl/PrinterServiceImpl.java

@@ -19,8 +19,10 @@ import com.fuint.common.vo.printer.*;
 import com.fuint.framework.annoation.OperationServiceLog;
 import com.fuint.framework.exception.BusinessCheckException;
 import com.fuint.framework.pagination.PaginationResponse;
+import com.fuint.repository.mapper.MtPrinterCateMapper;
 import com.fuint.repository.mapper.MtPrinterMapper;
 import com.fuint.repository.model.MtPrinter;
+import com.fuint.repository.model.MtPrinterCate;
 import com.fuint.repository.model.MtSetting;
 import com.fuint.repository.model.MtStore;
 import com.fuint.utils.StringUtil;
@@ -38,6 +40,8 @@ import org.springframework.data.domain.PageRequest;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.*;
 import java.util.concurrent.*;
 
@@ -55,6 +59,11 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
 
     private MtPrinterMapper mtPrinterMapper;
 
+    /**
+     * 打印机与商品分类映射 Mapper
+     * */
+    private MtPrinterCateMapper mtPrinterCateMapper;
+
     /**
      * 系统配置服务接口
      * */
@@ -164,7 +173,7 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @throws Exception
      * @return
      * */
@@ -204,109 +213,181 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
             throw new BusinessCheckException("打印失败:该店铺还没有添加云打印机!");
         }
 
-        MtStore storeInfo = orderInfo.getStoreInfo();
+        // 按打印机与商品分类的映射关系,计算每台打印机需要打印的订单商品行
+        Map<Integer, List<Integer>> printerGoodsMap = resolvePrintGoodsIds(printers, orderInfo, goodsIds);
+
         for (MtPrinter mtPrinter : printers) {
+            List<Integer> printGoodsIds = printerGoodsMap.get(mtPrinter.getId());
+            if (printGoodsIds == null || printGoodsIds.size() < 1) {
+                logger.info("云打印订单:打印机{}(ID:{})本次没有需要打印的商品,跳过", mtPrinter.getName(), mtPrinter.getId());
+                continue;
+            }
             printRequest.setSn(mtPrinter.getSn());
-
-            StringBuilder printContent = new StringBuilder();
-            printContent.append("<C>").append("<B>" + storeInfo.getName() + "</B>").append("<BR></C>");
-            printContent.append("<BR>");
-
-            // 分割线
-            printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
-
-            // 订单ID、单号、桌码
-            printContent.append("<L>订单ID:").append(orderInfo.getId()).append("</L>");
-            printContent.append("<BR>");
-            printContent.append("<L>订单号:").append(orderInfo.getOrderSn()).append("</L>");
-            if (orderInfo.getTableInfo() != null && StringUtil.isNotEmpty(orderInfo.getTableInfo().getCode())) {
-                printContent.append("<BR>");
-                printContent.append("<R><BOLD>桌码:").append(orderInfo.getTableInfo().getCode()).append("</BOLD></R>");
+            printRequest.setContent(buildOrderPrintContent(orderInfo, printGoodsIds));
+            printRequest.setCopies(1);
+            printRequest.setVoice(2);
+            printRequest.setMode(0);
+            ObjectRestResponse<String> result = PrinterUtil.print(printRequest);
+            if (result != null && result.getCode() != 0) {
+                logger.error("云打印订单失败:" + result.getMsg());
+                throw new BusinessCheckException("打印失败:" + result.getMsg());
             }
+        }
+        return true;
+    }
 
-            // 订单状态
-            String payStatus = PayStatusEnum.WAIT.getValue();
-            if (orderInfo.getPayStatus().equals(PayStatusEnum.SUCCESS.getKey())) {
-                payStatus = PayStatusEnum.SUCCESS.getValue();
-            }
+    /**
+     * 构建订单小票的打印内容
+     *
+     * @param orderInfo 订单信息
+     * @param printGoodsIds 需要打印的订单商品行ID,为空表示打印订单全部商品
+     * @return 打印内容
+     */
+    private String buildOrderPrintContent(UserOrderDto orderInfo, List<Integer> printGoodsIds) {
+        StringBuilder printContent = new StringBuilder();
+        printContent.append("<C>").append("<B>" + orderInfo.getStoreInfo().getName() + "</B>").append("<BR></C>");
+        printContent.append("<BR>");
+
+        // 分割线
+        printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
+
+        // 订单ID、单号、桌码
+        printContent.append("<L>订单ID:").append(orderInfo.getId()).append("</L>");
+        printContent.append("<BR>");
+        printContent.append("<L>订单号:").append(orderInfo.getOrderSn()).append("</L>");
+        if (orderInfo.getTableInfo() != null && StringUtil.isNotEmpty(orderInfo.getTableInfo().getCode())) {
             printContent.append("<BR>");
-            printContent.append("<L>").append("支付状态:").append(payStatus).append("<BR></L>");
+            printContent.append("<R><BOLD>桌码:").append(orderInfo.getTableInfo().getCode()).append("</BOLD></R>");
+        }
 
-            // 分割线
-            printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
+        // 订单状态
+        String payStatus = PayStatusEnum.WAIT.getValue();
+        if (orderInfo.getPayStatus().equals(PayStatusEnum.SUCCESS.getKey())) {
+            payStatus = PayStatusEnum.SUCCESS.getValue();
+        }
+        printContent.append("<BR>");
+        printContent.append("<L>").append("支付状态:").append(payStatus).append("<BR></L>");
 
-            printContent.append("品名").append(StringUtils.repeat(" ", 16))
-                        .append("数量").append(StringUtils.repeat(" ", 2))
-                        .append("单价").append(StringUtils.repeat(" ", 2))
-                        .append("<BR>");
+        // 分割线
+        printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
 
-            // 分割线
-            printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
+        printContent.append("品名").append(StringUtils.repeat(" ", 16))
+                    .append("数量").append(StringUtils.repeat(" ", 2))
+                    .append("单价").append(StringUtils.repeat(" ", 2))
+                    .append("<BR>");
 
-            // 商品列表
-            if (orderInfo.getGoods() != null && orderInfo.getGoods().size() > 0) {
-                for (OrderGoodsDto goodsDto : orderInfo.getGoods()) {
-                     List<GoodsSpecValueDto> specList = goodsDto.getSpecList();
-                     String name = goodsDto.getName();
-                     List<String> specValue = new ArrayList<>();
-                     if (specList != null && specList.size() > 0) {
-                        for (GoodsSpecValueDto spec : specList) {
-                            if (StringUtil.isNotEmpty(spec.getSpecValue())) {
-                                specValue.add(spec.getSpecValue());
-                            }
-                        }
-                        if (specValue.size() > 0) {
-                            name = name + "(" + String.join(",", specValue) + ")";
+        // 分割线
+        printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
+
+        // 商品列表(只打印指定的订单商品行)
+        BigDecimal printGoodsAmount = BigDecimal.ZERO;
+        int printGoodsCount = 0;
+        if (orderInfo.getGoods() != null && orderInfo.getGoods().size() > 0) {
+            for (OrderGoodsDto goodsDto : orderInfo.getGoods()) {
+                 if (printGoodsIds != null && printGoodsIds.size() > 0 && !printGoodsIds.contains(goodsDto.getId())) {
+                     continue;
+                 }
+                 List<GoodsSpecValueDto> specList = goodsDto.getSpecList();
+                 String name = goodsDto.getName();
+                 List<String> specValue = new ArrayList<>();
+                 if (specList != null && specList.size() > 0) {
+                    for (GoodsSpecValueDto spec : specList) {
+                        if (StringUtil.isNotEmpty(spec.getSpecValue())) {
+                            specValue.add(spec.getSpecValue());
                         }
-                     }
-                     if (goodsIds != null && goodsIds.size() > 0) {
-                         if (goodsIds.contains(goodsDto.getGoodsId())) {
-                             printContent.append(NoteFormatter.formatPrintOrderItemForNewLine80(name, goodsDto.getNum(), Double.parseDouble(goodsDto.getPrice())));
-                         }
-                     } else {
-                         printContent.append(NoteFormatter.formatPrintOrderItemForNewLine80(name, goodsDto.getNum(), Double.parseDouble(goodsDto.getPrice())));
-                     }
-                }
+                    }
+                    if (specValue.size() > 0) {
+                        name = name + "(" + String.join(",", specValue) + ")";
+                    }
+                 }
+                 printContent.append(NoteFormatter.formatPrintOrderItemForNewLine80(name, goodsDto.getNum(), Double.parseDouble(goodsDto.getPrice())));
+                 printGoodsAmount = printGoodsAmount.add(new BigDecimal(goodsDto.getPrice()).multiply(BigDecimal.valueOf(goodsDto.getNum())));
+                 printGoodsCount++;
             }
+        }
 
-            // 分割线
-            printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
+        // 分割线
+        printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
 
+        // 合计:只打印部分商品时,合计只计算本次打印的商品金额,避免与商品清单对不上
+        int totalGoodsCount = orderInfo.getGoods() == null ? 0 : orderInfo.getGoods().size();
+        if (printGoodsCount < totalGoodsCount) {
+            printContent.append("<R>").append("本单合计:").append(printGoodsAmount.setScale(2, RoundingMode.HALF_UP)).append("元").append("<BR></R>");
+        } else {
             printContent.append("<R>").append("合计:").append(orderInfo.getPayAmount()).append("元").append("<BR></R>");
+        }
 
-            // 配送订单,打印配送信息
-            if (orderInfo.getOrderMode().equals(OrderModeEnum.EXPRESS.getKey())) {
-                printContent.append("<BR>");
-                printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
-                printContent.append("<L>")
-                        .append("配送姓名:").append(orderInfo.getAddress().getName()).append("<BR>")
-                        .append("联系电话:").append(orderInfo.getAddress().getMobile()).append("<BR>")
-                        .append("详细地址:").append(orderInfo.getAddress().getProvinceName() + orderInfo.getAddress().getCityName() + orderInfo.getAddress().getRegionName() + orderInfo.getAddress().getDetail()).append("<BR>");
-            }
-
+        // 配送订单,打印配送信息
+        if (orderInfo.getOrderMode().equals(OrderModeEnum.EXPRESS.getKey())) {
             printContent.append("<BR>");
+            printContent.append(StringUtils.repeat("-", 32)).append("<BR>");
             printContent.append("<L>")
-                    .append("店铺地址:").append((orderInfo.getStoreInfo().getAddress() == null) ? "无" : orderInfo.getStoreInfo().getAddress()).append("<BR>")
-                    .append("联系电话:").append((orderInfo.getStoreInfo().getPhone() == null) ? "无" : orderInfo.getStoreInfo().getPhone()).append("<BR>")
-                    .append("下单时间:").append(orderInfo.getCreateTime()).append("<BR>")
-                    .append("订单备注:").append(StringUtil.isEmpty(orderInfo.getRemark()) ? "无" : orderInfo.getRemark()).append("<BR>");
-
-            // 网站二维码
-            String webSite = env.getProperty("website.url");
-            if (StringUtil.isNotEmpty(webSite)) {
-                printContent.append("<C>").append("<QR>" + webSite + "</QR>").append("</C>");
-            }
+                    .append("配送姓名:").append(orderInfo.getAddress().getName()).append("<BR>")
+                    .append("联系电话:").append(orderInfo.getAddress().getMobile()).append("<BR>")
+                    .append("详细地址:").append(orderInfo.getAddress().getProvinceName() + orderInfo.getAddress().getCityName() + orderInfo.getAddress().getRegionName() + orderInfo.getAddress().getDetail()).append("<BR>");
+        }
 
-            printRequest.setContent(printContent.toString());
-            printRequest.setCopies(1);
-            printRequest.setVoice(2);
-            printRequest.setMode(0);
-            ObjectRestResponse<String> result = PrinterUtil.print(printRequest);
-            if (result != null && result.getCode() != 0) {
-                logger.error("云打印订单失败:" + result.getMsg());
-                throw new BusinessCheckException("打印失败:" + result.getMsg());
-            }
+        printContent.append("<BR>");
+        printContent.append("<L>")
+                .append("店铺地址:").append((orderInfo.getStoreInfo().getAddress() == null) ? "无" : orderInfo.getStoreInfo().getAddress()).append("<BR>")
+                .append("联系电话:").append((orderInfo.getStoreInfo().getPhone() == null) ? "无" : orderInfo.getStoreInfo().getPhone()).append("<BR>")
+                .append("下单时间:").append(orderInfo.getCreateTime()).append("<BR>")
+                .append("订单备注:").append(StringUtil.isEmpty(orderInfo.getRemark()) ? "无" : orderInfo.getRemark()).append("<BR>");
+
+        // 网站二维码
+        String webSite = env.getProperty("website.url");
+        if (StringUtil.isNotEmpty(webSite)) {
+            printContent.append("<C>").append("<QR>" + webSite + "</QR>").append("</C>");
         }
+
+        return printContent.toString();
+    }
+
+    /**
+     * 向指定打印机打印整个订单的明细(不按商品分类分单,且不播报来单语音)
+     *
+     * @param orderInfo 订单信息
+     * @param mtPrinter 指定的打印机
+     * @throws Exception
+     * @return
+     */
+    @Override
+    public Boolean printOrderToPrinter(UserOrderDto orderInfo, MtPrinter mtPrinter) throws Exception {
+        if (orderInfo == null) {
+            throw new BusinessCheckException("打印失败:订单不存在!");
+        }
+        if (mtPrinter == null) {
+            throw new BusinessCheckException("打印失败:打印机不存在!");
+        }
+        if (!StatusEnum.ENABLED.getKey().equals(mtPrinter.getStatus())) {
+            throw new BusinessCheckException("打印失败:该打印机已停用!");
+        }
+        if (orderInfo.getStoreInfo() == null) {
+            throw new BusinessCheckException("打印失败:订单所属店铺信息为空!");
+        }
+        if (mtPrinter.getMerchantId() != null && mtPrinter.getMerchantId() > 0 && !mtPrinter.getMerchantId().equals(orderInfo.getMerchantId())) {
+            throw new BusinessCheckException("打印失败:打印机与订单不属于同一商户!");
+        }
+
+        logger.info("云打印订单到指定打印机,订单编号:{},打印机:{}(SN:{})", orderInfo.getOrderSn(), mtPrinter.getName(), mtPrinter.getSn());
+
+        PrintRequest printRequest = new PrintRequest();
+        createRequestHeader(orderInfo.getMerchantId(), printRequest);
+        printRequest.setSn(mtPrinter.getSn());
+        // 打印整个订单的明细,不按商品分类分单
+        printRequest.setContent(buildOrderPrintContent(orderInfo, null));
+        printRequest.setCopies(1);
+        // 静音模式,不播报“您有新的订单,请及时处理”(0取消订单模式 1静音模式 2来单播放模式)
+        printRequest.setVoice(1);
+        printRequest.setMode(0);
+
+        ObjectRestResponse<String> result = PrinterUtil.print(printRequest);
+        if (result == null || result.getCode() != 0) {
+            String errorMsg = result == null ? "未知错误" : result.getMsg();
+            logger.error("云打印订单失败:{}", errorMsg);
+            throw new BusinessCheckException("打印失败:" + errorMsg);
+        }
+
         return true;
     }
 
@@ -317,7 +398,7 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @throws Exception
      * @return
      * */
@@ -356,12 +437,23 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
             logger.error("云打印标签失败:该店铺还没有添加云打印机!");
             throw new BusinessCheckException("打印失败:该店铺还没有添加云打印机!");
         }
+        // 按打印机与商品分类的映射关系,计算每台打印机需要打印的订单商品行
+        Map<Integer, List<Integer>> printerGoodsMap = resolvePrintGoodsIds(printers, orderInfo, goodsIds);
+
         for (MtPrinter mtPrinter : printers) {
+             List<Integer> printGoodsIds = printerGoodsMap.get(mtPrinter.getId());
+             if (printGoodsIds == null || printGoodsIds.size() < 1) {
+                 logger.info("云打印标签:打印机{}(ID:{})本次没有需要打印的商品,跳过", mtPrinter.getName(), mtPrinter.getId());
+                 continue;
+             }
              printRequest.setSn(mtPrinter.getSn());
              StringBuilder printContent = new StringBuilder();
-             // 商品列表
+             // 商品列表(只打印该打印机负责的商品行)
              if (orderInfo.getGoods() != null && orderInfo.getGoods().size() > 0) {
                  for (OrderGoodsDto goodsDto : orderInfo.getGoods()) {
+                      if (!printGoodsIds.contains(goodsDto.getId())) {
+                          continue;
+                      }
                       String goodsName = goodsDto.getName();
                       String tableCode = (orderInfo.getTableInfo() != null ? orderInfo.getTableInfo().getCode() : ""); // 为空时直接设为空字符串
                       String remarkOrTime = (orderInfo.getRemark() != null ? orderInfo.getRemark() : orderInfo.getPayTime());
@@ -446,6 +538,11 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
             restRequest.setSnlist(snList);
             PrinterUtil.delPrinters(restRequest);
         }
+        // 删除该打印机与商品分类的映射关系
+        LambdaQueryWrapper<MtPrinterCate> deleteWrapper = Wrappers.lambdaQuery();
+        deleteWrapper.eq(MtPrinterCate::getPrinterId, mtPrinter.getId());
+        mtPrinterCateMapper.delete(deleteWrapper);
+
         mtPrinter.setStatus(StatusEnum.DISABLE.getKey());
         mtPrinter.setUpdateTime(new Date());
         mtPrinterMapper.updateById(mtPrinter);
@@ -508,6 +605,7 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
         String beforePay = params.get("beforePay") == null ? "" : params.get("beforePay").toString();
         String afterPay = params.get("afterPay") == null ? "" : params.get("afterPay").toString();
         String type = params.get("type") == null ? "" : params.get("type").toString();
+        String isDefault = params.get("isDefault") == null ? "" : params.get("isDefault").toString();
 
         LambdaQueryWrapper<MtPrinter> lambdaQueryWrapper = Wrappers.lambdaQuery();
         if (StringUtils.isNotBlank(status)) {
@@ -540,11 +638,227 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
         if (StringUtils.isNotBlank(type)) {
             lambdaQueryWrapper.eq(MtPrinter::getType, type);
         }
+        if (StringUtils.isNotBlank(isDefault)) {
+            lambdaQueryWrapper.eq(MtPrinter::getIsDefault, isDefault);
+        }
         lambdaQueryWrapper.orderByAsc(MtPrinter::getId);
 
         return mtPrinterMapper.selectList(lambdaQueryWrapper);
     }
 
+    /**
+     * 判断该订单是否已配置支付前(下单即打)的订单打印机
+     *
+     * @param orderInfo 订单信息
+     * @return
+     */
+    @Override
+    public boolean hasBeforePayPrinter(UserOrderDto orderInfo) {
+        if (orderInfo == null || orderInfo.getStoreInfo() == null) {
+            return false;
+        }
+        Map<String, Object> params = new HashMap<>();
+        params.put("storeId", orderInfo.getStoreInfo().getId());
+        params.put("status", StatusEnum.ENABLED.getKey());
+        params.put("type", PrinterTypeEnum.ORDER.getKey());
+        params.put("autoPrint", YesOrNoEnum.YES.getKey());
+        params.put("beforePay", YesOrNoEnum.YES.getKey());
+        List<MtPrinter> printers = queryPrinterListByParams(params);
+        return printers != null && printers.size() > 0;
+    }
+
+    /**
+     * 查询打印机已配置的商品分类ID
+     *
+     * @param printerId 打印机ID
+     * @return
+     */
+    @Override
+    public List<Integer> queryCateIdsByPrinterId(Integer printerId) {
+        List<Integer> cateIds = new ArrayList<>();
+        if (printerId == null || printerId < 1) {
+            return cateIds;
+        }
+        LambdaQueryWrapper<MtPrinterCate> lambdaQueryWrapper = Wrappers.lambdaQuery();
+        lambdaQueryWrapper.eq(MtPrinterCate::getPrinterId, printerId);
+        lambdaQueryWrapper.eq(MtPrinterCate::getStatus, StatusEnum.ENABLED.getKey());
+        List<MtPrinterCate> list = mtPrinterCateMapper.selectList(lambdaQueryWrapper);
+        if (list != null && list.size() > 0) {
+            for (MtPrinterCate mtPrinterCate : list) {
+                cateIds.add(mtPrinterCate.getCateId());
+            }
+        }
+        return cateIds;
+    }
+
+    /**
+     * 保存打印机与商品分类的映射(先清后插)
+     *
+     * @param mtPrinter 打印机信息
+     * @param cateIds 商品分类ID集合
+     * @param accountInfo 操作人
+     * @throws BusinessCheckException
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    @OperationServiceLog(description = "保存打印机与商品分类映射")
+    public void savePrinterCate(MtPrinter mtPrinter, List<Integer> cateIds, AccountInfo accountInfo) throws BusinessCheckException {
+        if (mtPrinter == null || mtPrinter.getId() == null || mtPrinter.getId() < 1) {
+            return;
+        }
+        // 同一个店铺下最多只能有一台默认打印机
+        if (YesOrNoEnum.YES.getKey().equals(mtPrinter.getIsDefault())) {
+            Map<String, Object> params = new HashMap<>();
+            params.put("status", StatusEnum.ENABLED.getKey());
+            params.put("storeId", mtPrinter.getStoreId());
+            params.put("isDefault", YesOrNoEnum.YES.getKey());
+            List<MtPrinter> printerList = queryPrinterListByParams(params);
+            if (printerList != null && printerList.size() > 0) {
+                for (MtPrinter printer : printerList) {
+                    if (!printer.getId().equals(mtPrinter.getId())) {
+                        throw new BusinessCheckException("该店铺已存在默认打印机:" + printer.getName() + ",请先取消其默认设置");
+                    }
+                }
+            }
+        }
+
+        // 先删除该打印机原有的映射关系
+        LambdaQueryWrapper<MtPrinterCate> deleteWrapper = Wrappers.lambdaQuery();
+        deleteWrapper.eq(MtPrinterCate::getPrinterId, mtPrinter.getId());
+        mtPrinterCateMapper.delete(deleteWrapper);
+
+        if (cateIds == null || cateIds.size() < 1) {
+            return;
+        }
+
+        Date now = new Date();
+        for (Integer cateId : cateIds) {
+            if (cateId == null || cateId < 1) {
+                continue;
+            }
+            MtPrinterCate mtPrinterCate = new MtPrinterCate();
+            mtPrinterCate.setMerchantId(mtPrinter.getMerchantId());
+            mtPrinterCate.setStoreId(mtPrinter.getStoreId());
+            mtPrinterCate.setPrinterId(mtPrinter.getId());
+            mtPrinterCate.setCateId(cateId);
+            mtPrinterCate.setStatus(StatusEnum.ENABLED.getKey());
+            mtPrinterCate.setOperator(accountInfo == null ? "" : accountInfo.getAccountName());
+            mtPrinterCate.setCreateTime(now);
+            mtPrinterCate.setUpdateTime(now);
+            mtPrinterCateMapper.insert(mtPrinterCate);
+        }
+    }
+
+    /**
+     * 计算每台打印机需要打印的订单商品行ID
+     * 规则:
+     * 1、只有一台打印机时,该打印机打印全部商品,不区分映射
+     * 2、多台打印机但都没有配置映射时,每台都打印全部商品(兼容未配置映射的场景)
+     * 3、多台打印机且至少一台配置了映射时,按映射分单:
+     *    配置了映射的打印机只打印其映射品类内的商品;
+     *    未配置映射的打印机不打印;
+     *    默认打印机额外兜底打印未被任何映射认领的商品(无分类或分类未被认领)
+     *
+     * @param printers 打印机列表
+     * @param orderInfo 订单信息
+     * @param goodsIds 本次新增的订单商品行ID,为空表示订单全部商品
+     * @return key=打印机ID,value=该打印机需要打印的订单商品行ID集合
+     */
+    private Map<Integer, List<Integer>> resolvePrintGoodsIds(List<MtPrinter> printers, UserOrderDto orderInfo, List<Integer> goodsIds) {
+        Map<Integer, List<Integer>> result = new HashMap<>();
+        if (printers == null || printers.size() < 1 || orderInfo == null || orderInfo.getGoods() == null || orderInfo.getGoods().size() < 1) {
+            return result;
+        }
+
+        // 本次需要打印的商品行:加单时只打印本次新增的商品行
+        List<OrderGoodsDto> printGoods = new ArrayList<>();
+        for (OrderGoodsDto goodsDto : orderInfo.getGoods()) {
+            if (goodsIds == null || goodsIds.size() < 1 || goodsIds.contains(goodsDto.getId())) {
+                printGoods.add(goodsDto);
+            }
+        }
+        if (printGoods.size() < 1) {
+            return result;
+        }
+
+        // 各打印机配置的商品分类
+        Map<Integer, List<Integer>> printerCateMap = new HashMap<>();
+        boolean hasMapping = false;
+        for (MtPrinter printer : printers) {
+            List<Integer> cateIds = queryCateIdsByPrinterId(printer.getId());
+            printerCateMap.put(printer.getId(), cateIds);
+            if (cateIds != null && cateIds.size() > 0) {
+                hasMapping = true;
+            }
+        }
+
+        // 只有一台打印机,或所有打印机都没有配置映射:每台都打印全部商品
+        if (printers.size() == 1 || !hasMapping) {
+            List<Integer> allGoodsIds = new ArrayList<>();
+            for (OrderGoodsDto goodsDto : printGoods) {
+                allGoodsIds.add(goodsDto.getId());
+            }
+            for (MtPrinter printer : printers) {
+                result.put(printer.getId(), allGoodsIds);
+            }
+            return result;
+        }
+
+        // 已被映射认领的商品分类
+        Set<Integer> mappedCateIds = new HashSet<>();
+        for (List<Integer> cateIds : printerCateMap.values()) {
+            if (cateIds != null && cateIds.size() > 0) {
+                mappedCateIds.addAll(cateIds);
+            }
+        }
+
+        // 默认打印机:未指定时,用ID最小的那台兜底,避免漏打
+        MtPrinter defaultPrinter = null;
+        for (MtPrinter printer : printers) {
+            if (YesOrNoEnum.YES.getKey().equals(printer.getIsDefault())) {
+                defaultPrinter = printer;
+                break;
+            }
+        }
+        if (defaultPrinter == null) {
+            defaultPrinter = printers.get(0);
+            logger.warn("打印分单:未指定默认打印机,回退使用打印机{}(ID:{})兜底打印未分配商品", defaultPrinter.getName(), defaultPrinter.getId());
+        }
+
+        // 未被任何映射认领的商品行
+        List<Integer> unassignedGoodsIds = new ArrayList<>();
+        for (OrderGoodsDto goodsDto : printGoods) {
+            if (goodsDto.getCateId() == null || goodsDto.getCateId() < 1 || !mappedCateIds.contains(goodsDto.getCateId())) {
+                unassignedGoodsIds.add(goodsDto.getId());
+            }
+        }
+
+        for (MtPrinter printer : printers) {
+            List<Integer> cateIds = printerCateMap.get(printer.getId());
+            List<Integer> myGoodsIds = new ArrayList<>();
+            if (cateIds != null && cateIds.size() > 0) {
+                for (OrderGoodsDto goodsDto : printGoods) {
+                    if (goodsDto.getCateId() != null && cateIds.contains(goodsDto.getCateId())) {
+                        myGoodsIds.add(goodsDto.getId());
+                    }
+                }
+            }
+            // 默认打印机兜底打印未分配的商品
+            if (printer.getId().equals(defaultPrinter.getId()) && unassignedGoodsIds.size() > 0) {
+                for (Integer goodsId : unassignedGoodsIds) {
+                    if (!myGoodsIds.contains(goodsId)) {
+                        myGoodsIds.add(goodsId);
+                    }
+                }
+            }
+            if (myGoodsIds.size() > 0) {
+                result.put(printer.getId(), myGoodsIds);
+            }
+        }
+
+        return result;
+    }
+
     /**
      * 同时打印订单和标签(双线程并行执行)
      *
@@ -552,7 +866,7 @@ public class PrinterServiceImpl extends ServiceImpl<MtPrinterMapper, MtPrinter>
      * @param autoPrint 自动打印
      * @param beforePay 支付前打印
      * @param afterPay 支付后打印
-     * @param goodsIds 打印的商品Id
+     * @param goodsIds 本次新增的订单商品行ID集合,为空时打印订单全部商品
      * @return 包含打印订单和标签结果的Map,key为"order"和"label"
      * @throws Exception
      */

+ 95 - 54
fuint-application/src/main/java/com/fuint/common/service/impl/WeixinServiceImpl.java

@@ -39,7 +39,6 @@ import com.ijpay.wxpay.model.OrderQueryModel;
 import com.ijpay.wxpay.model.RefundModel;
 import com.ijpay.wxpay.model.UnifiedOrderModel;
 import lombok.AllArgsConstructor;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.Lazy;
@@ -48,15 +47,10 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import weixin.popular.util.JsonUtil;
 
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.math.BigDecimal;
-import java.security.AlgorithmParameters;
-import java.security.Security;
 import java.util.*;
 
 /**
@@ -117,6 +111,16 @@ public class WeixinServiceImpl implements WeixinService {
 
     private static final String FUINT_ACCESS_TOKEN_PRE = "FUINT_ACCESS_TOKEN";
 
+    /**
+     * 获取稳定版接口调用凭据(官方推荐,支持 force_refresh,可避免多实例并发刷新导致 access_token 互相失效)
+     * */
+    private static final String STABLE_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token";
+
+    /**
+     * 手机号快速验证组件:code 换取用户手机号
+     * */
+    private static final String GET_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
+
     /**
      * 获取微信accessToken
      *
@@ -127,7 +131,7 @@ public class WeixinServiceImpl implements WeixinService {
      * */
     @Override
     public String getAccessToken(Integer merchantId, boolean isMinApp, boolean useCache) {
-        String platForm = isMinApp == true ? "minApp" : "mp";
+        String platForm = isMinApp ? "minApp" : "mp";
         String wxAppId = env.getProperty("weixin.official.appId");
         String wxAppSecret = env.getProperty("weixin.official.appSecret");
         if (isMinApp) {
@@ -144,30 +148,37 @@ public class WeixinServiceImpl implements WeixinService {
             }
         }
 
-        String wxTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
-        String url = String.format(wxTokenUrl, wxAppId, wxAppSecret);
         String token = "";
 
         if (useCache) {
             token = RedisUtil.get(tokenKey);
         }
 
-        if (token == null || StringUtil.isEmpty(token)) {
+        if (StringUtil.isEmpty(token)) {
+            JSONObject reqData = new JSONObject();
+            reqData.put("grant_type", "client_credential");
+            reqData.put("appid", wxAppId);
+            reqData.put("secret", wxAppSecret);
+            // 固定使用「普通模式」(不传 force_refresh,等价于 false)。
+            // 官方限制:强制刷新模式每天仅 20 次且需间隔 30 秒,一旦打满将拿不到 access_token,
+            // 会连累订阅消息、卡券、小程序码、urllink、发货信息、jsapi_ticket 等全部调用方。
+            // 普通模式在 token 失效时平台会自动签发新的(并提前 5 分钟更新),无需强制刷新。
+            // useCache=false 仅表示跳过本地 Redis 缓存、直接向微信重新拉取。
             try {
-                String response = HttpRESTDataClient.requestGet(url);
-                JSONObject json = (JSONObject) JSONObject.parse(response);
-                if (!json.containsKey("errcode")) {
-                    RedisUtil.set(tokenKey, json.get("access_token"), 7200);
-                    token = (String) json.get("access_token");
+                String response = HttpRESTDataClient.requestPost(STABLE_TOKEN_URL, "application/json; charset=utf-8", reqData.toJSONString());
+                JSONObject json = JSONObject.parseObject(response);
+                if (json != null && StringUtil.isNotEmpty(json.getString("access_token"))) {
+                    token = json.getString("access_token");
+                    RedisUtil.set(tokenKey, token, 7200);
                 } else {
-                    logger.error("获取微信accessToken出错:" + json.get("errmsg"));
+                    logger.error("获取微信accessToken出错:errcode={}, errmsg={}", json == null ? "" : json.get("errcode"), json == null ? "" : json.get("errmsg"));
                 }
             } catch (Exception e) {
-                logger.error("获取微信accessToken异常:" + e.getMessage());
+                logger.error("获取微信accessToken异常:", e);
             }
         }
 
-        return token;
+        return token == null ? "" : token;
     }
 
     /**
@@ -375,7 +386,7 @@ public class WeixinServiceImpl implements WeixinService {
             if (!json.containsKey("errcode")) {
                 return json;
             } else {
-                logger.error("获取微信getWxProfile出错:code = " + json.containsKey("errcode") + ",msg="+ json.get("errmsg"));
+                logger.error("获取微信getWxProfile出错:errcode = " + json.get("errcode") + ", msg = " + json.get("errmsg"));
             }
         } catch (Exception e) {
             logger.error("获取微信getWxProfile异常:" + e.getMessage());
@@ -412,7 +423,7 @@ public class WeixinServiceImpl implements WeixinService {
             if (!json.containsKey("errcode")) {
                 return json;
             } else {
-                logger.error("获取openId出错:code = " + json.containsKey("errcode") + ",msg="+ json.get("errmsg"));
+                logger.error("获取openId出错:errcode = " + json.get("errcode") + ", msg = " + json.get("errmsg"));
             }
         } catch (Exception e) {
             logger.error("获取微信openId异常:" + e.getMessage());
@@ -422,46 +433,74 @@ public class WeixinServiceImpl implements WeixinService {
     }
 
     /**
-     * 获取微信绑定手机号
+     * 获取微信绑定手机号(手机号快速验证组件)
      *
-     * @param encryptedData 微信返回加密字符串
-     * @param sessionKey session键值
-     * @param iv 微信IV
+     * 注意:code 由前端 getPhoneNumber 回调返回,5分钟内有效且只能使用一次。
+     *
+     * @param merchantId 商户ID
+     * @param code 前端 getPhoneNumber 回调返回的动态令牌code
      * @return
      * */
     @Override
-    public String getPhoneNumber(String encryptedData, String sessionKey, String iv) {
-        // 被加密的数据
-        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
-        // 加密秘钥
-        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
-        // 偏移量
-        byte[] ivByte = Base64.getDecoder().decode(iv);
-        try {
-            // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
-            int base = 16;
-            if (keyByte.length % base != 0) {
-                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
-                byte[] temp = new byte[groups * base];
-                Arrays.fill(temp, (byte) 0);
-                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
-                keyByte = temp;
+    public String getPhoneNumberByCode(Integer merchantId, String code) {
+        if (StringUtil.isEmpty(code)) {
+            return null;
+        }
+
+        String accessToken = getAccessToken(merchantId, true, true);
+        if (StringUtil.isEmpty(accessToken)) {
+            logger.error("获取手机号失败:accessToken为空,merchantId={}", merchantId);
+            return null;
+        }
+
+        JSONObject json = requestPhoneNumber(merchantId, code, accessToken);
+        // 40001 表示 access_token 失效,此时 code 尚未被微信消费,重新拉取 token 后重试一次是安全的;
+        // 而 40029/40163 表示 code 无效或已被使用,重试无意义,必须由用户重新点击授权
+        if (json != null && "40001".equals(String.valueOf(json.get("errcode")))) {
+            logger.info("获取手机号:access_token失效,重新拉取后重试一次,merchantId={}", merchantId);
+            accessToken = getAccessToken(merchantId, true, false);
+            if (StringUtil.isEmpty(accessToken)) {
+                logger.error("获取手机号失败:重新拉取后accessToken仍为空,merchantId={}", merchantId);
+                return null;
             }
-            // 初始化
-            Security.addProvider(new BouncyCastleProvider());
-            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
-            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
-            parameters.init(new IvParameterSpec(ivByte));
-            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
-            byte[] resultByte = cipher.doFinal(dataByte);
-            if (null != resultByte && resultByte.length > 0) {
-                String result = new String(resultByte, "UTF-8");
-                JSONObject object = JSONObject.parseObject(result);
-                return object.getString("phoneNumber");
+            json = requestPhoneNumber(merchantId, code, accessToken);
+        }
+
+        if (json == null) {
+            return null;
+        }
+        // 以 phone_info.phoneNumber 是否存在判定成功,而不是 errcode == 0:
+        // 该接口成功时返回 phone_info,errcode 仅在失败时出现,用 errcode 判成功会把成功误判为失败
+        JSONObject phoneInfo = json.getJSONObject("phone_info");
+        if (phoneInfo != null && StringUtil.isNotEmpty(phoneInfo.getString("phoneNumber"))) {
+            return phoneInfo.getString("phoneNumber");
+        }
+
+        logger.error("获取手机号出错:errcode={}, errmsg={}, merchantId={}", json.get("errcode"), json.get("errmsg"), merchantId);
+        return null;
+    }
+
+    /**
+     * 调用微信「手机号快速验证」接口,用 code 换取用户手机号
+     *
+     * @param merchantId 商户ID
+     * @param code 前端 getPhoneNumber 回调返回的动态令牌code
+     * @param accessToken 小程序接口调用凭据
+     * @return 微信原始响应;请求异常时返回 null
+     * */
+    private JSONObject requestPhoneNumber(Integer merchantId, String code, String accessToken) {
+        try {
+            JSONObject reqData = new JSONObject();
+            reqData.put("code", code);
+            String url = String.format(GET_PHONE_NUMBER_URL, accessToken);
+            String response = HttpRESTDataClient.requestPost(url, "application/json; charset=utf-8", reqData.toJSONString());
+            JSONObject json = JSONObject.parseObject(response);
+            if (json == null) {
+                logger.error("获取手机号失败:响应为空,merchantId={}", merchantId);
             }
+            return json;
         } catch (Exception e) {
-            e.printStackTrace();
+            logger.error("获取手机号异常:" + e.getMessage());
         }
 
         return null;
@@ -571,7 +610,9 @@ public class WeixinServiceImpl implements WeixinService {
             logger.info("WeixinService sendSubscribeMessage response={}", response);
             JSONObject json = (JSONObject) JSONObject.parse(response);
             if (json.get("errcode").toString().equals("40001")) {
-                getAccessToken(merchantId, true,true);
+                // useCache=false 跳过 Redis 直接向微信重拉并回写缓存;
+                // 若传 true 会命中本地缓存里的旧 token,这次刷新就白做了
+                getAccessToken(merchantId, true, false);
                 logger.error("发送订阅消息出错error1:" + json.get("errcode").toString());
                 return false;
             } else if (!json.get("errcode").toString().equals("0")) {

+ 52 - 0
fuint-application/src/main/java/com/fuint/module/backendApi/controller/merchant/BackendPrinterController.java

@@ -4,9 +4,11 @@ import com.fuint.common.dto.system.AccountInfo;
 import com.fuint.common.dto.order.UserOrderDto;
 import com.fuint.common.enums.PrinterSettingEnum;
 import com.fuint.common.enums.SettingTypeEnum;
+import com.fuint.common.enums.StatusEnum;
 import com.fuint.common.param.PrinterPage;
 import com.fuint.common.param.PrinterParam;
 import com.fuint.common.param.StatusParam;
+import com.fuint.common.service.CateService;
 import com.fuint.common.service.OrderService;
 import com.fuint.common.service.PrinterService;
 import com.fuint.common.service.SettingService;
@@ -16,6 +18,7 @@ import com.fuint.framework.exception.BusinessCheckException;
 import com.fuint.framework.pagination.PaginationResponse;
 import com.fuint.framework.web.BaseController;
 import com.fuint.framework.web.ResponseObject;
+import com.fuint.repository.model.MtGoodsCate;
 import com.fuint.repository.model.MtPrinter;
 import com.fuint.repository.model.MtSetting;
 import com.fuint.repository.model.MtStore;
@@ -64,6 +67,11 @@ public class BackendPrinterController extends BaseController {
      * */
     private OrderService orderService;
 
+    /**
+     * 商品分类服务接口
+     * */
+    private CateService cateService;
+
     /**
      * 打印机列表查询
      */
@@ -86,6 +94,8 @@ public class BackendPrinterController extends BaseController {
         Map<String, Object> result = new HashMap<>();
         result.put("paginationResponse", paginationResponse);
         result.put("storeList", storeList);
+        // 商品分类列表,供配置打印机与商品分类的映射关系
+        result.put("cateList", cateService.getCateList(accountInfo.getMerchantId(), accountInfo.getStoreId(), null, StatusEnum.ENABLED.getKey()));
 
         return getSuccessResult(result);
     }
@@ -134,6 +144,9 @@ public class BackendPrinterController extends BaseController {
         } else {
             printerService.addPrinter(mtPrinter);
         }
+        // 保存打印机与商品分类的映射关系
+        printerService.savePrinterCate(mtPrinter, printer.getCateIds(), accountInfo);
+
         return getSuccessResult(true);
     }
 
@@ -155,6 +168,9 @@ public class BackendPrinterController extends BaseController {
 
         Map<String, Object> result = new HashMap<>();
         result.put("printerInfo", printerInfo);
+        // 已配置的商品分类,以及可选的商品分类列表
+        result.put("cateIds", printerService.queryCateIdsByPrinterId(printerInfo.getId()));
+        result.put("cateList", cateService.getCateList(accountInfo.getMerchantId(), accountInfo.getStoreId(), null, StatusEnum.ENABLED.getKey()));
 
         return getSuccessResult(result);
     }
@@ -255,4 +271,40 @@ public class BackendPrinterController extends BaseController {
 
         return getSuccessResult(result);
     }
+
+    /**
+     * 向指定打印机打印订单
+     */
+    @ApiOperation(value = "向指定打印机打印订单")
+    @RequestMapping(value = "/printOrder", method = RequestMethod.POST)
+    @CrossOrigin
+    @PreAuthorize("@pms.hasPermission('order:index')")
+    public ResponseObject printOrder(@RequestBody Map<String, Object> param) throws Exception {
+        AccountInfo accountInfo = TokenUtil.getAccountInfo();
+        Integer orderId = param.get("orderId") == null ? 0 : Integer.parseInt(param.get("orderId").toString());
+        Integer printerId = param.get("printerId") == null ? 0 : Integer.parseInt(param.get("printerId").toString());
+
+        if (orderId < 1 || printerId < 1) {
+            return getFailureResult(201, "订单ID和打印机ID不能为空");
+        }
+
+        UserOrderDto orderInfo = orderService.getOrderById(orderId);
+        if (orderInfo == null) {
+            return getFailureResult(201, "该订单不存在");
+        }
+
+        if (accountInfo.getMerchantId() != null && accountInfo.getMerchantId() > 0 && !accountInfo.getMerchantId().equals(orderInfo.getMerchantId())) {
+            return getFailureResult(1004);
+        }
+
+        MtPrinter printerInfo = printerService.queryPrinterById(printerId);
+        if (printerInfo == null) {
+            return getFailureResult(201, "该打印机不存在");
+        }
+
+        // 打印整个订单的明细到指定打印机
+        Boolean result = printerService.printOrderToPrinter(orderInfo, printerInfo);
+
+        return getSuccessResult(result);
+    }
 }

+ 17 - 7
fuint-application/src/main/java/com/fuint/module/clientApi/controller/ClientSignController.java

@@ -89,25 +89,35 @@ public class ClientSignController extends BaseController {
         String storeId = request.getHeader("storeId") == null ? "0" : request.getHeader("storeId");
         String merchantNo = request.getHeader("merchantNo") == null ? "" : request.getHeader("merchantNo");
         String shareId = param.get("shareId") == null ? "0" : param.get("shareId").toString();
+        String code = param.get("code") == null ? "" : param.get("code").toString();
         JSONObject paramsObj = new JSONObject(param);
         logger.info("微信授权登录参数:{}", param);
         Integer merchantId = merchantService.getMerchantId(merchantNo);
         // 校验商户是否已过期
         merchantService.checkMerchantValid(merchantId);
         JSONObject userInfo = paramsObj.getJSONObject("userInfo");
-        JSONObject loginInfo = weixinService.getWxProfile(merchantId, param.get("code").toString());
-        if (loginInfo == null) {
+        if (userInfo == null) {
+            return getFailureResult(0, "微信登录失败:userInfo不能为空");
+        }
+        if (StringUtil.isEmpty(code)) {
+            return getFailureResult(0, "微信登录失败:code不能为空");
+        }
+        JSONObject loginInfo = weixinService.getWxProfile(merchantId, code);
+        if (loginInfo == null || StringUtil.isEmpty(loginInfo.getString("openid"))) {
             return getFailureResult(0, "微信登录失败");
         }
 
-        String type = userInfo.getString("type");
-        String encryptedData = userInfo.getString("encryptedData");
         userInfo.put("phone", "");
         userInfo.put("shareId", shareId);
         userInfo.put("source", MemberSourceEnum.WECHAT_LOGIN.getKey());
-        if (type.equals("phone") && StringUtil.isNotEmpty(encryptedData)) {
-            String phone = weixinService.getPhoneNumber(userInfo.get("encryptedData").toString(), loginInfo.get("session_key").toString(), userInfo.get("iv").toString());
-            userInfo.put("phone", phone);
+        // 注意:顶层 param.code 来自 wx.login(),userInfo.code 来自 getPhoneNumber 回调,
+        // 两者字段名相同但用途完全无关、不可互换,混用会报 40029 / 40163
+        String phoneCode = userInfo.getString("code");
+        if (StringUtil.isNotEmpty(phoneCode)) {
+            String phone = weixinService.getPhoneNumberByCode(merchantId, phoneCode);
+            if (StringUtil.isNotEmpty(phone)) {
+                userInfo.put("phone", phone);
+            }
         }
         // 默认店铺
         if (StringUtil.isEmpty(storeId)) {

+ 4 - 4
fuint-application/src/main/java/com/fuint/module/clientApi/controller/ClientUserController.java

@@ -311,11 +311,11 @@ public class ClientUserController extends BaseController {
             }
         }
 
-        // 通过小程序获取手机号
+        // 通过小程序获取手机号(手机号快速验证组件,code 5分钟内有效且只能使用一次)
         if (StringUtil.isNotEmpty(code)) {
-            JSONObject loginInfo = weixinService.getWxProfile(merchantId, code);
-            if (loginInfo != null) {
-                mobile = weixinService.getPhoneNumber(memberInfo.getEncryptedData(), loginInfo.get("session_key").toString(), memberInfo.getIv());
+            phone = weixinService.getPhoneNumberByCode(merchantId, code);
+            if (StringUtil.isNotEmpty(phone)) {
+                mobile = phone;
             }
         }
 

+ 0 - 6
fuint-application/src/main/java/com/fuint/module/clientApi/request/MemberInfoRequest.java

@@ -28,12 +28,6 @@ public class MemberInfoRequest implements Serializable {
     @ApiModelProperty(value="微信手机号授权码", name="code")
     private String code;
 
-    @ApiModelProperty(value="微信手机号授权加密数据", name="encryptedData")
-    private String encryptedData;
-
-    @ApiModelProperty(value="微信手机号授权码iv", name="iv")
-    private String iv;
-
     @ApiModelProperty(value="新密码", name="password")
     private String password;
 

+ 2 - 2
fuint-application/src/main/resources/application.properties

@@ -22,11 +22,11 @@ multipart.max-request-size=20mb
 # 最大上传文件
 spring.servlet.multipart.max-file-size=10MB
 spring.servlet.multipart.max-request-size=10MB
-mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
 
 # mybatis配置
 mybatis-plus.mapper-locations = classpath*:/mapper/*.xml
-mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
 
 # 默认时间格式
 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

+ 14 - 0
fuint-repository/src/main/java/com/fuint/repository/mapper/MtPrinterCateMapper.java

@@ -0,0 +1,14 @@
+package com.fuint.repository.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fuint.repository.model.MtPrinterCate;
+
+/**
+ *  打印机与商品分类映射 Mapper 接口
+ *
+ * Created by FSQ
+ * CopyRight https://www.fuint.cn
+ */
+public interface MtPrinterCateMapper extends BaseMapper<MtPrinterCate> {
+   // empty
+}

+ 3 - 0
fuint-repository/src/main/java/com/fuint/repository/model/MtPrinter.java

@@ -51,6 +51,9 @@ public class MtPrinter implements Serializable {
     @ApiModelProperty("支付后打印")
     private String afterPay;
 
+    @ApiModelProperty("是否默认打印机,Y:是;N:否。默认打印机负责打印未被任何映射认领的商品")
+    private String isDefault;
+
     @ApiModelProperty("创建时间")
     private Date createTime;
 

+ 53 - 0
fuint-repository/src/main/java/com/fuint/repository/model/MtPrinterCate.java

@@ -0,0 +1,53 @@
+package com.fuint.repository.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 打印机与商品分类映射
+ *
+ * Created by FSQ
+ * CopyRight https://www.fuint.cn
+ */
+@Data
+@TableName("mt_printer_cate")
+@ApiModel(value = "MtPrinterCate对象", description = "")
+public class MtPrinterCate implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("自增ID")
+    @TableId(value = "ID", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("所属商户ID")
+    private Integer merchantId;
+
+    @ApiModelProperty("所属店铺ID")
+    private Integer storeId;
+
+    @ApiModelProperty("打印机ID")
+    private Integer printerId;
+
+    @ApiModelProperty("商品分类ID")
+    private Integer cateId;
+
+    @ApiModelProperty("状态,A:正常;D:删除")
+    private String status;
+
+    @ApiModelProperty("最后操作人")
+    private String operator;
+
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    private Date updateTime;
+
+}