client 标签打印 长链接

会员绑定
This commit is contained in:
wangw 2024-06-21 09:18:56 +08:00
parent 4d7e6c3650
commit 4a4c5623aa
15 changed files with 493 additions and 21 deletions

12
pom.xml
View File

@ -112,6 +112,18 @@
<scope>runtime</scope>
</dependency>
<!-- zxing生成二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>

View File

@ -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){

View File

@ -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);

View File

@ -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 "";
}
}
}

View File

@ -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<Channel, Long> 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();
}
}

View File

@ -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 {

View File

@ -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<NioSocketCha
pipeline.addLast(new IdleStateHandler(30, 10, 120, TimeUnit.SECONDS));
pipeline.addLast(new HeartbeatHandler());
// 添加连接防抖处理器(没用)
// pipeline.addLast(new ConnectionDebouncerHandler());
//本地试调时使用
// SslContext sslContext = SslContextBuilder.forServer(loadResourceAsByteArrayInputStream("\\pem\\fullchain.pem"), loadResourceAsByteArrayInputStream("\\pem\\privkey.key")).build();
// pipeline.addLast(sslContext.newHandler(ch.alloc()));

View File

@ -0,0 +1,187 @@
package com.chaozhanggui.system.cashierservice.netty;
import com.alibaba.fastjson.JSONObject;
import com.chaozhanggui.system.cashierservice.netty.config.NettyChannelHandlerAdapter;
import com.chaozhanggui.system.cashierservice.util.JSONUtil;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 客户端
*/
@Slf4j
@Sharable
@Component
public class PushToClientChannelHandlerAdapter extends NettyChannelHandlerAdapter {
/**
* [shopId, [clientId, ctx]]
*/
private static Map<String, ConcurrentHashMap<String, ChannelHandlerContext>> webSocketMap = new HashMap<>();
/**
* [ctx, shopId:clientId]
*/
private static Map<ChannelHandlerContext, String> 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<String, ChannelHandlerContext> 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<String, ChannelHandlerContext> clientSocketMap = webSocketMap.get(shopId);
clientSocketMap.put(clientId, ctx);
} else {
ConcurrentHashMap<String, ChannelHandlerContext> 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<String, ChannelHandlerContext> 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<String, ChannelHandlerContext> value : webSocketMap.values()) {
for (ChannelHandlerContext ctx : value.values()) {
sendMesToApp(message,ctx);
}
}
} else if (webSocketMap.containsKey(shopId)) {
ConcurrentHashMap<String, ChannelHandlerContext> 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);
}
}
}
}
}

View File

@ -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<NioSocketChannel> {
@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;
}
}

View File

@ -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);
}
}

View File

@ -116,9 +116,15 @@ public class LoginService {
tbUserInfoMapper.updateByPrimaryKeySelective(userInfo);
List<TbShopUser> 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<TbShopUser> tbShopUsers1 = tbShopUserMapper.selectByPhone(telephone);
for (TbShopUser tbShopUser : tbShopUsers1) {
tbShopUser.setUpdatedAt(System.currentTimeMillis());
tbShopUser.setUserId(userInfo.getId().toString());
tbShopUserMapper.upUserBYId(tbShopUser);
}
}
}

View File

@ -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<TbCashierCart> 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(){
//

View File

@ -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<EncodeHintType, Object> hints = new HashMap();
//EncodeHintType.CHARACTER_SET设置字符编码类型
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//EncodeHintType.ERROR_CORRECTION设置误差校正
//ErrorCorrectionLevel误差校正等级L = ~7% correctionM = ~15% correctionQ = ~25% correctionH = ~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;
}
}

View File

@ -29,6 +29,7 @@ websocket:
netty:
server:
port: 9999
client-port: 9998
# 接收者的线程数
parent-group-threads: 10
# 客户端的线程数

View File

@ -380,6 +380,15 @@
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="upUserBYId" parameterType="com.chaozhanggui.system.cashierservice.entity.TbShopUser">
update tb_shop_user
set
user_id = #{userId,jdbcType=VARCHAR},
updated_at = #{updatedAt,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<select id="selectByUserIdAndShopId" resultMap="BaseResultMap">
select * from tb_shop_user where user_id=#{userId} and shop_id=#{shopId}
</select>