package com.ruoyi.web.controller.smartor; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.mchange.v2.uid.UidUtils; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.framework.config.ServerConfig; import com.smartor.domain.Question; import com.smartor.domain.SvyLibTemplate; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URI; import java.util.*; @Slf4j @Api(description = "问卷二维码") @RestController @RequestMapping("/qrcode") public class QRCodeController { private final Configuration configuration; public QRCodeController(Configuration configuration) { this.configuration = configuration; } @Value("${ruoyi.profile}") private String profile; /** * 公司logo在classpath中的路径,打包进jar后任何环境都能读取到 */ private static final String LOGO_CLASSPATH = "/images/hrslogo.png"; @Autowired private ServerConfig serverConfig; /** * 问卷题目 * * @param reqid 问卷ID * userid 用户ID * @return * @throws IOException * @throws TemplateException */ @ResponseBody @GetMapping(value = "/generateStaticHtml/{reqid}/{userid}", produces = MediaType.TEXT_HTML_VALUE) public String generateStaticHtml(@PathVariable("reqid") String reqid, @PathVariable("userid") String userid) throws IOException, TemplateException { // 创建Freemarker配置 Configuration configuration = new Configuration(Configuration.VERSION_2_3_31); configuration.setClassForTemplateLoading(SvyLibTemplateController.class, "/template"); configuration.setDefaultEncoding("UTF-8"); try { // 加载模板 Template template = configuration.getTemplate("question.ftl"); // 准备题目数据 List questions = new ArrayList<>(); // 单选题 List singleChoiceOptions = new ArrayList<>(); singleChoiceOptions.add("Option 1"); singleChoiceOptions.add("Option 2"); singleChoiceOptions.add("Option 3"); Question singleChoiceQuestion = new Question("singleChoice", "单选题111", "单选题题目1:", singleChoiceOptions); List singleChoiceOptions2 = new ArrayList<>(); singleChoiceOptions2.add("Option 4"); singleChoiceOptions2.add("Option 5"); singleChoiceOptions2.add("Option 6"); Question singleChoiceQuestion2 = new Question("singleChoice", "单选题222", "单选题题目2:", singleChoiceOptions2); questions.add(singleChoiceQuestion); questions.add(singleChoiceQuestion2); // 多选题 List multipleChoiceOptions = new ArrayList<>(); multipleChoiceOptions.add("Option A"); multipleChoiceOptions.add("Option B"); multipleChoiceOptions.add("Option C"); Question multipleChoiceQuestion = new Question("multipleChoice", "多选题1", "Question 2: Select multiple options", multipleChoiceOptions); questions.add(multipleChoiceQuestion); // 问答题 Question openEndedQuestion = new Question("openEnded", "问答题题", "问答题题目1", null); questions.add(openEndedQuestion); // 准备模板数据 Map data = new HashMap<>(); data.put("questions", questions); // 渲染模板 StringWriter writer = new StringWriter(); template.process(data, writer); String renderedTemplate = writer.toString(); return writer.toString(); // 返回模板内容给客户端 // System.out.println(renderedTemplate); // 这里只是示例,实际应用中应将内容返回给客户端进行展示 } catch (IOException | TemplateException e) { e.printStackTrace(); } return null; } /** * @param url 问卷ID * @param url 用户ID */ @ApiOperation("获取问卷二维码") @PostMapping(value = "/getQRcode") public AjaxResult getQRcode(@RequestParam("url") String url) { log.info("获取问卷二维码的入参:{}", url); // 尝试从url中提取p参数的值作为图片名称,值为空则用UUID String fileName = UUID.randomUUID().toString().replace("-", ""); try { URI uri = new URI(url); String query = uri.getQuery(); if (query != null) { for (String param : query.split("&")) { String[] pair = param.split("=", 2); if ("p".equals(pair[0]) && pair.length > 1 && !pair[1].isEmpty()) { fileName = pair[1] + "-" + System.currentTimeMillis(); break; } } } } catch (Exception e) { log.warn("解析url中的p参数失败,使用UUID作为文件名"); } String filePath = profile + "\\qrpath" + "\\" + fileName + ".png"; // 保存二维码图像的文件路径 //判断文件夹是否存在 File file = new File(profile + "\\qrpath"); if (!file.exists()) { file.mkdirs(); } generateQRCode(url, filePath); return AjaxResult.success(serverConfig.getUrl() + "/profile/qrpath/" + fileName + ".png"); } private void generateQRCode(String url, String filePath) { QRCodeWriter qrCodeWriter = new QRCodeWriter(); try { // 使用高容错级别(H级,30%容错),确保二维码中间嵌入logo后仍可正常扫描 Map hints = new HashMap<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = qrCodeWriter.encode(url, BarcodeFormat.QR_CODE, 500, 500, hints); // 将BitMatrix转为二值BufferedImage(黑白) BufferedImage binaryImage = MatrixToImageWriter.toBufferedImage(bitMatrix); // 创建RGB彩色图像,把二维码画上去,否则后续绘制logo会丢失颜色 BufferedImage qrImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = qrImage.createGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, 500, 500); graphics.drawImage(binaryImage, 0, 0, null); // 从classpath读取logo并绘制到二维码中间 try (InputStream logoStream = this.getClass().getResourceAsStream(LOGO_CLASSPATH)) { if (logoStream != null) { BufferedImage logoImage = ImageIO.read(logoStream); // 计算logo尺寸,设为二维码的1/5 int logoWidth = qrImage.getWidth() / 5; int logoHeight = qrImage.getHeight() / 5; // 计算logo居中位置 int logoX = (qrImage.getWidth() - logoWidth) / 2; int logoY = (qrImage.getHeight() - logoHeight) / 2; // 绘制白色圆角背景,让logo更突出 int padding = 6; graphics.setColor(Color.WHITE); graphics.fillRoundRect(logoX - padding, logoY - padding, logoWidth + padding * 2, logoHeight + padding * 2, 10, 10); // 绘制logo graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.drawImage(logoImage, logoX, logoY, logoWidth, logoHeight, null); } else { log.warn("classpath中未找到二维码logo: {}", LOGO_CLASSPATH); } } graphics.dispose(); // 输出最终图片 File outputFile = new File(filePath); ImageIO.write(qrImage, "PNG", outputFile); } catch (WriterException | IOException e) { e.printStackTrace(); } } /** * 问券结果处理 * * @param request */ @PostMapping(value = "/getFormDate") public void getFormDate(HttpServletRequest request) { Map formData = request.getParameterMap(); System.out.println(formData.toString()); } }