|
@@ -2,6 +2,7 @@ package com.fs.course.service.impl;
|
|
|
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
import com.fs.common.core.domain.R;
|
|
import com.fs.common.core.domain.R;
|
|
import com.fs.common.utils.DateUtils;
|
|
import com.fs.common.utils.DateUtils;
|
|
@@ -22,11 +23,28 @@ import com.fs.course.vo.*;
|
|
import com.fs.course.vo.newfs.FsUserCourseListVO;
|
|
import com.fs.course.vo.newfs.FsUserCourseListVO;
|
|
import com.fs.his.vo.OptionsVO;
|
|
import com.fs.his.vo.OptionsVO;
|
|
import com.fs.system.service.ISysConfigService;
|
|
import com.fs.system.service.ISysConfigService;
|
|
|
|
+import com.google.zxing.BarcodeFormat;
|
|
|
|
+import com.google.zxing.EncodeHintType;
|
|
|
|
+import com.google.zxing.client.j2se.MatrixToImageWriter;
|
|
|
|
+import com.google.zxing.common.BitMatrix;
|
|
|
|
+import com.google.zxing.qrcode.QRCodeWriter;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
+
|
|
|
|
+import javax.imageio.ImageIO;
|
|
|
|
+import java.awt.*;
|
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
+import java.net.URL;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
+import java.util.List;
|
|
|
|
|
|
/**
|
|
/**
|
|
* 课程Service业务层处理
|
|
* 课程Service业务层处理
|
|
@@ -60,6 +78,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
|
|
|
|
|
|
private static final String realLink = "/courseH5/pages/course/learning?course=";
|
|
private static final String realLink = "/courseH5/pages/course/learning?course=";
|
|
private static final String shortLink = "/courseH5/pages/course/learning?s=";
|
|
private static final String shortLink = "/courseH5/pages/course/learning?s=";
|
|
|
|
+ private static final String userRealLink = "/pages/user/users/becomeVIP?";
|
|
|
|
|
|
/**
|
|
/**
|
|
* 查询课程
|
|
* 查询课程
|
|
@@ -414,10 +433,227 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
|
|
String courseJson = JSON.toJSONString(courseMap);
|
|
String courseJson = JSON.toJSONString(courseMap);
|
|
|
|
|
|
link.setRealLink(realLink + courseJson);
|
|
link.setRealLink(realLink + courseJson);
|
|
|
|
+ String random = generateRandomString();
|
|
|
|
+ link.setLink(random);
|
|
|
|
+ link.setCreateTime(new Date());
|
|
|
|
+
|
|
|
|
+ //获取过期时间
|
|
|
|
+ Calendar calendar = getExpireDay(param, config, link.getCreateTime());
|
|
|
|
+ link.setUpdateTime(calendar.getTime());
|
|
|
|
+ int i = fsCourseLinkMapper.insertFsCourseLink(link);
|
|
|
|
+ if (i > 0){
|
|
|
|
+ String domainName = getDomainName(param.getCompanyUserId(), config);
|
|
|
|
+ String sortLink = domainName + shortLink + link.getLink();
|
|
|
|
+ return R.ok().put("url", sortLink).put("link", random);
|
|
|
|
+ }
|
|
|
|
+ return R.error("生成链接失败!");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String createCourseImageQR(String url, String backgroundImagePath, File file,
|
|
|
|
+ String outputFormat, String title, String duration) throws Exception {
|
|
|
|
+ // 读取并缩放背景图片
|
|
|
|
+ BufferedImage backgroundImage = createScaledBackgroundImage(backgroundImagePath);
|
|
|
|
+ int scaledWidth = backgroundImage.getWidth();
|
|
|
|
+ int scaledHeight = backgroundImage.getHeight();
|
|
|
|
+
|
|
|
|
+ // 创建最终的合成图片,确保底部区域有足够空间
|
|
|
|
+ int bottomHeight = 200; // 增加底部区域高度,确保内容完全显示
|
|
|
|
+ int totalHeight = scaledHeight + bottomHeight;
|
|
|
|
+ BufferedImage combined = new BufferedImage(scaledWidth, totalHeight, BufferedImage.TYPE_INT_ARGB);
|
|
|
|
+ Graphics2D graphics = initializeGraphics(combined);
|
|
|
|
+
|
|
|
|
+ // 绘制背景和底部白色区域
|
|
|
|
+ graphics.drawImage(backgroundImage, 0, 0, null);
|
|
|
|
+ graphics.setColor(Color.WHITE);
|
|
|
|
+ graphics.fillRect(0, scaledHeight, scaledWidth, bottomHeight);
|
|
|
|
+
|
|
|
|
+ // 绘制推荐区域(图片和文字)
|
|
|
|
+ drawRecommendationArea(graphics, file, scaledHeight);
|
|
|
|
+
|
|
|
|
+ // 绘制二维码
|
|
|
|
+ drawQRCode(graphics, url, scaledWidth, totalHeight);
|
|
|
|
+
|
|
|
|
+ // 绘制标题和时长
|
|
|
|
+ drawTitleAndDuration(graphics, title, duration, scaledHeight);
|
|
|
|
+
|
|
|
|
+ graphics.dispose();
|
|
|
|
+
|
|
|
|
+ // 转换为Base64
|
|
|
|
+ return convertToBase64(combined, outputFormat);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 读取并缩放背景图片
|
|
|
|
+ */
|
|
|
|
+ private BufferedImage createScaledBackgroundImage(String backgroundImagePath) throws IOException {
|
|
|
|
+ // 设置固定的目标尺寸
|
|
|
|
+ final int TARGET_WIDTH = 600;
|
|
|
|
+ final int TARGET_HEIGHT = 300; // 固定背景图片高度为300像素
|
|
|
|
+
|
|
|
|
+ URL imagePath = new URL(backgroundImagePath);
|
|
|
|
+ BufferedImage originalBackground = ImageIO.read(imagePath);
|
|
|
|
+
|
|
|
|
+ // 计算缩放比例,以高度为基准
|
|
|
|
+ double scale = (double) TARGET_HEIGHT / originalBackground.getHeight();
|
|
|
|
+ int scaledWidth = (int) (originalBackground.getWidth() * scale);
|
|
|
|
+
|
|
|
|
+ // 创建固定大小的背景图片
|
|
|
|
+ BufferedImage backgroundImage = new BufferedImage(TARGET_WIDTH, TARGET_HEIGHT, BufferedImage.TYPE_INT_ARGB);
|
|
|
|
+ Graphics2D g2d = backgroundImage.createGraphics();
|
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
|
|
|
+
|
|
|
|
+ // 计算居中位置
|
|
|
|
+ int x = (TARGET_WIDTH - scaledWidth) / 2;
|
|
|
|
+
|
|
|
|
+ // 绘制缩放后的图片
|
|
|
|
+ g2d.drawImage(originalBackground, x, 0, scaledWidth, TARGET_HEIGHT, null);
|
|
|
|
+ g2d.dispose();
|
|
|
|
+
|
|
|
|
+ return backgroundImage;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 初始化图形上下文
|
|
|
|
+ */
|
|
|
|
+ private Graphics2D initializeGraphics(BufferedImage combined) {
|
|
|
|
+ Graphics2D graphics = combined.createGraphics();
|
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
|
|
|
+ return graphics;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绘制推荐区域
|
|
|
|
+ */
|
|
|
|
+ private void drawRecommendationArea(Graphics2D graphics, File file, int scaledHeight) throws IOException {
|
|
|
|
+ // 读取并绘制推荐图片
|
|
|
|
+ BufferedImage referenceImg = ImageIO.read(file);
|
|
|
|
+ int refHeight = 40;
|
|
|
|
+ int refWidth = 40;
|
|
|
|
+ int refY = scaledHeight + 20;
|
|
|
|
+ int refX = 30;
|
|
|
|
+ graphics.drawImage(referenceImg, refX, refY, refWidth, refHeight, null);
|
|
|
|
+
|
|
|
|
+ // 设置并绘制推荐文字
|
|
|
|
+ Font recommendFont = new Font("微软雅黑", Font.BOLD, 16);
|
|
|
|
+ graphics.setFont(recommendFont);
|
|
|
|
+
|
|
|
|
+ // 绘制"云联融智"
|
|
|
|
+ int textX = refX + refWidth + 10;
|
|
|
|
+ int textY = refY + 25;
|
|
|
|
+ graphics.setColor(new Color(51, 51, 51));
|
|
|
|
+ graphics.drawString("云联融智", textX, textY);
|
|
|
|
+
|
|
|
|
+ // 绘制"为您推荐"
|
|
|
|
+ FontMetrics metrics = graphics.getFontMetrics(recommendFont);
|
|
|
|
+ int brandWidth = metrics.stringWidth("云联融智");
|
|
|
|
+ int textX1 = textX + brandWidth + 5;
|
|
|
|
+ graphics.setColor(new Color(102, 102, 102));
|
|
|
|
+ graphics.drawString("为您推荐", textX1, textY);
|
|
|
|
+// FontMetrics metrics = graphics.getFontMetrics(recommendFont);
|
|
|
|
+ int reWidth = metrics.stringWidth("为您推荐");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 生成二维码图片
|
|
|
|
+ */
|
|
|
|
+ private static BufferedImage generateQrCodeImage(String text, int width, int height) throws Exception {
|
|
|
|
+ QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
|
|
|
+ Map<EncodeHintType, Object> hints = new HashMap<>();
|
|
|
|
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
|
|
|
|
+ hints.put(EncodeHintType.MARGIN, 1);
|
|
|
|
+ BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
|
|
|
|
+ return MatrixToImageWriter.toBufferedImage(bitMatrix);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绘制二维码
|
|
|
|
+ */
|
|
|
|
+ private void drawQRCode(Graphics2D graphics, String url, int scaledWidth, int totalHeight) throws Exception {
|
|
|
|
+ BufferedImage qrCodeImage = generateQrCodeImage(url, 200, 200);
|
|
|
|
+ int qrSize = 100;
|
|
|
|
+ int qrMarginRight = 25;
|
|
|
|
+ int qrMarginBottom = 30;
|
|
|
|
+ int qrX = scaledWidth - qrSize - qrMarginRight;
|
|
|
|
+ int qrY = totalHeight - qrSize - qrMarginBottom;
|
|
|
|
+ graphics.drawImage(qrCodeImage, qrX, qrY, qrSize, qrSize, null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 绘制标题和时长
|
|
|
|
+ */
|
|
|
|
+ private void drawTitleAndDuration(Graphics2D graphics, String title, String duration, int scaledHeight) {
|
|
|
|
+ Font titleFont = new Font("微软雅黑", Font.BOLD, 18);
|
|
|
|
+ Font descFont = new Font("微软雅黑", Font.PLAIN, 14);
|
|
|
|
+
|
|
|
|
+ int contentX = 30;
|
|
|
|
+ int titleY = scaledHeight + 85;
|
|
|
|
+ int descY = titleY + 25;
|
|
|
|
+
|
|
|
|
+ graphics.setFont(titleFont);
|
|
|
|
+ graphics.setColor(new Color(51, 51, 51));
|
|
|
|
+ graphics.drawString(title, contentX, titleY);
|
|
|
|
+
|
|
|
|
+ graphics.setFont(descFont);
|
|
|
|
+ graphics.setColor(new Color(102, 102, 102));
|
|
|
|
+ if(StringUtils.isNotBlank(duration)){
|
|
|
|
+ // 将秒转化为分钟
|
|
|
|
+ BigDecimal bigDecimal = new BigDecimal(duration);
|
|
|
|
+ bigDecimal = bigDecimal.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
|
|
|
|
+ String minutes = bigDecimal.toString();
|
|
|
|
+ graphics.drawString("播放时长:" + minutes + "分钟", contentX, descY);
|
|
|
|
+ } else {
|
|
|
|
+ graphics.drawString("播放时长:" + "暂无", contentX, descY);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 将图片转换为Base64字符串
|
|
|
|
+ */
|
|
|
|
+ private String convertToBase64(BufferedImage image, String outputFormat) throws IOException {
|
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
|
+ ImageIO.write(image, outputFormat, outputStream);
|
|
|
|
+ byte[] imageBytes = outputStream.toByteArray();
|
|
|
|
+ String s = Base64.getEncoder().encodeToString(imageBytes);
|
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
|
+ if(StringUtils.isNotEmpty(s)){
|
|
|
|
+ result.append("data:image/png;base64,").append(s);
|
|
|
|
+ }
|
|
|
|
+ return result.toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public R createCourseMemberSortLink(FsCourseLinkCreateParam param) {
|
|
|
|
+ // todo 需要调整路径
|
|
|
|
+ String json = configService.selectConfigByKey("company.user");
|
|
|
|
+ JSONObject object = JSON.parseObject(json);
|
|
|
|
+ String realLinkDomainName = object.get("realLinkDomainName").toString();
|
|
|
|
+ Integer linkExpireTime = Integer.parseInt(object.get("linkExpireTime").toString());
|
|
|
|
+
|
|
|
|
+ //新增链接表信息
|
|
|
|
+ FsCourseLink link = new FsCourseLink();
|
|
|
|
+ BeanUtils.copyProperties(param, link);
|
|
|
|
+ link.setLinkType(0);
|
|
|
|
+ link.setIsRoom(0);
|
|
|
|
+
|
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
|
+ BeanUtils.copyProperties(link, courseMap);
|
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
|
+
|
|
|
|
+ link.setRealLink(userRealLink + courseJson);
|
|
link.setLink(generateRandomString());
|
|
link.setLink(generateRandomString());
|
|
link.setCreateTime(new Date());
|
|
link.setCreateTime(new Date());
|
|
|
|
|
|
//获取过期时间
|
|
//获取过期时间
|
|
|
|
+ CourseConfig config = new CourseConfig();
|
|
|
|
+ config.setRealLinkDomainName(realLinkDomainName);
|
|
|
|
+ config.setVideoLinkExpireDate(linkExpireTime);
|
|
Calendar calendar = getExpireDay(param, config, link.getCreateTime());
|
|
Calendar calendar = getExpireDay(param, config, link.getCreateTime());
|
|
link.setUpdateTime(calendar.getTime());
|
|
link.setUpdateTime(calendar.getTime());
|
|
int i = fsCourseLinkMapper.insertFsCourseLink(link);
|
|
int i = fsCourseLinkMapper.insertFsCourseLink(link);
|