Commit 2ec8939b authored by daniel's avatar daniel

fix(wechat): 微信小程序工具

parent 1a6681e7
package com.dlbridge.jadp.plugins.cmpt.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "小程序WxCodeParam",description = "小程序WxCodeParam")
public class WxCodeParam {
@ApiModelProperty(value = "宽度")
Integer width=1280;
@ApiModelProperty(value = "跳转的页面",allowEmptyValue = true)
String path="pages/index/index";
@ApiModelProperty(value = "正式版为 release,体验版为 trial,开发版为 develop" )
String envVersion="release";
@ApiModelProperty(value = "赛客id" )
Integer cmptId;
}
\ No newline at end of file
......@@ -7,8 +7,6 @@ import com.dlbridge.jadp.plugins.cmpt.utils.GpsUtil;
import com.dlbridge.jadp.plugins.cmpt.utils.QRCodeUtil;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
......@@ -27,7 +25,6 @@ import java.util.stream.Collectors;
@Service
public class RichTextService {
private static final Logger log = LoggerFactory.getLogger(RichTextService.class);
@Resource
RichTextMapper richTextMapper;
......
......@@ -2,10 +2,13 @@ package com.dlbridge.jadp.plugins.cmpt.service;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.dlbridge.jadp.plugins.cmpt.bean.Partner;
import com.dlbridge.jadp.plugins.cmpt.bean.UrlLinkParam;
import com.dlbridge.jadp.plugins.cmpt.bean.UrlSchemeParam;
import com.dlbridge.jadp.plugins.cmpt.bean.WxCodeParam;
import com.dlbridge.jadp.plugins.cmpt.dao.model.Cmpt;
import com.dlbridge.jadp.plugins.cmpt.utils.HttpUtil;
import com.dlbridge.jadp.plugins.cmpt.utils.json.JsonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
......@@ -16,6 +19,14 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
......@@ -36,18 +47,23 @@ public class WechatMiniService {
/**
* 获取url link
*/
public final static String GENERATE_URL_LINK = "https://api.weixin.qq.com/wxa/generate_urllink";
public final static String GENERATE_URL_LINK = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=";
/**
* 获取 SCHEME
*/
public final static String GENERATE_SCHEME = "https://api.weixin.qq.com/wxa/generatescheme";
public final static String GENERATE_SCHEME = "https://api.weixin.qq.com/wxa/generatescheme?access_token=";
/**
* 获取小程序码
*/
public final static String GET_WXACODE = "https://api.weixin.qq.com/wxa/getwxacode?access_token=";
/**
* 重新获取小程序access_token
*
* @param partnerId 小程序组委会ID
* @return access_token
*/
public String getMicroAccessToken(String partnerId){
public String getMicroAccessToken(String partnerId) {
String accessToken = (String) valueOperations.get("micro_gk:access_token_" + partnerId);
log.info("===== redis access_token:" + accessToken);
if (StringUtils.isEmpty(accessToken)) {
......@@ -57,8 +73,10 @@ public class WechatMiniService {
log.info("URL:" + aokenUrl + ", 返回结果:" + json);
Map<String, Object> resultMap = JsonUtil.fromJson(json, new TypeReference<Map<String, Object>>() {
});
accessToken = resultMap.get("access_token").toString();
String expiresIn = resultMap.get("expires_in").toString();
accessToken = resultMap.get("access_token")
.toString();
String expiresIn = resultMap.get("expires_in")
.toString();
valueOperations.set("micro_gk:access_token_" + partnerId, accessToken, Long.parseLong(expiresIn), TimeUnit.SECONDS);
}
return accessToken;
......@@ -66,11 +84,12 @@ public class WechatMiniService {
/**
* 重置小程序access_token
*
* @param partnerId 小程序组委会ID
* @return access_token
*/
public String resetMicroAccessToken(String partnerId){
public String resetMicroAccessToken(String partnerId) {
String accessToken = "";
Partner partner = partnerService.getPartnerById(partnerId);
String aokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + partner.getAppId() + "&secret=" + partner.getAppSecret();
......@@ -78,8 +97,10 @@ public class WechatMiniService {
log.info("URL:" + aokenUrl + ", 重新获取小程序token返回结果:" + json);
Map<String, Object> resultMap = JsonUtil.fromJson(json, new TypeReference<Map<String, Object>>() {
});
accessToken = resultMap.get("access_token").toString();
String expiresIn = resultMap.get("expires_in").toString();
accessToken = resultMap.get("access_token")
.toString();
String expiresIn = resultMap.get("expires_in")
.toString();
valueOperations.set("micro_gk:access_token_" + partnerId, accessToken, Long.parseLong(expiresIn), TimeUnit.SECONDS);
return accessToken;
}
......@@ -106,15 +127,17 @@ public class WechatMiniService {
String jsonParams = JsonUtil.toJson(params);
// 发起请求
// String response = HttpUtil.post(GENERATE_URL_LINK, accessToken, "application/json", jsonParams);
HttpRequest request = HttpRequest.post(GENERATE_URL_LINK + "?access_token=" + accessToken)
HttpRequest request = HttpRequest.post(GENERATE_URL_LINK + accessToken)
.header("Content-Type", "application/json")
.body(jsonParams);
String response = request.execute().body();
String response = request.execute()
.body();
// 解析返回结果
cn.hutool.json.JSONObject result = JSONUtil.parseObj(response);
if (result.containsKey("url_link")) {
return Result.success().setContent(result.getStr("url_link"));
return Result.success()
.setContent(result.getStr("url_link"));
} else {
log.error("Failed to generate UrlLink: {}", result);
return null;
......@@ -128,7 +151,7 @@ public class WechatMiniService {
}
/**
* @param urlSchemeParam 参数
* @param urlSchemeParam 参数
* @return string
* @link <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/generateUrlLink.html">...</a>
*/
......@@ -152,15 +175,17 @@ public class WechatMiniService {
// 输出调试信息,确认请求的 JSON 格式
System.out.println("Sending JSON: " + jsonParams);
// 发起请求
HttpRequest request = HttpRequest.post(GENERATE_SCHEME + "?access_token=" + accessToken)
HttpRequest request = HttpRequest.post(GENERATE_SCHEME + accessToken)
.header("Content-Type", "application/json")
.body(jsonParams);
String response = request.execute().body();
String response = request.execute()
.body();
// 解析返回结果
cn.hutool.json.JSONObject result = JSONUtil.parseObj(response);
if (result.containsKey("openlink")) {
return Result.success().setContent(result.getStr("openlink"));
return Result.success()
.setContent(result.getStr("openlink"));
} else {
log.error("Failed to generate UrlSchema: {}", result);
return null;
......@@ -172,4 +197,115 @@ public class WechatMiniService {
return null;
}
}
/**
* @param wxCodeParam 参数
* @link <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getQRCode.html">...</a>
*/
public void generateQrCode(WxCodeParam wxCodeParam, HttpServletResponse response) {
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=qrcode.jpg");
String partnerId = cmptService.getPartnerId(wxCodeParam.getCmptId());
try {
// 获取 access_token
String accessToken = getMicroAccessToken(partnerId);
// 构造请求参数
cn.hutool.json.JSONObject obj = new cn.hutool.json.JSONObject();
obj.set("path", wxCodeParam.getPath());
obj.set("width", 1280);
obj.set("env_version", wxCodeParam.getEnvVersion());
// 发起请求
HttpResponse resp = HttpRequest.post(GET_WXACODE + accessToken)
.header("Content-Type", "application/json")
.body(obj.toString())
.execute();
String contentType = resp.header("Content-Type");
byte[] bytes = resp.bodyBytes();
if (contentType != null && contentType.contains("json")) {
// 微信返回错误 JSON
String errJson = new String(bytes, StandardCharsets.UTF_8);
log.error("===== 获取小程序码失败: {}", errJson);
resetMicroAccessToken(partnerId);
response.getWriter()
.write(errJson);
return;
}
Cmpt cmpt = cmptService.queryCmptBycmptId(wxCodeParam.getCmptId());
// 读取二维码图片
BufferedImage qrCode = ImageIO.read(new ByteArrayInputStream(bytes));
BufferedImage qr = ImageIO.read(new URL(cmpt.getLogo()));
// 如果你要处理二维码(比如叠加 logo)
overlapImageHDPlus(qrCode, qr, response);
} catch (Exception e) {
log.error("===== 下载小程序二维码异常:{}", e.getMessage(), e);
resetMicroAccessToken(partnerId);
}
}
private void overlapImageHDPlus(BufferedImage bg, BufferedImage qrCodeIps, HttpServletResponse response) throws Exception {
// 进一步增大背景图片尺寸
BufferedImage background = resizeImageHighQuality(2400, 2400, bg);
// 绘制logo
BufferedImage qrCode = resizeImageHighQuality(1120, 1120, qrCodeIps);
qrCode = converToRadius(qrCode, 1120, 1120);
qrCode.createGraphics()
.setBackground(Color.WHITE);
Graphics2D g = background.createGraphics();
g.drawImage(qrCode, 640, 640, qrCode.getWidth(), qrCode.getHeight(), null);
// 调整二维码位置
g.dispose();
ImageIO.write(background, "jpg", response.getOutputStream());
}
/**
* 使用高质量算法调整图片尺寸
*
* @param x 目标宽度
* @param y 目标高度
* @param bfi 原始图片
* @return 调整尺寸后的图片
*/
private BufferedImage resizeImageHighQuality(int x, int y, BufferedImage bfi) {
BufferedImage bufferedImage = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
// 设置高质量的渲染提示
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(bfi, 0, 0, x, y, null);
g.dispose();
return bufferedImage;
}
/**
* 图片圆角
*
* @param image 图片
* @param width 宽度
* @param height 高度
* @return 圆角图片
*/
private static BufferedImage converToRadius(BufferedImage image, int width, int height) {
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fill(new RoundRectangle2D.Float(0, 0, width, height, width, height));
g2.setComposite(AlphaComposite.SrcAtop);
g2.drawImage(image, 0, 0, null);
g2.dispose();
return output;
}
}
......@@ -43,13 +43,6 @@ public class ResourcesController {
private WechatMiniService wechatMiniService;
/**
* 小程序码生成
* @param cmptid 赛事ID
......
package com.dlbridge.jadp.plugins.cmpt.web;
import com.dlbridge.jadp.common.interceptor.PageView;
import com.dlbridge.jadp.plugins.cmpt.bean.UrlLinkParam;
import com.dlbridge.jadp.plugins.cmpt.bean.UrlSchemeParam;
import com.dlbridge.jadp.plugins.cmpt.bean.WxCodeParam;
import com.dlbridge.jadp.plugins.cmpt.dao.model.Cmpt;
import com.dlbridge.jadp.plugins.cmpt.service.CmptService;
import com.dlbridge.jadp.plugins.cmpt.service.WechatMiniService;
import com.neusoft.www.utils.web.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
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 org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
@Controller
@RestController
......@@ -23,6 +26,20 @@ import javax.annotation.Resource;
public class WechatMiniController {
@Resource
private WechatMiniService wechatMiniService;
@Resource
private CmptService cmptService;
@RequestMapping("index/{cmptId}")
@PageView
public ModelAndView index(@PathVariable("cmptId") Integer cmptId, ModelAndView mv) {
Cmpt cmpt = cmptService.queryCmptBycmptId(cmptId);
mv.addObject("cmpt", cmpt);
mv.setViewName("cmpt/wechat");
return mv;
}
/**
* @param urlLinkParam 参数
* @return string
......@@ -43,4 +60,14 @@ public class WechatMiniController {
public Result<String> generateUrlScheme(@RequestBody UrlSchemeParam urlSchemeParam) {
return wechatMiniService.generateUrlScheme(urlSchemeParam);
}
/**
* @param wxCodeParam 参数
* @return string
* @link <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/generateUrlLink.html">...</a>
*/
@GetMapping("/wxQrCode")
@ApiOperation(value = "生成小程序二维码",notes = "生成小程序二维码")
public void generateQrcode(WxCodeParam wxCodeParam, HttpServletResponse response) {
wechatMiniService.generateQrCode(wxCodeParam,response);
}
}
package com.dlbridge.jadp.plugins.common;
import cn.hutool.http.HttpUtil;
import com.dlbridge.jadp.plugins.cmpt.dao.model.Cmpt;
import com.dlbridge.jadp.plugins.cmpt.service.CmptService;
import com.dlbridge.jadp.plugins.cmpt.utils.AliOssUtil;
import com.neusoft.www.utils.web.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.neusoft.www.utils.web.Result;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
......@@ -25,8 +21,7 @@ import java.util.*;
@Slf4j
public class CommonController {
@Resource
private CmptService cmptService;
// 定义允许上传的文件类型及其对应的魔数
private static final Map<String, byte[]> ALLOWED_FILE_TYPES = new HashMap<>();
......@@ -102,6 +97,31 @@ public class CommonController {
return Result.fail().setMsg("文件上传失败");
}
@PostMapping("/uploadWithName")
@ApiOperation(value = "文件上传+文件名")
public Result<?> uploadWithName(@RequestPart @RequestParam("file") MultipartFile file, @RequestParam(value = "folder", defaultValue = "") String folder,@RequestParam(value = "fileName") String fileName) {
try {
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;
// 检查文件名中是否包含点号
int lastIndex = originalFilename.lastIndexOf(".");
if (lastIndex == -1) {
// 如果没有后缀名,直接返回报错信息
return Result.fail().setMsg("上传的文件没有后缀名,请检查文件");
}
String objectName = fileName;
if (!Objects.equals(folder, "")) {
objectName = folder + "/" + fileName;
}
String filePath = aliOssUtil.upload(file.getBytes(), objectName);
log.info("文件上传名: {} 保存的文件名:{}", originalFilename,filePath);
return Result.success().setContent(filePath);
} catch (IOException e) {
log.error("文件上传失败:{}", e);
}
return Result.fail().setMsg("文件上传失败");
}
@PostMapping("/uploadClaimer")
@ApiOperation(value = "参赛声明上传")
public Result<?> uploadClaimer(@RequestPart @RequestParam("file") MultipartFile file,@RequestParam(value = "gCmptId") Integer gCmptId) {
......
......@@ -404,6 +404,13 @@
<span class="menu-txt">领物导航</span>
</a>
</li>
<li class="wechat menu-sub-item">
<a
href="/gk/wechatMini/index/${cmpt.cmptId}?uId=${uId}&cId=${cId}&timestamp=${timestamp}&sign=${sign}">
<span class="left-arrow"></span>
<span class="menu-txt">微信小程序</span>
</a>
</li>
</ul>
</li>
</ul>
......@@ -466,7 +473,7 @@
var classes = ['cmptSetting', 'nameList', 'manual', 'contact', 'manage',
'route', 'road', 'equip', 'photo', 'news', 'cert', 'data',
'banner', 'score', 'auto', 'scan', 'review', 'customer', 'rule',
'outter-link', 'sure-letter', 'rankSetting', 'service', "module", "swiper", "bag", "invoice", "markNoSelect", "feedback", "markNoRule", "gunRule", "disClaimer", "location"];
'outter-link', 'sure-letter', 'rankSetting', 'service', "module", "swiper", "bag", "invoice", "markNoSelect", "feedback", "markNoRule", "gunRule", "disClaimer", "location", "wechat"];
let url = document.location.href;
var done = false;
for (var i in classes) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment