diff --git a/captcha_preview.png b/captcha_preview.png new file mode 100644 index 00000000..bd861c15 Binary files /dev/null and b/captcha_preview.png differ diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/CaptchaUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/CaptchaUtil.java index 265f3368..b861b864 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/CaptchaUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/CaptchaUtil.java @@ -9,11 +9,25 @@ package com.suisung.mall.common.utils; import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; public class CaptchaUtil { + // 字符点阵图案缓存 + private static final Map CHARACTER_PATTERN_CACHE = new ConcurrentHashMap<>(); + + // 预定义字符集 + private static final char[] DIGITS = "0123456789".toCharArray(); + private static final char[] ALPHANUMERIC = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ".toCharArray(); + + // ThreadLocal Random 避免线程竞争 + private static final ThreadLocal THREAD_LOCAL_RANDOM = ThreadLocal.withInitial(Random::new); public static void main(String[] args) throws IOException { // 生成示例验证码图片 @@ -29,17 +43,37 @@ public class CaptchaUtil { } /** - * 生成简单的数字和字母验证码 + * 生成简单的数字验证码 * * @param length 验证码长度 * @return 验证码字符串 */ public static String generateSimpleCaptcha(int length) { - String chars = "0123456789"; - StringBuilder captcha = new StringBuilder(); - java.util.Random random = new java.util.Random(); + return generateSimpleCaptcha(length, DIGITS); + } + + /** + * 生成数字和字母验证码 + * + * @param length 验证码长度 + * @return 验证码字符串 + */ + public static String generateAlphanumericCaptcha(int length) { + return generateSimpleCaptcha(length, ALPHANUMERIC); + } + + /** + * 生成验证码 + * + * @param length 验证码长度 + * @param chars 字符集 + * @return 验证码字符串 + */ + private static String generateSimpleCaptcha(int length, char[] chars) { + StringBuilder captcha = new StringBuilder(length); + Random random = THREAD_LOCAL_RANDOM.get(); for (int i = 0; i < length; i++) { - captcha.append(chars.charAt(random.nextInt(chars.length()))); + captcha.append(chars[random.nextInt(chars.length)]); } return captcha.toString(); } @@ -55,56 +89,91 @@ public class CaptchaUtil { int height = 40; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - java.awt.Graphics2D g = image.createGraphics(); + Graphics2D g = image.createGraphics(); - // 设置背景色 - g.setColor(java.awt.Color.WHITE); - g.fillRect(0, 0, width, height); + try { + // 启用渲染提示以提高图像质量 + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - // 绘制干扰线 - java.util.Random random = new java.util.Random(); - g.setColor(new java.awt.Color(200, 200, 200)); - for (int i = 0; i < 8; i++) { - int x1 = random.nextInt(width); - int y1 = random.nextInt(height); - int x2 = random.nextInt(width); - int y2 = random.nextInt(height); - g.drawLine(x1, y1, x2, y2); + // 设置背景色 + g.setColor(Color.WHITE); + g.fillRect(0, 0, width, height); + + Random random = THREAD_LOCAL_RANDOM.get(); + + // 绘制干扰线 + g.setColor(new Color(200, 200, 200)); + for (int i = 0; i < 8; i++) { + int x1 = random.nextInt(width); + int y1 = random.nextInt(height); + int x2 = random.nextInt(width); + int y2 = random.nextInt(height); + g.drawLine(x1, y1, x2, y2); + } + + // 绘制干扰点 + for (int i = 0; i < 30; i++) { + int x = random.nextInt(width); + int y = random.nextInt(height); + g.fillRect(x, y, 1, 1); + } + + // 绘制验证码字符 + int charWidth = width / (captchaCode.length() + 1); + + for (int i = 0; i < captchaCode.length(); i++) { + char c = captchaCode.charAt(i); + int x = (i + 1) * charWidth - 8; + int y = height / 2 - 10; + + // 添加随机旋转和位置偏移 + java.awt.geom.AffineTransform original = g.getTransform(); + java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(); + transform.translate(x, y); + // 减小旋转角度,避免字符变形过大 + transform.rotate((random.nextDouble() - 0.5) * 0.3); + g.setTransform(transform); + + // 使用点阵方式绘制字符 + drawCharacter(g, c, 0, 0, random); + + // 恢复原始变换 + g.setTransform(original); + } + + return image; + } finally { + g.dispose(); } + } - // 绘制干扰点 - g.setColor(new java.awt.Color(200, 200, 200)); - for (int i = 0; i < 30; i++) { - int x = random.nextInt(width); - int y = random.nextInt(height); - g.fillRect(x, y, 1, 1); + + /** + * 创建错误提示图片 + */ + public static void createErrorImage(OutputStream os) { + try { + int width = 120; + int height = 40; + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + try { + g.setColor(Color.WHITE); + g.fillRect(0, 0, width, height); + + g.setColor(Color.RED); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.drawString("Error", 10, 25); + + ImageIO.write(image, "png", os); + } finally { + g.dispose(); + } + } catch (Exception e) { + System.out.println("创建错误图片失败: " + e.getMessage()); } - - // 绘制验证码字符 - int charWidth = width / (captchaCode.length() + 1); - - for (int i = 0; i < captchaCode.length(); i++) { - char c = captchaCode.charAt(i); - int x = (i + 1) * charWidth - 8; - int y = height / 2 - 10; - - // 添加随机旋转和位置偏移 - java.awt.geom.AffineTransform original = g.getTransform(); - java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(); - transform.translate(x, y); - // 减小旋转角度,避免字符变形过大 - transform.rotate((random.nextDouble() - 0.5) * 0.3); - g.setTransform(transform); - - // 使用点阵方式绘制字符 - drawCharacter(g, c, 0, 0, random); - - // 恢复原始变换 - g.setTransform(original); - } - - g.dispose(); - return image; } /** @@ -116,39 +185,52 @@ public class CaptchaUtil { * @param y y坐标偏移 * @param random 随机数生成器 */ - private static void drawCharacter(java.awt.Graphics2D g, char c, int x, int y, java.util.Random random) { + private static void drawCharacter(Graphics2D g, char c, int x, int y, Random random) { // 使用5x7点阵绘制字符 int[][] pattern = getCharacterPattern(c); if (pattern != null) { // 设置画笔颜色 - g.setColor(java.awt.Color.BLACK); + g.setColor(Color.BLACK); + + // 预计算偏移量减少重复计算 + int baseOffsetX = random.nextInt(3) - 1; + int baseOffsetY = random.nextInt(3) - 1; for (int i = 0; i < pattern.length; i++) { for (int j = 0; j < pattern[i].length; j++) { if (pattern[i][j] == 1) { - // 添加轻微随机偏移使字符不那么规整 - int offsetX = random.nextInt(3) - 1; - int offsetY = random.nextInt(3) - 1; - // 使用更清晰的绘制方式,增大像素点 - g.fillRect(x + j * 3, y + i * 3, 2, 2); + // 使用更少的随机计算 + int offsetX = (random.nextInt(3) - 1 + baseOffsetX) / 2; + int offsetY = (random.nextInt(3) - 1 + baseOffsetY) / 2; + g.fillRect(x + j * 3 + offsetX, y + i * 3 + offsetY, 2, 2); } } } } else { // 如果字符图案未定义,绘制一个简单的替代图形 - g.setColor(java.awt.Color.GRAY); + g.setColor(Color.GRAY); g.fillOval(x + 5, y + 5, 6, 6); } } /** - * 获取字符的点阵图案 + * 获取字符的点阵图案(带缓存) * * @param c 字符 * @return 点阵图案(二维数组) */ private static int[][] getCharacterPattern(char c) { + return CHARACTER_PATTERN_CACHE.getOrDefault(c, createCharacterPattern(c)); + } + + /** + * 创建字符的点阵图案 + * + * @param c 字符 + * @return 点阵图案(二维数组) + */ + private static int[][] createCharacterPattern(char c) { // 简化的5x7点阵字符图案 switch (c) { case '0': diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseConfigController.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseConfigController.java index ef0c060b..e6526010 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseConfigController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/controller/admin/ShopBaseConfigController.java @@ -6,6 +6,7 @@ import com.suisung.mall.common.modules.base.ShopBaseConfig; import com.suisung.mall.shop.base.service.ShopBaseConfigService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; /** *

@@ -22,6 +24,7 @@ import javax.servlet.http.HttpServletResponse; * @author Xinze * @since 2021-06-29 */ +@Slf4j @Api(tags = "系统参数设置表") @RestController("admin-shop-base-config") @RequestMapping("/admin/shop/shop-base-config") @@ -53,7 +56,18 @@ public class ShopBaseConfigController { @ApiOperation(value = "获取验证码", notes = "获取验证码") @RequestMapping(value = "/image", method = RequestMethod.GET) public void image(HttpServletResponse response) { - shopBaseConfigService.image(response); + try { + shopBaseConfigService.image(response); + } catch (Exception e) { + log.error("验证码生成异常: ", e); + try { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("text/plain;charset=utf-8"); + response.getWriter().write("验证码生成失败"); + } catch (IOException ioException) { + log.error("验证码响应写入异常: ", ioException); + } + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseConfigServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseConfigServiceImpl.java index 9723b4c4..c6ff0dbe 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseConfigServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseConfigServiceImpl.java @@ -101,10 +101,16 @@ public class ShopBaseConfigServiceImpl extends BaseServiceImpl items = (List) ObjectUtil.defaultIfNull(cart_data.get("items"), new ArrayList()); if (items.size() == 1) { Map items_item = ObjectUtil.defaultIfNull(items.get(0), new HashMap()); @@ -6043,6 +6042,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl giftbags = (List) ObjectUtil.defaultIfNull(activitys.get("giftbag"), new ArrayList<>()); - if (CollUtil.isNotEmpty(giftbags)) { - for (Map giftbag : giftbags) { - //套餐库存判断 - } - } +// List giftbags = (List) ObjectUtil.defaultIfNull(activitys.get("giftbag"), new ArrayList<>()); +// if (CollUtil.isNotEmpty(giftbags)) { +// for (Map giftbag : giftbags) { +// //套餐库存判断 +// } +// } // 直接判断库存 Integer item_quantity = Convert.toInt(item_tmp_row.get("item_quantity")); @@ -6248,6 +6248,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl 0) { packingFee = new BigDecimal("10"); } } + // 打包费 + base_row.put("packing_fee", packingFee); + BigDecimal voucher_price = Convert.toBigDecimal(order_voucher_row.get("voucher_price")); // 【重要】应付款金额计算 BigDecimal order_payment_amount = NumberUtil.sub(NumberUtil.add(order_money_select_items, freight, packingFee), voucher_price); @@ -6390,7 +6394,6 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl