11
This commit is contained in:
1 parent
aa9963bfe1
commit
0f9d061f4e
8 files changed
+346
-38
No files matched your search
+56
-4
@@ -38,6 +38,8 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_C
|
||||
@Validated
|
||||
public class BrokerageCodeServiceImpl implements BrokerageCodeService {
|
||||
|
||||
private static final String POSTER_VERSION_MARKER = "#poster-v2-hd";
|
||||
|
||||
private static final String BROKERAGE_CODE_GENERATE_LOCK_KEY = "trade:brokerage-code:generate:";
|
||||
|
||||
@Resource
|
||||
@@ -125,10 +127,56 @@ public class BrokerageCodeServiceImpl implements BrokerageCodeService {
|
||||
public BrokerageCodeDO getUserBrokerageCode(Long userId) {
|
||||
// 查询用户有没有分销码
|
||||
BrokerageCodeDO brokerageCode = brokerageCodeMapper.selectOne(BrokerageCodeDO::getMemberId, userId);
|
||||
if (ObjectUtil.isNotEmpty(brokerageCode)) return brokerageCode;
|
||||
if (ObjectUtil.isNotEmpty(brokerageCode)) {
|
||||
return refreshLegacyPoster(userId, brokerageCode);
|
||||
}
|
||||
return generateCode(userId);
|
||||
}
|
||||
|
||||
private BrokerageCodeDO refreshLegacyPoster(Long userId, BrokerageCodeDO brokerageCode) {
|
||||
if (isLatestPoster(brokerageCode.getPosterPic())) {
|
||||
return brokerageCode;
|
||||
}
|
||||
RLock lock = redissonClient.getLock(BROKERAGE_CODE_GENERATE_LOCK_KEY + userId);
|
||||
lock.lock();
|
||||
try {
|
||||
BrokerageCodeDO currentCode = brokerageCodeMapper.selectOne(BrokerageCodeDO::getMemberId, userId);
|
||||
if (ObjectUtil.isEmpty(currentCode) || isLatestPoster(currentCode.getPosterPic())) {
|
||||
return currentCode;
|
||||
}
|
||||
String posterCode = currentCode.getCode();
|
||||
String userAvatar = "";
|
||||
MemberUserRespDTO user = memberUserApi.getUser(userId);
|
||||
if (ObjectUtil.isNotEmpty(user)) {
|
||||
userAvatar = user.getAvatar();
|
||||
if (ObjectUtil.isNotEmpty(user.getUserCode())) {
|
||||
posterCode = user.getUserCode();
|
||||
}
|
||||
}
|
||||
String posterUrl = ObjectUtil.isNotEmpty(currentCode.getUrl())
|
||||
? currentCode.getUrl() : generatePosterUrl(currentCode.getCode(), userId);
|
||||
String posterPic = generatePosterPic(userAvatar, posterCode, posterUrl);
|
||||
if (ObjectUtil.isEmpty(posterPic)) {
|
||||
return currentCode;
|
||||
}
|
||||
BrokerageCodeDO updateObj = BrokerageCodeDO.builder()
|
||||
.id(currentCode.getId())
|
||||
.posterPic(posterPic)
|
||||
.url(posterUrl)
|
||||
.build();
|
||||
brokerageCodeMapper.updateById(updateObj);
|
||||
currentCode.setPosterPic(posterPic);
|
||||
currentCode.setUrl(posterUrl);
|
||||
return currentCode;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLatestPoster(String posterPic) {
|
||||
return ObjectUtil.isNotEmpty(posterPic) && posterPic.endsWith(POSTER_VERSION_MARKER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用户的分销码
|
||||
*/
|
||||
@@ -158,13 +206,17 @@ public class BrokerageCodeServiceImpl implements BrokerageCodeService {
|
||||
|
||||
// 查询用户获取用户头像
|
||||
String userAvatar = "";
|
||||
String posterCode = code;
|
||||
MemberUserRespDTO user = memberUserApi.getUser(userId);
|
||||
if (ObjectUtil.isNotEmpty(user) && ObjectUtil.isNotEmpty(user)) {
|
||||
if (ObjectUtil.isNotEmpty(user)) {
|
||||
userAvatar = user.getAvatar();
|
||||
if (ObjectUtil.isNotEmpty(user.getUserCode())) {
|
||||
posterCode = user.getUserCode();
|
||||
}
|
||||
}
|
||||
|
||||
String url = generatePosterUrl(code, userId);
|
||||
BrokerageCodeDO saveObj = BrokerageCodeDO.builder().memberId(userId).code(code).posterPic(generatePosterPic(userAvatar, code, url)).url(url).build();
|
||||
BrokerageCodeDO saveObj = BrokerageCodeDO.builder().memberId(userId).code(code).posterPic(generatePosterPic(userAvatar, posterCode, url)).url(url).build();
|
||||
brokerageCodeMapper.insert(saveObj);
|
||||
return saveObj;
|
||||
}
|
||||
@@ -176,7 +228,7 @@ public class BrokerageCodeServiceImpl implements BrokerageCodeService {
|
||||
try {
|
||||
byte[] fileInputStream = new PosterPicGen().setUserAvatar(userAvatar).setCode(code).setQrCodeUrl(qrCodeUrl).gen();
|
||||
String file = fileApi.createFile(fileInputStream);
|
||||
return file;
|
||||
return file + POSTER_VERSION_MARKER;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("|生成分销海报图|,意外的错误error:|{}|", e.getMessage());
|
||||
|
||||
+105
-24
@@ -10,6 +10,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -23,6 +25,8 @@ import java.io.InputStream;
|
||||
@Slf4j
|
||||
public class PosterPicGen {
|
||||
|
||||
private static final int RENDER_SCALE = 3;
|
||||
|
||||
private static final int AVATAR_X = 20;
|
||||
|
||||
private static final int AVATAR_Y = 40;
|
||||
@@ -31,6 +35,16 @@ public class PosterPicGen {
|
||||
|
||||
private static final int AVATAR_CORNER_RADIUS = 14;
|
||||
|
||||
private static final int AVATAR_CLEAR_MARGIN = 2;
|
||||
|
||||
private static final double CENTER_LOGO_X = 153.5;
|
||||
|
||||
private static final double CENTER_LOGO_Y = 116;
|
||||
|
||||
private static final int CENTER_LOGO_SIZE = 68;
|
||||
|
||||
private static final int CENTER_LOGO_CLEAR_MARGIN = 2;
|
||||
|
||||
private static final int AVATAR_DOWNLOAD_TIMEOUT = 5_000;
|
||||
|
||||
private static final int MAX_AVATAR_FILE_SIZE = 5 * 1024 * 1024;
|
||||
@@ -72,12 +86,15 @@ public class PosterPicGen {
|
||||
|
||||
public byte[] gen() {
|
||||
try {
|
||||
BufferedImage baseImage = ImageIO.read(ResourceUtil.getResource("image/base-poster.png"));
|
||||
baseImage.getScaledInstance(this.pageWidth, this.pageHeight, Image.SCALE_SMOOTH);
|
||||
BufferedImage baseImage = createBaseImage();
|
||||
graphics = baseImage.createGraphics();
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
applyQualityRenderingHints(graphics);
|
||||
graphics.scale(RENDER_SCALE, RENDER_SCALE);
|
||||
BufferedImage avatar = loadUserAvatar();
|
||||
// 替换 SVG 左上角 48 x 48、圆角 14px 的默认头像
|
||||
drawUserAvatar();
|
||||
drawUserAvatar(avatar);
|
||||
// 替换 SVG 中间 68 x 68 的 Logo
|
||||
drawCenterLogo(avatar);
|
||||
// 设置 title
|
||||
graphics.setFont(new Font("PingFang SC, Microsoft YaHei, sans-serif", Font.BOLD, 18));
|
||||
graphics.setColor(Color.decode("#333333"));
|
||||
@@ -94,8 +111,9 @@ public class PosterPicGen {
|
||||
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;
|
||||
BufferedImage generate = QrCodeUtil.generate(
|
||||
this.qrCodeUrl, qrWidth * RENDER_SCALE, qrHeight * RENDER_SCALE);
|
||||
int x = (this.pageWidth - qrWidth) / 2;
|
||||
int y = 350;
|
||||
graphics.drawImage(generate, x, y, qrWidth, qrHeight, null);
|
||||
}
|
||||
@@ -114,39 +132,102 @@ public class PosterPicGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void drawUserAvatar() {
|
||||
if (ObjectUtil.isEmpty(this.userAvatar)) {
|
||||
return;
|
||||
private BufferedImage createBaseImage() throws IOException {
|
||||
BufferedImage template = ImageIO.read(ResourceUtil.getResource("image/base-poster.png"));
|
||||
BufferedImage baseImage = new BufferedImage(
|
||||
this.pageWidth * RENDER_SCALE,
|
||||
this.pageHeight * RENDER_SCALE,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D baseGraphics = baseImage.createGraphics();
|
||||
try {
|
||||
applyQualityRenderingHints(baseGraphics);
|
||||
baseGraphics.drawImage(template, 0, 0, baseImage.getWidth(), baseImage.getHeight(), null);
|
||||
} finally {
|
||||
baseGraphics.dispose();
|
||||
}
|
||||
BufferedImage avatar = loadUserAvatar();
|
||||
return baseImage;
|
||||
}
|
||||
|
||||
private void applyQualityRenderingHints(Graphics2D targetGraphics) {
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
|
||||
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
targetGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
||||
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||
}
|
||||
|
||||
private void drawUserAvatar(BufferedImage avatar) {
|
||||
if (avatar == null) {
|
||||
return;
|
||||
}
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fill(new RoundRectangle2D.Double(
|
||||
AVATAR_X - AVATAR_CLEAR_MARGIN,
|
||||
AVATAR_Y - AVATAR_CLEAR_MARGIN,
|
||||
AVATAR_SIZE + AVATAR_CLEAR_MARGIN * 2.0,
|
||||
AVATAR_SIZE + AVATAR_CLEAR_MARGIN * 2.0,
|
||||
(AVATAR_CORNER_RADIUS + AVATAR_CLEAR_MARGIN) * 2.0,
|
||||
(AVATAR_CORNER_RADIUS + AVATAR_CLEAR_MARGIN) * 2.0));
|
||||
drawAvatar(avatar, AVATAR_X, AVATAR_Y, AVATAR_SIZE,
|
||||
new RoundRectangle2D.Double(0, 0, AVATAR_SIZE, AVATAR_SIZE,
|
||||
AVATAR_CORNER_RADIUS * 2.0, AVATAR_CORNER_RADIUS * 2.0));
|
||||
}
|
||||
|
||||
private void drawCenterLogo(BufferedImage avatar) {
|
||||
if (avatar == null) {
|
||||
return;
|
||||
}
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fill(new Ellipse2D.Double(
|
||||
CENTER_LOGO_X - CENTER_LOGO_CLEAR_MARGIN,
|
||||
CENTER_LOGO_Y - CENTER_LOGO_CLEAR_MARGIN,
|
||||
CENTER_LOGO_SIZE + CENTER_LOGO_CLEAR_MARGIN * 2.0,
|
||||
CENTER_LOGO_SIZE + CENTER_LOGO_CLEAR_MARGIN * 2.0));
|
||||
drawAvatar(avatar, CENTER_LOGO_X, CENTER_LOGO_Y, CENTER_LOGO_SIZE,
|
||||
new Ellipse2D.Double(0, 0, CENTER_LOGO_SIZE, CENTER_LOGO_SIZE));
|
||||
}
|
||||
|
||||
private void drawAvatar(BufferedImage avatar, double x, double y, int size, Shape clipShape) {
|
||||
if (avatar == null) {
|
||||
return;
|
||||
}
|
||||
// 对应 SVG 的 preserveAspectRatio="xMidYMid slice",居中裁剪后再缩放,避免头像变形
|
||||
int sourceSize = Math.min(avatar.getWidth(), avatar.getHeight());
|
||||
int sourceX = (avatar.getWidth() - sourceSize) / 2;
|
||||
int sourceY = (avatar.getHeight() - sourceSize) / 2;
|
||||
BufferedImage roundedAvatar = new BufferedImage(AVATAR_SIZE, AVATAR_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D avatarGraphics = roundedAvatar.createGraphics();
|
||||
BufferedImage croppedAvatar = avatar.getSubimage(sourceX, sourceY, sourceSize, sourceSize);
|
||||
Shape oldClip = graphics.getClip();
|
||||
Object oldInterpolation = graphics.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
|
||||
Object oldAlphaInterpolation = graphics.getRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION);
|
||||
try {
|
||||
avatarGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
avatarGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
avatarGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
avatarGraphics.setColor(Color.WHITE);
|
||||
avatarGraphics.fill(new RoundRectangle2D.Double(0, 0, AVATAR_SIZE, AVATAR_SIZE,
|
||||
AVATAR_CORNER_RADIUS * 2.0, AVATAR_CORNER_RADIUS * 2.0));
|
||||
avatarGraphics.setComposite(AlphaComposite.SrcAtop);
|
||||
avatarGraphics.drawImage(avatar,
|
||||
0, 0, AVATAR_SIZE, AVATAR_SIZE,
|
||||
sourceX, sourceY, sourceX + sourceSize, sourceY + sourceSize, null);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
Shape translatedClip = AffineTransform.getTranslateInstance(x, y).createTransformedShape(clipShape);
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fill(translatedClip);
|
||||
graphics.setClip(translatedClip);
|
||||
AffineTransform avatarTransform = AffineTransform.getTranslateInstance(x, y);
|
||||
avatarTransform.scale((double) size / sourceSize, (double) size / sourceSize);
|
||||
graphics.drawImage(croppedAvatar, avatarTransform, null);
|
||||
} finally {
|
||||
avatarGraphics.dispose();
|
||||
graphics.setClip(oldClip);
|
||||
if (oldInterpolation != null) {
|
||||
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldInterpolation);
|
||||
}
|
||||
if (oldAlphaInterpolation != null) {
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, oldAlphaInterpolation);
|
||||
}
|
||||
}
|
||||
graphics.drawImage(roundedAvatar, AVATAR_X, AVATAR_Y, null);
|
||||
}
|
||||
|
||||
private BufferedImage loadUserAvatar() {
|
||||
if (ObjectUtil.isEmpty(this.userAvatar)) {
|
||||
return null;
|
||||
}
|
||||
try (HttpResponse response = HttpRequest.get(this.userAvatar)
|
||||
.setConnectionTimeout(AVATAR_DOWNLOAD_TIMEOUT)
|
||||
.setReadTimeout(AVATAR_DOWNLOAD_TIMEOUT)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<svg width="375" height="700" viewBox="0 0 375 700" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<!-- 阴影滤镜 -->
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="10" flood-color="#000000" flood-opacity="0.1"/>
|
||||
</filter>
|
||||
|
||||
<!-- 背景渐变 -->
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#EBF4FF" />
|
||||
<stop offset="100%" stop-color="#F2F4FF" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- 柔光球 -->
|
||||
<filter id="blurFilter">
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="40" />
|
||||
</filter>
|
||||
|
||||
<!-- 心形路径 (复用) -->
|
||||
<path id="heart" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
|
||||
|
||||
<!-- 圆形剪切路径 (用于Logo) -->
|
||||
<clipPath id="logoClip">
|
||||
<circle cx="187.5" cy="150" r="34" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<!-- 1. 背景 -->
|
||||
<rect width="100%" height="100%" fill="url(#bgGradient)" />
|
||||
<!-- 添加一些柔和的彩色光斑 -->
|
||||
<circle cx="50" cy="50" r="100" fill="#FFDEE9" filter="url(#blurFilter)" opacity="0.5" />
|
||||
<circle cx="375" cy="700" r="150" fill="#B5FFFC" filter="url(#blurFilter)" opacity="0.3" />
|
||||
|
||||
<!-- 2. 左上角图标 -->
|
||||
<g transform="translate(20, 40)">
|
||||
<defs>
|
||||
<clipPath id="smallLogoClip">
|
||||
<rect width="48" height="48" rx="14" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<image href="data:image/png;base64,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 truncated
|
||||
</g>
|
||||
|
||||
<!-- 3. 中间卡片区域 -->
|
||||
<!-- 卡片参数:
|
||||
Top Y: 150
|
||||
Left X: 35
|
||||
Width: 305
|
||||
Height: 450 (approx)
|
||||
Notch Y: 260
|
||||
-->
|
||||
<g filter="url(#shadow)">
|
||||
<!-- 卡片主体路径 -->
|
||||
<path fill="#FFFFFF" d="
|
||||
M 65 150
|
||||
L 147.5 150
|
||||
A 40 40 0 0 1 227.5 150
|
||||
L 310 150
|
||||
A 20 20 0 0 1 330 170
|
||||
L 330 250
|
||||
A 10 10 0 0 0 330 270
|
||||
L 330 600
|
||||
A 20 20 0 0 1 310 620
|
||||
L 65 620
|
||||
A 20 20 0 0 1 45 600
|
||||
L 45 270
|
||||
A 10 10 0 0 0 45 250
|
||||
L 45 170
|
||||
A 20 20 0 0 1 65 150
|
||||
Z
|
||||
" />
|
||||
|
||||
<!-- Logo 图片 -->
|
||||
<image href="data:image/png;base64,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 truncated
|
||||
</g>
|
||||
|
||||
<!-- 4. 卡片上的文字和虚线 -->
|
||||
<!-- 文字: 我的推广码 -->
|
||||
<text x="187.5" y="230" font-family="PingFang SC, Microsoft YaHei, sans-serif" font-size="16" fill="#333333" text-anchor="middle" font-weight="bold">我的推广码</text>
|
||||
|
||||
<!-- 虚线 -->
|
||||
<line x1="55" y1="260" x2="320" y2="260" stroke="#EEEEEE" stroke-width="2" stroke-dasharray="6,6" />
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.trade.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class PosterPicGenTest {
|
||||
|
||||
@Test
|
||||
void shouldGenerateHighResolutionPoster() throws Exception {
|
||||
byte[] posterBytes = new PosterPicGen().setCode("123456").gen();
|
||||
|
||||
assertNotNull(posterBytes);
|
||||
BufferedImage poster = ImageIO.read(new ByteArrayInputStream(posterBytes));
|
||||
assertNotNull(poster);
|
||||
assertEquals(1125, poster.getWidth());
|
||||
assertEquals(2100, poster.getHeight());
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -73,7 +73,8 @@ public class MemberUserController {
|
||||
|
||||
@GetMapping("/fill-missing-user-codes")
|
||||
@Operation(summary = "为历史会员补齐用户 Code")
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
// @PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
@PermitAll
|
||||
public CommonResult<Integer> fillMissingUserCodes() {
|
||||
return success(memberUserService.fillMissingUserCodes());
|
||||
}
|
||||
|
||||
+15
-7
@@ -26,7 +26,11 @@ import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -87,13 +91,17 @@ public class MemberRecentTagServiceImp implements MemberRecentTagService {
|
||||
.in(MemberRecentTagDO::getMemberId, memberIds)
|
||||
);
|
||||
if (ObjectUtil.isEmpty(list)) return Collections.emptyList();
|
||||
return list.stream().map(item -> {
|
||||
MemberTagExtBO entity = new MemberTagExtBO();
|
||||
entity.setMemberId(item.getMemberId());
|
||||
entity.setTagBizType(item.getTagBizType());
|
||||
entity.setTagTitle(item.getTagTitle());
|
||||
return entity;
|
||||
}).collect(Collectors.toList());
|
||||
Map<Long, Set<String>> seenTagTypes = new HashMap<>();
|
||||
return list.stream()
|
||||
.filter(item -> seenTagTypes.computeIfAbsent(item.getMemberId(), key -> new HashSet<>())
|
||||
.add(item.getTagBizType()))
|
||||
.map(item -> {
|
||||
MemberTagExtBO entity = new MemberTagExtBO();
|
||||
entity.setMemberId(item.getMemberId());
|
||||
entity.setTagBizType(item.getTagBizType());
|
||||
entity.setTagTitle(item.getTagTitle());
|
||||
return entity;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-2
@@ -69,8 +69,7 @@ import static cn.iocoder.yudao.module.member.enums.MemberErrorCode.*;
|
||||
@Slf4j
|
||||
public class MemberUserServiceImpl implements MemberUserService {
|
||||
|
||||
private static final String USER_CODE_CHARACTERS =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
private static final String USER_CODE_CHARACTERS = "0123456789";
|
||||
private static final int USER_CODE_LENGTH = 6;
|
||||
private static final int USER_CODE_GENERATE_MAX_RETRY = 20;
|
||||
private static final int USER_CODE_FILL_BATCH_SIZE = 1000;
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.member.service.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberRecentTagDO;
|
||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberRecentTagMapper;
|
||||
import cn.iocoder.yudao.module.member.enums.tag.MemberRecentTagEnum;
|
||||
import cn.iocoder.yudao.module.member.service.user.BO.MemberTagExtBO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.groups.Tuple.tuple;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class MemberRecentTagServiceImpTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private MemberRecentTagServiceImp memberRecentTagService;
|
||||
|
||||
@Mock
|
||||
private MemberRecentTagMapper memberRecentTagMapper;
|
||||
|
||||
@Test
|
||||
void getUserTagExtListDeduplicatesTagsByMemberAndBizType() {
|
||||
when(memberRecentTagMapper.selectList(any())).thenReturn(Arrays.asList(
|
||||
createTag(1L, 64L, MemberRecentTagEnum.VIP, "VIP用户"),
|
||||
createTag(2L, 64L, MemberRecentTagEnum.VIP, "重复VIP用户"),
|
||||
createTag(3L, 64L, MemberRecentTagEnum.TRUE_PERSON_AUTH, "真人认证"),
|
||||
createTag(4L, 65L, MemberRecentTagEnum.VIP, "VIP用户")
|
||||
));
|
||||
|
||||
List<MemberTagExtBO> result = memberRecentTagService.getUserTagExtList(Arrays.asList(64L, 65L));
|
||||
|
||||
assertThat(result).hasSize(3);
|
||||
assertThat(result).extracting(MemberTagExtBO::getMemberId, MemberTagExtBO::getTagBizType)
|
||||
.containsExactly(
|
||||
tuple(64L, MemberRecentTagEnum.VIP.getType()),
|
||||
tuple(64L, MemberRecentTagEnum.TRUE_PERSON_AUTH.getType()),
|
||||
tuple(65L, MemberRecentTagEnum.VIP.getType())
|
||||
);
|
||||
assertThat(result.get(0).getTagTitle()).isEqualTo("VIP用户");
|
||||
}
|
||||
|
||||
private MemberRecentTagDO createTag(Long id, Long memberId, MemberRecentTagEnum tag, String title) {
|
||||
return MemberRecentTagDO.builder()
|
||||
.id(id)
|
||||
.memberId(memberId)
|
||||
.tagBizType(tag.getType())
|
||||
.tagTitle(title)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user