From f2fb656912fec21b15475eb84e3d62a8d8d52543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Sat, 29 Aug 2026 16:23:09 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E4=BB=A3=E8=A1=A8=E5=9B=A2?= =?UTF-8?q?=E5=87=AD=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/sys/controller/SysConfController.java | 4 +- .../app/sys/controller/SysMenuController.java | 6 +- .../sys/services/impl/SysFileServiceImpl.java | 2 +- .../budwk/app/sys/utils/DevFileMinIoUtil.java | 2 +- ...TeacherCongressDelegateCardController.java | 218 ++++++++++++++++++ .../views/platform/sys/conf/index.html | 12 + .../teachercongress/delegate/card/index.html | 84 +++++++ 7 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegateCardController.java create mode 100644 src/main/resources/views/platform/zhghh5/democratic/teachercongress/delegate/card/index.html diff --git a/src/main/java/com/budwk/app/sys/controller/SysConfController.java b/src/main/java/com/budwk/app/sys/controller/SysConfController.java index d6a0e76e..0a9964c9 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysConfController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysConfController.java @@ -105,7 +105,8 @@ public class SysConfController { if (conf == null || (!"AppHomeImg".equals(conf.getConfigKey()) && !"H5AppHomeImg".equals(conf.getConfigKey()) && !"AppFeaturedActivityImg".equals(conf.getConfigKey()) - && !"AppFestivalBenefitImg".equals(conf.getConfigKey()))) { + && !"AppFestivalBenefitImg".equals(conf.getConfigKey()) + && !"TeacherCongressCardBackImg".equals(conf.getConfigKey()))) { return; } String configValue = Strings.sNull(conf.getConfigValue()); @@ -145,6 +146,7 @@ public class SysConfController { ensureAppImageConfig("H5AppHomeImg", "移动端首页轮播图"); ensureAppImageConfig("AppFeaturedActivityImg", "精彩活动页顶部图片"); ensureAppImageConfig("AppFestivalBenefitImg", "节日福利页顶部图片"); + ensureAppImageConfig("TeacherCongressCardBackImg", "教代会代表证背景图片"); Cnd cnd = Cnd.NEW(); if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) { cnd.orderBy(pageOrderName, PageUtil.getOrder(pageOrderBy)); diff --git a/src/main/java/com/budwk/app/sys/controller/SysMenuController.java b/src/main/java/com/budwk/app/sys/controller/SysMenuController.java index 8c669d2a..0c41c193 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysMenuController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysMenuController.java @@ -323,7 +323,7 @@ public class SysMenuController { public Object enable(String menuId, HttpServletRequest req) { try { req.setAttribute("name", sysMenuService.fetch(menuId).getName()); - sysMenuService.update(org.nutz.dao.Chain.make("disabled", false), Cnd.where("id", "=", menuId)); + sysMenuService.update(Chain.make("disabled", false), Cnd.where("id", "=", menuId)); sysMenuService.clearCache(); sysUserService.clearCache(); sysRoleService.clearCache(); @@ -340,7 +340,7 @@ public class SysMenuController { public Object disable(String menuId, HttpServletRequest req) { try { req.setAttribute("name", sysMenuService.fetch(menuId).getName()); - sysMenuService.update(org.nutz.dao.Chain.make("disabled", true), Cnd.where("id", "=", menuId)); + sysMenuService.update(Chain.make("disabled", true), Cnd.where("id", "=", menuId)); sysMenuService.clearCache(); sysUserService.clearCache(); sysRoleService.clearCache(); @@ -398,7 +398,7 @@ public class SysMenuController { sysMenuService.execute(Sqls.create("update sys_menu set location=0 where platform = '%s'".formatted(platform))); for (String s : menuIds) { if (!Strings.isBlank(s)) { - sysMenuService.update(org.nutz.dao.Chain.make("location", i), Cnd.where("id", "=", s)); + sysMenuService.update(Chain.make("location", i), Cnd.where("id", "=", s)); i++; } } diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java index 75975cea..df36d760 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java @@ -208,7 +208,7 @@ public class SysFileServiceImpl extends BaseServiceImpl implements Sys if (!FileUtil.exist(file)) { throw new IOException("找不到存储的文件"); } -// CommonDownloadUtil.download(sys_file.getName(), IoUtil.readBytes(FileUtil.getInputStream(file)), response); + return IoUtil.readBytes(FileUtil.getInputStream(file)); } else if (sys_file.getEngine().equals(SysFileEngineTypeEnum.MINIO.getValue())) { return SysFileMinIoUtil.getFileBytes(sys_file.getBucket(), sys_file.getStoragePath()); } diff --git a/src/main/java/com/budwk/app/sys/utils/DevFileMinIoUtil.java b/src/main/java/com/budwk/app/sys/utils/DevFileMinIoUtil.java index 6857534e..b04a3b57 100644 --- a/src/main/java/com/budwk/app/sys/utils/DevFileMinIoUtil.java +++ b/src/main/java/com/budwk/app/sys/utils/DevFileMinIoUtil.java @@ -1,4 +1,4 @@ -///* +package com.budwk.app.sys.utils; ///* // * Copyright [2022] [https://www.xiaonuo.vip] // * // * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegateCardController.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegateCardController.java new file mode 100644 index 00000000..68610321 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegate/controller/TeacherCongressDelegateCardController.java @@ -0,0 +1,218 @@ +package com.budwk.app.zhgh.democratic.teachercongress.delegate.controller; + +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.result.Result; +import com.budwk.app.sys.models.Sys_config; +import com.budwk.app.sys.services.SysFileService; +import com.budwk.app.sys.services.SysRoleService; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.Teacher_congress_delegate; +import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation; +import com.budwk.app.zhgh.democratic.teachercongress.prepare.models.Teacher_congress_session; +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.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.json.Json; +import org.nutz.lang.Lang; +import org.nutz.lang.util.NutMap; +import org.nutz.mvc.annotation.At; +import org.nutz.mvc.annotation.Ok; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * 教代会代表证 + */ +@IocBean +@At("/platform/teacherCongress/delegate/card") +@Ok("json:full") +public class TeacherCongressDelegateCardController { + + private static final String CARD_BACK_IMG_CONFIG = "TeacherCongressCardBackImg"; + + @Inject + private Dao dao; + @Inject + private SysFileService sysFileService; + @Inject + private SysRoleService sysRoleService; + + @At("/h5") + @Ok("beetl:/platform/zhghh5/democratic/teachercongress/delegate/card/index.html") + @SaCheckLogin + public void h5Index() { + } + + @At + @SaCheckLogin + public Object isDb() { + Teacher_congress_session session = getCurrentSession(); + if (session == null) { + return Result.error("当前没有开启的教代会"); + } + Teacher_congress_delegate delegate = getCurrentDelegate(session.getId()); + if (delegate == null) { + return Result.error("抱歉," + getSessionTitle(session) + "您不是代表"); + } + return Result.success(); + } + + @At + @Ok("void") + @SaCheckLogin + public void showCard(HttpServletResponse response) throws IOException, WriterException { + NutMap cardInfo = getCardInfo(); + BufferedImage backgroundImage = readBackgroundImage(); + BufferedImage resultImage = new BufferedImage(backgroundImage.getWidth(), backgroundImage.getHeight(), BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = resultImage.createGraphics(); + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.drawImage(backgroundImage, 0, 0, null); + + Map hintMap = new EnumMap<>(EncodeHintType.class); + hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + NutMap qrData = new NutMap(); + qrData.setv("userId", SecurityUtil.getUserId()); + BitMatrix bitMatrix = new MultiFormatWriter().encode(Json.toJson(qrData), BarcodeFormat.QR_CODE, 200, 200, hintMap); + int qrCodeX = (resultImage.getWidth() - 200) / 2; + int qrCodeY = (resultImage.getHeight() - 200) / 2; + for (int x = 0; x < 200; x++) { + for (int y = 0; y < 200; y++) { + if (bitMatrix.get(x, y)) { + resultImage.setRGB(qrCodeX + x, qrCodeY + y, Color.BLACK.getRGB()); + } + } + } + + String title = StrUtil.blankToDefault(cardInfo.getString("title"), ""); + int titleY = (resultImage.getHeight() / 2) - 130; + Font titleFont = new Font("微软雅黑", Font.PLAIN, 26).deriveFont(Font.BOLD); + commonFont(resultImage, graphics, title, titleY, titleFont, Color.decode("#d1b930")); + + String nameText = StrUtil.blankToDefault(cardInfo.getString("nameText"), SecurityUtil.getUserUsername()); + int nameTextY = (resultImage.getHeight() / 2) + 150; + Font nameFont = new Font("楷体", Font.BOLD, 40); + commonFont(resultImage, graphics, nameText, nameTextY, nameFont, Color.WHITE); + + String dbtText = StrUtil.blankToDefault(cardInfo.getString("dbtText"), ""); + int dbtTextY = (resultImage.getHeight() / 2) + 200; + Font dbtFont = new Font("cursive", Font.BOLD, 24); + commonFont(resultImage, graphics, dbtText, dbtTextY, dbtFont, Color.WHITE); + + response.setContentType("image/png"); + ImageIO.write(resultImage, "png", response.getOutputStream()); + } + + private NutMap getCardInfo() throws IOException { + Teacher_congress_session session = getCurrentSession(); + if (session == null) { + throw new IOException("当前没有开启的教代会"); + } + Teacher_congress_delegate delegate = getCurrentDelegate(session.getId()); + if (delegate == null) { + throw new IOException("抱歉," + getSessionTitle(session) + "您不是代表"); + } + Teacher_congress_delegation delegation = null; + if (StrUtil.isNotBlank(delegate.getDelegationId())) { + delegation = dao.fetch(Teacher_congress_delegation.class, delegate.getDelegationId()); + } + NutMap nutMap = NutMap.NEW(); + nutMap.put("title", getSessionTitle(session)); + nutMap.put("nameText", StrUtil.blankToDefault(delegate.getUserName(), SecurityUtil.getUserUsername())); + nutMap.put("dbtText", delegation == null ? "" : StrUtil.blankToDefault(delegation.getName(), "")); + return nutMap; + } + + private Teacher_congress_session getCurrentSession() { + List sessions = dao.query(Teacher_congress_session.class, Cnd.where("enable", "=", true) + .desc("startDate")); + if (Lang.isEmpty(sessions)) { + return null; + } + return sessions.get(0); + } + + private Teacher_congress_delegate getCurrentDelegate(String sessionId) { + List roleIds = sysRoleService.getRoleIdsByCode(List.of( + RoleConstant.TEACHER_CONGRESS_DELEGATE_FORMAL, + RoleConstant.TEACHER_CONGRESS_DELEGATE_ATTENDANCE, + RoleConstant.TEACHER_CONGRESS_DELEGATE_SPECIALLY_INVITE + )); + return dao.fetch(Teacher_congress_delegate.class, Cnd.where("sessionId", "=", sessionId) + .and("userId", "=", SecurityUtil.getUserId()) + .and("roleId", "in", roleIds)); + } + + private String getSessionTitle(Teacher_congress_session session) { + String fullName = StrUtil.blankToDefault(session.getFullName(), ""); + if (fullName.contains("教代会")) { + return fullName; + } + return fullName + "教代会"; + } + + private BufferedImage readBackgroundImage() throws IOException { + String fileId = getCardBackImgFileId(); + if (StrUtil.isBlank(fileId)) { + throw new IOException("请先配置教代会代表证背景图片"); + } + byte[] bytes = sysFileService.download(fileId); + BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes)); + if (image == null) { + throw new IOException("教代会代表证背景图片读取失败"); + } + return image; + } + + private String getCardBackImgFileId() { + String configValue = getConfigValue(CARD_BACK_IMG_CONFIG); + if (StrUtil.isBlank(configValue)) { + configValue = getConfigValue("cardBackImg"); + } + if (StrUtil.isBlank(configValue)) { + return ""; + } + String firstValue = StrUtil.subBefore(configValue, ",", false); + if (firstValue.contains("id=")) { + firstValue = StrUtil.subAfter(firstValue, "id=", true); + firstValue = StrUtil.subBefore(firstValue, "&", false); + } + return URLDecoder.decode(firstValue, StandardCharsets.UTF_8); + } + + private String getConfigValue(String configKey) { + Sys_config config = dao.fetch(Sys_config.class, configKey); + return config == null ? "" : StrUtil.blankToDefault(config.getConfigValue(), ""); + } + + private void commonFont(BufferedImage resultImage, Graphics2D graphics, String text, int textY, Font font, Color color) { + graphics.setFont(font); + graphics.setColor(color); + FontMetrics fontMetrics = graphics.getFontMetrics(); + int textWidth = fontMetrics.stringWidth(text); + int textX = (resultImage.getWidth() - textWidth) / 2; + graphics.drawString(text, textX, textY); + } +} diff --git a/src/main/resources/views/platform/sys/conf/index.html b/src/main/resources/views/platform/sys/conf/index.html index bca250ff..3db83e5b 100644 --- a/src/main/resources/views/platform/sys/conf/index.html +++ b/src/main/resources/views/platform/sys/conf/index.html @@ -147,6 +147,18 @@ layout("/layouts/platform.html"){ > 最佳分辨率:750 x 280,节日福利页顶部图片 +