From 4a4c5623aac08735944e12fb93947808c87631d0 Mon Sep 17 00:00:00 2001 From: wangw <1594593906@qq.com> Date: Fri, 21 Jun 2024 09:18:56 +0800 Subject: [PATCH] =?UTF-8?q?client=20=E6=A0=87=E7=AD=BE=E6=89=93=E5=8D=B0?= =?UTF-8?q?=20=E9=95=BF=E9=93=BE=E6=8E=A5=20=E4=BC=9A=E5=91=98=E7=BB=91?= =?UTF-8?q?=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 ++ .../controller/NotifyController.java | 10 +- .../cashierservice/dao/TbShopUserMapper.java | 1 + .../cashierservice/entity/TbCashierCart.java | 9 + .../netty/ConnectionDebouncerHandler.java | 35 ++++ .../netty/HeartbeatHandler.java | 2 +- .../netty/PushToAppChannelInitializer.java | 6 +- .../PushToClientChannelHandlerAdapter.java | 187 ++++++++++++++++++ .../netty/PushToClientChannelInitializer.java | 52 +++++ .../netty/config/NettyConfig.java | 7 + .../cashierservice/service/LoginService.java | 10 +- .../cashierservice/service/PayService.java | 58 +++++- .../cashierservice/util/QrCodeUtils.java | 115 +++++++++++ src/main/resources/application.yml | 1 + .../resources/mapper/TbShopUserMapper.xml | 9 + 15 files changed, 493 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/chaozhanggui/system/cashierservice/netty/ConnectionDebouncerHandler.java create mode 100644 src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToClientChannelHandlerAdapter.java create mode 100644 src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToClientChannelInitializer.java create mode 100644 src/main/java/com/chaozhanggui/system/cashierservice/util/QrCodeUtils.java diff --git a/pom.xml b/pom.xml index 660455f..2ea4374 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,18 @@ runtime + + + com.google.zxing + core + 3.5.3 + + + + com.google.zxing + javase + 3.5.3 + org.apache.commons diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/controller/NotifyController.java b/src/main/java/com/chaozhanggui/system/cashierservice/controller/NotifyController.java index 52970dc..4b33f13 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/controller/NotifyController.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/controller/NotifyController.java @@ -9,10 +9,7 @@ import com.chaozhanggui.system.cashierservice.service.PayService; import com.chaozhanggui.system.cashierservice.util.DateUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.Date; @@ -69,6 +66,11 @@ public class NotifyController { } + @RequestMapping("test") + public void test(@RequestParam String payOrderNO){ + payService.test(payOrderNO); + } + @RequestMapping("notifyCallBack") public String notifyCallBack(HttpServletRequest request){ diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/dao/TbShopUserMapper.java b/src/main/java/com/chaozhanggui/system/cashierservice/dao/TbShopUserMapper.java index 3c46087..ab3069b 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/dao/TbShopUserMapper.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/dao/TbShopUserMapper.java @@ -25,6 +25,7 @@ public interface TbShopUserMapper { int updateByPrimaryKeySelective(TbShopUser record); int updateByPrimaryKey(TbShopUser record); + int upUserBYId(TbShopUser record); TbShopUser selectByUserIdAndShopId(@Param("userId") String userId,@Param("shopId") String shopId); diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/entity/TbCashierCart.java b/src/main/java/com/chaozhanggui/system/cashierservice/entity/TbCashierCart.java index 4e8ab74..0c99e3a 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/entity/TbCashierCart.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/entity/TbCashierCart.java @@ -1,6 +1,7 @@ package com.chaozhanggui.system.cashierservice.entity; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.math.BigDecimal; @@ -60,4 +61,12 @@ public class TbCashierCart implements Serializable { private TbProductSpec tbProductSpec; private static final long serialVersionUID = 1L; + + public String getSkuName() { + if(StringUtils.isNotBlank(skuName)){ + return skuName; + }else { + return ""; + } + } } \ No newline at end of file diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/netty/ConnectionDebouncerHandler.java b/src/main/java/com/chaozhanggui/system/cashierservice/netty/ConnectionDebouncerHandler.java new file mode 100644 index 0000000..1a620da --- /dev/null +++ b/src/main/java/com/chaozhanggui/system/cashierservice/netty/ConnectionDebouncerHandler.java @@ -0,0 +1,35 @@ +package com.chaozhanggui.system.cashierservice.netty; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +public class ConnectionDebouncerHandler extends ChannelDuplexHandler { + private static final Map lastConnectionTimes = new ConcurrentHashMap<>(); + private static final long debounceIntervalMillis = 5*1000; // 防抖时间间隔,单位:毫秒 + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Channel channel = ctx.channel(); + long currentTimeMillis = System.currentTimeMillis(); + Long lastConnectionTime = lastConnectionTimes.get(channel); + if (lastConnectionTime == null || (currentTimeMillis - lastConnectionTime) > debounceIntervalMillis) { + // 允许新连接 + lastConnectionTimes.put(channel, currentTimeMillis); + super.channelActive(ctx); // 将事件传递给下一个处理器 + } else { + + channel.close(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + ctx.close(); + } +} diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/netty/HeartbeatHandler.java b/src/main/java/com/chaozhanggui/system/cashierservice/netty/HeartbeatHandler.java index f163b9b..3d2f1ba 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/netty/HeartbeatHandler.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/netty/HeartbeatHandler.java @@ -28,7 +28,7 @@ public class HeartbeatHandler extends ChannelDuplexHandler { super.userEventTriggered(ctx, evt); } else if (event.state() == IdleState.WRITER_IDLE) { // log.info("发送心跳"); - ctx.channel().writeAndFlush(new TextWebSocketFrame("Heartbeat")).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + ctx.channel().writeAndFlush(new TextWebSocketFrame("{\"type\":\"heartbeat\"}")).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); // ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } } else { diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToAppChannelInitializer.java b/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToAppChannelInitializer.java index 0da7d2c..74d0c25 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToAppChannelInitializer.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToAppChannelInitializer.java @@ -11,14 +11,10 @@ import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.handler.timeout.IdleStateHandler; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import javax.net.ssl.SSLException; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.concurrent.TimeUnit; @Component @@ -35,6 +31,8 @@ public class PushToAppChannelInitializer extends ChannelInitializer> webSocketMap = new HashMap<>(); + + /** + * [ctx, shopId:clientId] + */ + private static Map clientIdMap = new ConcurrentHashMap<>(); + + + private String clientId = ""; + private String shopId = ""; + + public PushToClientChannelHandlerAdapter() { + } + + public static PushToClientChannelHandlerAdapter getInstance() { + return new PushToClientChannelHandlerAdapter(); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.info("netty连接client 长连接激活"); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + log.info("netty连接client 长连接关闭:{}, {}",clientId,shopId); + ctx.close(); + removeCtx(ctx); + } + + /** + * 移除ctx + */ + private void removeCtx(ChannelHandlerContext ctx) { + // shopId:clientId + String key = clientIdMap.get(ctx); + if (StringUtils.isNotBlank(key)) { + String[] split = key.split(":"); + ConcurrentHashMap tableMap = webSocketMap.get(split[0]); + if (tableMap != null && !tableMap.isEmpty() && tableMap.size() > 0) { + tableMap.remove(split[1]); + if (tableMap.isEmpty() || tableMap.size() == 0) { + webSocketMap.remove(split[0]); + } + } + } + clientIdMap.remove(ctx); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + super.channelReadComplete(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + super.exceptionCaught(ctx, cause); + removeCtx(ctx); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, String msg) { + JSONObject jsonObject = new JSONObject(); + if (StringUtils.isNotEmpty(msg)) { + jsonObject = JSONObject.parseObject(msg); + }else { + log.info("netty连接client 接收到空数据:{}",msg); + } + String type = jsonObject.getString("type"); + if(type.equals("heartbeat")){//心跳 + log.info("netty连接client 接收到心跳数据:shop:{} clientId:{} meg:{}",shopId,clientId,msg); + }else { + if (type.equals("connect")) { + String clientId = jsonObject.getString("clientId"); + String shopId = jsonObject.getString("shopId"); + if (StringUtils.isBlank(type) || StringUtils.isBlank(shopId) || StringUtils.isBlank(clientId)) { + log.info("netty连接client 建立连接请求失败:{}",jsonObject); + channelInactive(ctx); + return; + } + + log.info("netty连接client 接收到数据 建立连接参数 param:{}",jsonObject); + this.clientId=clientId; + this.shopId=shopId; + if (webSocketMap.containsKey(shopId)) { + ConcurrentHashMap clientSocketMap = webSocketMap.get(shopId); + clientSocketMap.put(clientId, ctx); + } else { + ConcurrentHashMap clientSocketMap = new ConcurrentHashMap<>(); + clientSocketMap.put(clientId, ctx); + webSocketMap.put(shopId,clientSocketMap); + } + clientIdMap.put(ctx, shopId + ":" + clientId); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("status", "success"); + jsonObject1.put("msg", "连接成功"); + jsonObject1.put("type", "connect"); + sendMesToApp(jsonObject1.toString(), ctx); + } + } + //业务逻辑代码处理框架。。。 + ctx.flush(); + } + + public void sendMesToApp(String str, ChannelHandlerContext ctx) { + sendMessage(ctx, str); + } + + /** + * @param message 发送的消息内容 + * @param shopId 店铺Id + * @param clientId 客户端Id + * @param userFlag + * 为true 单发给clientId + * 为false 群发 shopId为空 发给所有人 + */ + @Async + public void AppSendInfo(String message, String shopId,String clientId, boolean userFlag) { + log.info("netty连接client 发送消息 shopId:{} clientId:{} userFlag:{} message:{}",shopId,clientId,userFlag, JSONUtil.toJSONString(message)); + if (userFlag) { + if (webSocketMap.containsKey(shopId)) { + ConcurrentHashMap webSockets = webSocketMap.get(shopId); + if(!webSockets.isEmpty()){ + if (StringUtils.isNotBlank(clientId)) { + ChannelHandlerContext ctx = webSockets.get(clientId); + if (ctx != null) { + sendMesToApp(message,ctx); + } + } + } + } + } else { + if (StringUtils.isEmpty(shopId)) { + // 向所有用户发送信息 + for (ConcurrentHashMap value : webSocketMap.values()) { + for (ChannelHandlerContext ctx : value.values()) { + sendMesToApp(message,ctx); + } + } + } else if (webSocketMap.containsKey(shopId)) { + ConcurrentHashMap webSockets = webSocketMap.get(shopId); + if(!webSockets.isEmpty()) { + for (String user : webSockets.keySet()) { + ChannelHandlerContext ctx = webSockets.get(user); + if (ctx != null) { + log.info("netty连接client 发送消息 桌码群发 clientId:{}",user); + sendMesToApp(message,ctx); + }else { + log.info("netty连接client 发送消息 桌码群发 clientId:{} 失败",user); + } + } + }else { + log.info("netty连接client 发送消息 桌码群发 clientId:{} 失败",clientId); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToClientChannelInitializer.java b/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToClientChannelInitializer.java new file mode 100644 index 0000000..fd2b305 --- /dev/null +++ b/src/main/java/com/chaozhanggui/system/cashierservice/netty/PushToClientChannelInitializer.java @@ -0,0 +1,52 @@ +package com.chaozhanggui.system.cashierservice.netty; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.stream.ChunkedWriteHandler; +import io.netty.handler.timeout.IdleStateHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.net.ssl.SSLException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class PushToClientChannelInitializer extends ChannelInitializer { + + + + @Override + protected void initChannel(NioSocketChannel ch) throws SSLException { + ChannelPipeline pipeline = ch.pipeline(); + + // 添加心跳处理器 多久没收到消息 断开 心跳时间(秒) 读写空闲时间(秒) + pipeline.addLast(new IdleStateHandler(30, 10, 120, TimeUnit.SECONDS)); + pipeline.addLast(new HeartbeatHandler()); + // 添加连接防抖处理器(没用) +// pipeline.addLast(new ConnectionDebouncerHandler()); + //本地试调时使用 wss +// SslContext sslContext = SslContextBuilder.forServer(loadResourceAsByteArrayInputStream("\\pem\\fullchain.pem"), loadResourceAsByteArrayInputStream("\\pem\\privkey.key")).build(); +// pipeline.addLast(sslContext.newHandler(ch.alloc())); + + // 添加HttpServerCodec用于处理HTTP编解码 + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new ChunkedWriteHandler()); + pipeline.addLast(new HttpObjectAggregator(65536)); + pipeline.addLast(new WebSocketServerProtocolHandler("/client")); + ch.pipeline().addLast(new PushToClientChannelHandlerAdapter()); + } + + + public static InputStream loadResourceAsByteArrayInputStream(String path) { + InputStream inputStream = PushToClientChannelInitializer.class.getClassLoader().getResourceAsStream(path); + return inputStream; + } +} diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/netty/config/NettyConfig.java b/src/main/java/com/chaozhanggui/system/cashierservice/netty/config/NettyConfig.java index 07793ec..8ded4f7 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/netty/config/NettyConfig.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/netty/config/NettyConfig.java @@ -1,6 +1,7 @@ package com.chaozhanggui.system.cashierservice.netty.config; import com.chaozhanggui.system.cashierservice.netty.PushToAppChannelInitializer; +import com.chaozhanggui.system.cashierservice.netty.PushToClientChannelInitializer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -18,6 +19,9 @@ public class NettyConfig { @Value("${netty.server.port}") private int port; + @Value("${netty.server.client-port}") + private int clientPort; + /** * 接收者的线程数 */ @@ -31,8 +35,11 @@ public class NettyConfig { @Resource private PushToAppChannelInitializer pushToAppChannelInitializer; + @Resource + private PushToClientChannelInitializer pushToClientChannelInitializer; @PostConstruct public void startCameraNetty() { NettyUtils.getInstance().initNetty(port, parentGroupThreads, childGroupThreads, pushToAppChannelInitializer); + NettyUtils.getInstance().initNetty(clientPort, parentGroupThreads, childGroupThreads, pushToClientChannelInitializer); } } diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/service/LoginService.java b/src/main/java/com/chaozhanggui/system/cashierservice/service/LoginService.java index 937bf18..db6db01 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/service/LoginService.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/service/LoginService.java @@ -116,9 +116,15 @@ public class LoginService { tbUserInfoMapper.updateByPrimaryKeySelective(userInfo); List tbShopUsers = tbShopUserMapper.selectAllByUserId(userInfo.getId().toString()); for (TbShopUser tbShopUser : tbShopUsers) { - tbShopUser.setTelephone(phone); + tbShopUser.setUserId(""); tbShopUser.setUpdatedAt(System.currentTimeMillis()); - tbShopUserMapper.updateByPrimaryKey(tbShopUser); + tbShopUserMapper.upUserBYId(tbShopUser); + } + List tbShopUsers1 = tbShopUserMapper.selectByPhone(telephone); + for (TbShopUser tbShopUser : tbShopUsers1) { + tbShopUser.setUpdatedAt(System.currentTimeMillis()); + tbShopUser.setUserId(userInfo.getId().toString()); + tbShopUserMapper.upUserBYId(tbShopUser); } } } diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/service/PayService.java b/src/main/java/com/chaozhanggui/system/cashierservice/service/PayService.java index 6e4815a..567afdb 100644 --- a/src/main/java/com/chaozhanggui/system/cashierservice/service/PayService.java +++ b/src/main/java/com/chaozhanggui/system/cashierservice/service/PayService.java @@ -11,6 +11,7 @@ import com.chaozhanggui.system.cashierservice.entity.vo.ShopUserListVo; import com.chaozhanggui.system.cashierservice.exception.MsgException; import com.chaozhanggui.system.cashierservice.model.PayReq; import com.chaozhanggui.system.cashierservice.model.TradeQueryReq; +import com.chaozhanggui.system.cashierservice.netty.PushToClientChannelHandlerAdapter; import com.chaozhanggui.system.cashierservice.rabbit.RabbitProducer; import com.chaozhanggui.system.cashierservice.redis.RedisCst; import com.chaozhanggui.system.cashierservice.redis.RedisUtil; @@ -148,6 +149,9 @@ public class PayService { if(!"unpaid".equals(orderInfo.getStatus())&&!"paying".equals(orderInfo.getStatus())){ return Result.fail("订单状态异常,不允许支付"); } + if (System.currentTimeMillis() - orderInfo.getCreatedAt() > 60 * 15 * 1000) { + return Result.fail("订单十五分钟内有效,当前已超时,请重新下单。"); + } if(ObjectUtil.isNull(orderInfo.getMerchantId())||ObjectUtil.isEmpty(orderInfo.getMerchantId())){ return Result.fail("没有对应的商户"); @@ -304,6 +308,9 @@ public class PayService { if (ObjectUtil.isEmpty(orderInfo)) { return Result.fail("订单信息不存在"); } + if (System.currentTimeMillis() - orderInfo.getCreatedAt() > 60 * 15 * 1000) { + return Result.fail("订单十五分钟内有效,当前已超时,请重新下单。"); + } TbUserInfo userInfo= tbUserInfoMapper.selectByPrimaryKey(Integer.valueOf(orderInfo.getUserId())); @@ -321,7 +328,7 @@ public class PayService { if (!"unpaid".equals(orderInfo.getStatus()) && !"paying".equals(orderInfo.getStatus()) ) { - return Result.fail("订单出状态异常"); + return Result.fail("订单状态异常"); } @@ -384,7 +391,7 @@ public class PayService { producer.putOrderCollect(jsonObject.toJSONString()); producer.printMechine(orderId); - + sendOrderToClient(orderInfo); return Result.success(CodeEnum.SUCCESS,"1"); } @@ -717,7 +724,8 @@ public class PayService { log.info("发送打印数据"); producer.printMechine(orderInfo.getId() + ""); - + sendOrderToClient(orderInfo); + redisUtil.deleteByKey(RedisCst.ORDER_EXPIRED.concat(orderInfo.getId().toString())); return Result.success(CodeEnum.SUCCESS,orderId); case "REFUND_ING": cartStatus="refunding"; @@ -751,11 +759,6 @@ public class PayService { } -// if("0".equals(userInfo.getIsPwd())){ -// return Result.fail("用户支付密码未设置"); -// } - - TbShopInfo shopInfo= tbShopInfoMapper.selectByPrimaryKey(Integer.valueOf(shopId)); if(ObjectUtil.isEmpty(shopInfo)){ return Result.fail("对应的店铺信息不存在"); @@ -872,7 +875,8 @@ public class PayService { log.info("发送打印数据"); producer.printMechine(orderInfo.getId() + ""); - + sendOrderToClient(orderInfo); + redisUtil.deleteByKey(RedisCst.ORDER_EXPIRED.concat(orderInfo.getId().toString())); return "SUCCESS"; } @@ -917,6 +921,8 @@ public class PayService { coupons.put("type","buy"); coupons.put("orderId",orderInfo.getId().toString()); producer.printCoupons(coupons.toJSONString()); + sendOrderToClient(orderInfo); + redisUtil.deleteByKey(RedisCst.ORDER_EXPIRED.concat(orderInfo.getId().toString())); return "SUCCESS"; } @@ -979,7 +985,11 @@ public class PayService { } - + @Transactional(rollbackFor = Exception.class) + public void test(String payOrderNO) { + TbOrderInfo orderInfo = tbOrderInfoMapper.selectByPayOrderNo(payOrderNO); + sendOrderToClient(orderInfo); + } @@ -1170,6 +1180,34 @@ public class PayService { return "SUCCESS"; } + public void sendOrderToClient(TbOrderInfo orderInfo) { + List tbCashierCarts = tbCashierCartMapper.selectByOrderId(orderInfo.getId().toString(), null); + JSONObject client = new JSONObject(); + JSONObject order = new JSONObject(); + order.put("orderNo",orderInfo.getOrderNo()); + order.put("masterId",StringUtils.isNotBlank(orderInfo.getMasterId())?orderInfo.getMasterId():""); + order.put("tableName",StringUtils.isNotBlank(orderInfo.getTableName())?orderInfo.getTableName():""); + client.put("carts", tbCashierCarts); + client.put("type", "order"); + client.put("amount", orderInfo.getPayAmount()); + client.put("remark", StringUtils.isNotBlank(orderInfo.getRemark())?orderInfo.getRemark():""); + client.put("orderInfo", order); + client.put("createdAt", orderInfo.getCreatedAt()); + client.put("outNumber", StringUtils.isNotBlank(orderInfo.getOutNumber())?orderInfo.getOutNumber():""); +// client.put("outNumberCode", ""); +// if(StringUtils.isNotBlank(orderInfo.getOutNumber())){ +// try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { +// QrCodeUtils.createCodeToOutputStream(orderInfo.getOutNumber(), outputStream); +// String s = Base64Utils.encodeToString(outputStream.toByteArray()); +// client.put("outNumberCode", s); +// } catch (Exception e) { +// log.info("生成 失败"); +// e.printStackTrace(); +// } +// } + PushToClientChannelHandlerAdapter.getInstance().AppSendInfo(client.toString(), orderInfo.getShopId(), "", false); + } + // public Result returnOrder(){ // diff --git a/src/main/java/com/chaozhanggui/system/cashierservice/util/QrCodeUtils.java b/src/main/java/com/chaozhanggui/system/cashierservice/util/QrCodeUtils.java new file mode 100644 index 0000000..c88cbda --- /dev/null +++ b/src/main/java/com/chaozhanggui/system/cashierservice/util/QrCodeUtils.java @@ -0,0 +1,115 @@ +package com.chaozhanggui.system.cashierservice.util; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import org.apache.commons.lang3.StringUtils; + +import javax.imageio.ImageIO; +import javax.swing.filechooser.FileSystemView; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * 二维码工具类 + * + * @author GYJ + */ +public class QrCodeUtils { + + //CODE_WIDTH:二维码宽度,单位像素 + private static final int CODE_WIDTH = 400; + //CODE_HEIGHT:二维码高度,单位像素 + private static final int CODE_HEIGHT = 400; + //FRONT_COLOR:二维码前景色,0x000000 表示黑色 + private static final int FRONT_COLOR = 0x000000; + //BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色 + //演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白 + private static final int BACKGROUND_COLOR = 0xFFFFFF; + + public static void createCodeToFile(String content, File codeImgFileSaveDir, String fileName) { + try { + if (StringUtils.isBlank(content) || StringUtils.isBlank(fileName)) { + return; + } + content = content.trim(); + if (codeImgFileSaveDir==null || codeImgFileSaveDir.isFile()) { + //二维码图片存在目录为空,默认放在桌面... + codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory(); + } + if (!codeImgFileSaveDir.exists()) { + //二维码图片存在目录不存在,开始创建... + codeImgFileSaveDir.mkdirs(); + } + + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + File codeImgFile = new File(codeImgFileSaveDir, fileName); + ImageIO.write(bufferedImage, "png", codeImgFile); + + System.out.println("二维码图片生成成功:" + codeImgFile.getPath()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 生成二维码并输出到输出流, 通常用于输出到网页上进行显示,输出到网页与输出到磁盘上的文件中,区别在于最后一句 ImageIO.write + * write(RenderedImage im,String formatName,File output):写到文件中 + * write(RenderedImage im,String formatName,OutputStream output):输出到输出流中 + * @param content :二维码内容 + * @param outputStream :输出流,比如 HttpServletResponse 的 getOutputStream + */ + public static void createCodeToOutputStream(String content, OutputStream outputStream) { + try { + if (StringUtils.isBlank(content)) { + return; + } + content = content.trim(); + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + //区别就是这一句,输出到输出流中,如果第三个参数是 File,则输出到文件中 + ImageIO.write(bufferedImage, "png", outputStream); + + System.out.println("二维码图片生成到输出流成功..."); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //核心代码-生成二维码 + private static BufferedImage getBufferedImage(String content) throws WriterException { + + //com.google.zxing.EncodeHintType:编码提示类型,枚举类型 + Map hints = new HashMap(); + + //EncodeHintType.CHARACTER_SET:设置字符编码类型 + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + + //EncodeHintType.ERROR_CORRECTION:设置误差校正 + //ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction + //不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + + //EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近 + hints.put(EncodeHintType.MARGIN, 1); + + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints); + BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR); + for (int x = 0; x < CODE_WIDTH; x++) { + for (int y = 0; y < CODE_HEIGHT; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR); + } + } + return bufferedImage; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 23bd948..ff32aef 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -29,6 +29,7 @@ websocket: netty: server: port: 9999 + client-port: 9998 # 接收者的线程数 parent-group-threads: 10 # 客户端的线程数 diff --git a/src/main/resources/mapper/TbShopUserMapper.xml b/src/main/resources/mapper/TbShopUserMapper.xml index 0bdeb13..0feb3d9 100644 --- a/src/main/resources/mapper/TbShopUserMapper.xml +++ b/src/main/resources/mapper/TbShopUserMapper.xml @@ -380,6 +380,15 @@ where id = #{id,jdbcType=VARCHAR} + + + update tb_shop_user + set + user_id = #{userId,jdbcType=VARCHAR}, + updated_at = #{updatedAt,jdbcType=BIGINT} + where id = #{id,jdbcType=VARCHAR} + +