feat:核销海报

This commit is contained in:
dzc committed 2025-12-04 13:13:22 +08:00
1 parent a4cddba118
commit 2b34989bfc
11 files changed
+241 -4

No files matched your search

+6
View File
@@ -128,6 +128,12 @@
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.trade.controller.app.brokerage;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.BrokerageUserCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageCodeDO;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageCodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "用户 APP - 分销码")
@RestController
@RequestMapping("/trade/brokerage-code")
@Validated
@Slf4j
public class AppBrokerageCodeController {
@Resource
private BrokerageCodeService brokerageCodeService;
@GetMapping("/get")
@Operation(summary = "获取用户的分销码")
public CommonResult<BrokerageCodeDO> createBrokerageUser() {
return success(brokerageCodeService.getUserBrokerageCode(getLoginUserId()));
}
}
@@ -43,4 +43,9 @@ public class BrokerageCodeDO extends BaseDO {
*/
private String code;
/**
* 分销链接
*/
private String url;
}
@@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.trade.framework.web.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("brokerage")
@Data
public class TradeBrokerageConfiguration {
/**
* app下载地址
*/
private String downloadUrl;
}
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.trade.service.brokerage;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.code.BrokerageCodePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.code.BrokerageCodeSaveReqVO;
@@ -37,4 +37,8 @@ public interface BrokerageCodeService {
*/
PageResult<BrokerageCodeDO> getBrokerageCodePage(BrokerageCodePageReqVO pageReqVO);
/**
* 获取用户的分销码
*/
BrokerageCodeDO getUserBrokerageCode(Long userId);
}
@@ -1,14 +1,24 @@
package cn.iocoder.yudao.module.trade.service.brokerage;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.code.BrokerageCodePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.code.BrokerageCodeSaveReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageCodeDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageCodeMapper;
import cn.iocoder.yudao.module.trade.framework.web.config.TradeBrokerageConfiguration;
import cn.iocoder.yudao.module.trade.util.PosterPicGen;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.io.FileInputStream;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -18,13 +28,20 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_C
/**
* 分销码 Service 实现类
*/
@Slf4j
@Service
@Validated
public class BrokerageCodeServiceImpl implements BrokerageCodeService {
@Resource
private TradeBrokerageConfiguration brokerageConfiguration;
@Resource
private BrokerageCodeMapper brokerageCodeMapper;
@Resource
private FileApi fileApi;
@Override
public Long createBrokerageCode(BrokerageCodeSaveReqVO createReqVO) {
// 插入
@@ -67,4 +84,51 @@ public class BrokerageCodeServiceImpl implements BrokerageCodeService {
return brokerageCodeMapper.selectPage(pageReqVO);
}
/**
* 获取用户的分销码
*/
@Override
public BrokerageCodeDO getUserBrokerageCode(Long userId) {
// 查询用户有没有分销码
BrokerageCodeDO brokerageCode = brokerageCodeMapper.selectOne(BrokerageCodeDO::getMemberId, userId);
if (ObjectUtil.isNotEmpty(brokerageCode)) return brokerageCode;
return generateCode(userId);
}
/**
* 生成用户的分销码
*/
private BrokerageCodeDO generateCode(Long userId) {
boolean isRepet = true;
String code = null;
while (isRepet) {
code = RandomUtil.randomNumbers(8);
BrokerageCodeDO brokerageCode = brokerageCodeMapper.selectOne(BrokerageCodeDO::getCode, code);
isRepet = ObjectUtil.isNotEmpty(brokerageCode);
}
String url = generatePosterUrl(code, userId);
BrokerageCodeDO saveObj = BrokerageCodeDO.builder().memberId(userId).code(code).posterPic(generatePosterPic(code, url)).url(url).build();
brokerageCodeMapper.insert(saveObj);
return saveObj;
}
/**
* 生成分销海报图
*/
private String generatePosterPic(String code, String qrCodeUrl) {
try {
byte[] fileInputStream = new PosterPicGen().setCode(code).setQrCodeUrl(qrCodeUrl).gen();
String file = fileApi.createFile(fileInputStream);
return file;
} catch (Exception e) {
e.printStackTrace();
log.error("|生成分销海报图|,意外的错误error:|{}|", e.getMessage());
}
return "";
}
private String generatePosterUrl(String code, Long userId) {
return brokerageConfiguration.getDownloadUrl() + "?code=" + code + "&userId=" + userId;
}
}
@@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.trade.util;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
/**
* 生成海报
*/
@Slf4j
public class PosterPicGen {
private Graphics2D graphics;
private int pageWidth = 375;
private int pageHeight = 700;
private String title = "荷塘月色";
private String description = "来自缘分的相遇";
private String code = "";
private String qrCodeUrl = "";
private String bottomDescription = "微信无法打开的用户请更换为其他方式扫码";
public PosterPicGen setCode(String code) {
this.code = code;
return this;
}
public PosterPicGen
setQrCodeUrl(String qrCodeUrl) {
this.qrCodeUrl = qrCodeUrl;
return this;
}
public byte[] gen() {
try {
BufferedImage baseImage = ImageIO.read(ResourceUtil.getResource("image/base-poster.png"));
baseImage.getScaledInstance(this.pageWidth, this.pageHeight, Image.SCALE_SMOOTH);
graphics = baseImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 设置 title
graphics.setFont(new Font("PingFang SC, Microsoft YaHei, sans-serif", Font.BOLD, 18));
graphics.setColor(Color.decode("#333333"));
graphics.drawString(this.title, 84, 60);
// 设置 description
graphics.setFont(new Font("PingFang SC, Microsoft YaHei, sans-serif", Font.PLAIN, 14));
graphics.setColor(Color.decode("#666666"));
graphics.drawString(this.description, 84, 85);
// 设置 code
writeCenterText(new Font("Arial, sans-serif", Font.BOLD, 28), this.code, 270);
// 设置 bottomDescription
writeCenterText(new Font("PingFang SC, sans-serif", Font.PLAIN, 12), this.bottomDescription, 640);
// 插入二维码
if (ObjectUtil.isNotEmpty(this.qrCodeUrl)) {
int qrWidth = 150;
int qrHeight = 150;
BufferedImage generate = QrCodeUtil.generate(this.qrCodeUrl, qrWidth, qrHeight);
int x = (baseImage.getWidth() - qrWidth) / 2;
int y = 350;
graphics.drawImage(generate, x, y, qrWidth, qrHeight, null);
}
graphics.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(baseImage, "png", baos);
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
log.error("|生成海报|,意外的错误error:|{}|", e.getMessage());
return null;
}
}
private void writeCenterText(Font font, String text, int y) {
graphics.setFont(font);
FontMetrics metrics = graphics.getFontMetrics(font);
int x = (this.pageWidth - metrics.stringWidth(text)) / 2;
graphics.drawString(text, x, y);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

@@ -243,4 +243,7 @@ al-config:
access-key-secret: EVXWFTS2SB6UkjUVFLhTDBfIvIXgI3
face-scene-id: 1000016045
face-verify-callback-url: https://rj4jv5rwqs.by.takin.cc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
brokerage:
download-url: https://www.baidu.com
@@ -265,4 +265,7 @@ al-config:
access-key-secret: EVXWFTS2SB6UkjUVFLhTDBfIvIXgI3
face-scene-id: 1000016045
face-verify-callback-url: https://rj4jv5rwqs.by.takin.cc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
brokerage:
download-url: https://www.baidu.com
@@ -255,4 +255,7 @@ al-config:
access-key-secret: EVXWFTS2SB6UkjUVFLhTDBfIvIXgI3
face-scene-id: 1000016045
face-verify-callback-url: https://rj4jv5rwqs.by.takin.cc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
face-verify-callback-token: uVuYOzoiQBWPp4A5kPgTddZlKWoSQlzc
brokerage:
download-url: https://www.baidu.com