|
|
|
@@ -0,0 +1,311 @@
|
|
|
|
|
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";
|
|
|
|
|
private static final String APP_LOGO_CONFIG = "AppLogo";
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
private Dao dao;
|
|
|
|
|
@Inject
|
|
|
|
|
private SysFileService sysFileService;
|
|
|
|
|
@Inject
|
|
|
|
|
private SysRoleService sysRoleService;
|
|
|
|
|
|
|
|
|
|
@At("/h5")
|
|
|
|
|
@Ok("void")
|
|
|
|
|
@SaCheckLogin
|
|
|
|
|
public void h5Index(HttpServletResponse response) throws IOException, WriterException {
|
|
|
|
|
showCard(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
|
|
|
|
graphics.drawImage(backgroundImage, 0, 0, null);
|
|
|
|
|
|
|
|
|
|
BufferedImage logoImage = tintLogo(readConfigImage(APP_LOGO_CONFIG, "系统Logo读取失败"), Color.decode("#1476DF"));
|
|
|
|
|
int logoMaxWidth = resultImage.getWidth() * 55 / 100;
|
|
|
|
|
int logoMaxHeight = resultImage.getHeight() * 11 / 100;
|
|
|
|
|
double logoScale = Math.min((double) logoMaxWidth / logoImage.getWidth(), (double) logoMaxHeight / logoImage.getHeight());
|
|
|
|
|
int logoWidth = (int) (logoImage.getWidth() * logoScale);
|
|
|
|
|
int logoHeight = (int) (logoImage.getHeight() * logoScale);
|
|
|
|
|
int logoX = (resultImage.getWidth() - logoWidth) / 2;
|
|
|
|
|
int logoY = resultImage.getHeight() * 15 / 100;
|
|
|
|
|
graphics.drawImage(logoImage, logoX, logoY, logoWidth, logoHeight, null);
|
|
|
|
|
|
|
|
|
|
String title = StrUtil.blankToDefault(cardInfo.getString("title"), "");
|
|
|
|
|
int titleY = resultImage.getHeight() * 32 / 100;
|
|
|
|
|
Font titleFont = fitFontToWidth(graphics, new Font("微软雅黑", Font.BOLD, 70), title,
|
|
|
|
|
resultImage.getWidth() * 82 / 100, 36);
|
|
|
|
|
commonSpacedFont(resultImage, graphics, title, titleY, titleFont, Color.decode("#1476DF"), 6);
|
|
|
|
|
|
|
|
|
|
Map<EncodeHintType, Object> hintMap = new EnumMap<>(EncodeHintType.class);
|
|
|
|
|
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
|
|
|
|
|
hintMap.put(EncodeHintType.MARGIN, 0);
|
|
|
|
|
NutMap qrData = new NutMap();
|
|
|
|
|
qrData.setv("userId", SecurityUtil.getUserId());
|
|
|
|
|
int qrCodeSize = Math.min(330, resultImage.getWidth() * 31 / 100);
|
|
|
|
|
BitMatrix bitMatrix = new MultiFormatWriter().encode(Json.toJson(qrData), BarcodeFormat.QR_CODE,
|
|
|
|
|
qrCodeSize, qrCodeSize, hintMap);
|
|
|
|
|
int qrCodeX = (resultImage.getWidth() - qrCodeSize) / 2;
|
|
|
|
|
int qrCodeY = resultImage.getHeight() * 37 / 100;
|
|
|
|
|
int qrPanelPadding = 8;
|
|
|
|
|
graphics.setColor(Color.WHITE);
|
|
|
|
|
graphics.fillRoundRect(qrCodeX - qrPanelPadding, qrCodeY - qrPanelPadding,
|
|
|
|
|
qrCodeSize + qrPanelPadding * 2, qrCodeSize + qrPanelPadding * 2, 22, 22);
|
|
|
|
|
for (int x = 0; x < qrCodeSize; x++) {
|
|
|
|
|
for (int y = 0; y < qrCodeSize; y++) {
|
|
|
|
|
if (bitMatrix.get(x, y)) {
|
|
|
|
|
resultImage.setRGB(qrCodeX + x, qrCodeY + y, Color.BLACK.getRGB());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dividerY = qrCodeY + qrCodeSize + 60;
|
|
|
|
|
int dividerWidth = resultImage.getWidth() * 38 / 100;
|
|
|
|
|
graphics.setColor(Color.decode("#1476DF"));
|
|
|
|
|
graphics.fillRect((resultImage.getWidth() - dividerWidth) / 2, dividerY, dividerWidth, 2);
|
|
|
|
|
graphics.fillRect(resultImage.getWidth() / 2 - 5, dividerY - 4, 10, 10);
|
|
|
|
|
|
|
|
|
|
String nameText = StrUtil.blankToDefault(cardInfo.getString("nameText"), SecurityUtil.getUserUsername());
|
|
|
|
|
int nameTextY = dividerY + 115;
|
|
|
|
|
Font nameFont = fitFontToWidth(graphics, new Font("微软雅黑", Font.BOLD, 68), nameText,
|
|
|
|
|
resultImage.getWidth() * 70 / 100, 34);
|
|
|
|
|
commonSpacedFont(resultImage, graphics, nameText, nameTextY, nameFont, Color.decode("#1476DF"), 30);
|
|
|
|
|
|
|
|
|
|
String dbtText = StrUtil.blankToDefault(cardInfo.getString("dbtText"), "");
|
|
|
|
|
int dbtTextY = nameTextY + 82;
|
|
|
|
|
Font dbtFont = fitFontToWidth(graphics, new Font("微软雅黑", Font.PLAIN, 36), dbtText,
|
|
|
|
|
resultImage.getWidth() * 75 / 100, 22);
|
|
|
|
|
commonSpacedFont(resultImage, graphics, dbtText, dbtTextY, dbtFont, Color.decode("#333333"), 8);
|
|
|
|
|
|
|
|
|
|
response.setContentType("image/png");
|
|
|
|
|
ImageIO.write(resultImage, "png", response.getOutputStream());
|
|
|
|
|
graphics.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<Teacher_congress_session> 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<String> 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 BufferedImage readConfigImage(String configKey, String errorMessage) throws IOException {
|
|
|
|
|
String fileId = getFileIdFromConfigValue(getConfigValue(configKey));
|
|
|
|
|
if (StrUtil.isBlank(fileId)) {
|
|
|
|
|
throw new IOException("请先配置" + configKey);
|
|
|
|
|
}
|
|
|
|
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(sysFileService.download(fileId)));
|
|
|
|
|
if (image == null) {
|
|
|
|
|
throw new IOException(errorMessage);
|
|
|
|
|
}
|
|
|
|
|
return image;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BufferedImage tintLogo(BufferedImage source, Color color) {
|
|
|
|
|
BufferedImage tinted = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
|
|
|
|
for (int x = 0; x < source.getWidth(); x++) {
|
|
|
|
|
for (int y = 0; y < source.getHeight(); y++) {
|
|
|
|
|
Color sourceColor = new Color(source.getRGB(x, y), true);
|
|
|
|
|
int luminance = (sourceColor.getRed() * 299 + sourceColor.getGreen() * 587
|
|
|
|
|
+ sourceColor.getBlue() * 114) / 1000;
|
|
|
|
|
int maskAlpha = Math.max(0, Math.min(255, (250 - luminance) * 25));
|
|
|
|
|
int alpha = sourceColor.getAlpha() * maskAlpha / 255;
|
|
|
|
|
if (alpha > 0) {
|
|
|
|
|
tinted.setRGB(x, y, new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha).getRGB());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return tinted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String getCardBackImgFileId() {
|
|
|
|
|
String configValue = getConfigValue(CARD_BACK_IMG_CONFIG);
|
|
|
|
|
if (StrUtil.isBlank(configValue)) {
|
|
|
|
|
configValue = getConfigValue("cardBackImg");
|
|
|
|
|
}
|
|
|
|
|
if (StrUtil.isBlank(configValue)) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
return getFileIdFromConfigValue(configValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String getFileIdFromConfigValue(String configValue) {
|
|
|
|
|
String firstValue = StrUtil.subBefore(StrUtil.blankToDefault(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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void commonSpacedFont(BufferedImage resultImage, Graphics2D graphics, String text, int textY,
|
|
|
|
|
Font font, Color color, int letterSpacing) {
|
|
|
|
|
graphics.setFont(font);
|
|
|
|
|
graphics.setColor(color);
|
|
|
|
|
FontMetrics fontMetrics = graphics.getFontMetrics();
|
|
|
|
|
int textWidth = 0;
|
|
|
|
|
for (int i = 0; i < text.length(); i++) {
|
|
|
|
|
textWidth += fontMetrics.charWidth(text.charAt(i));
|
|
|
|
|
}
|
|
|
|
|
textWidth += Math.max(0, text.length() - 1) * letterSpacing;
|
|
|
|
|
int textX = (resultImage.getWidth() - textWidth) / 2;
|
|
|
|
|
for (int i = 0; i < text.length(); i++) {
|
|
|
|
|
char character = text.charAt(i);
|
|
|
|
|
graphics.drawString(String.valueOf(character), textX, textY);
|
|
|
|
|
textX += fontMetrics.charWidth(character) + letterSpacing;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Font fitFontToWidth(Graphics2D graphics, Font font, String text, int maxWidth, int minSize) {
|
|
|
|
|
Font result = font;
|
|
|
|
|
while (result.getSize() > minSize) {
|
|
|
|
|
graphics.setFont(result);
|
|
|
|
|
if (graphics.getFontMetrics().stringWidth(text) <= maxWidth) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
result = result.deriveFont((float) (result.getSize() - 2));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|