My logo
Published on

电商系统-实战业务笔记录

1、在电商系统中,使用 int 类型和 BigDecimal 类型来存储价格各有优缺点。以下是对比分析

使用 int 类型存储价格

优点

  1. 性能

    • int 类型的数值计算速度快,内存占用少,操作简单高效。
    • 适合高并发场景下的计算需求。
  2. 简单性

    • 使用 int 类型操作简单,不涉及浮点数运算的复杂性。
    • 适合存储最小货币单位(例如:分、厘),避免浮点数的舍入误差。
  3. 精度

    • 使用整数存储货币值可以避免浮点数计算中的精度问题。

缺点

  1. 可读性

    • 存储最小货币单位(例如分)可能会影响数据的可读性和易理解性,需要转换为实际金额(例如元)进行展示和计算。
  2. 扩展性

    • 如果需要支持不同的货币单位或进行更加复杂的财务计算,使用 int 类型会显得局限。

使用 BigDecimal 类型存储价格

优点

  1. 精度和准确性

    • BigDecimal 提供高精度的运算,适合财务相关的计算,避免浮点数的精度问题。
    • 可以精确表示货币值,避免由于浮点数舍入导致的误差。
  2. 可读性和易用性

    • BigDecimal 类型更容易理解和操作,适合直接存储和展示实际金额。
    • 方便进行货币的算术运算和格式化操作。
  3. 灵活性

    • 支持更复杂的货币计算需求,如利息计算、折扣计算、税费计算等。
    • 可以定义舍入模式,处理不同场景下的舍入需求。

缺点

  1. 性能

    • 相对于 int 类型,BigDecimal 的计算和内存开销更大,操作更复杂。
    • 在高并发场景下可能存在性能瓶颈,需要进行性能优化。
  2. 复杂性

    • 使用 BigDecimal 需要注意初始化和舍入模式,操作相对复杂。
    • 对于简单场景,可能显得过于繁琐。

使用场景和推荐

  • 简单场景:如果价格只需要精确到最小货币单位(如分),且不需要复杂的货币计算,可以考虑使用 int 类型。这种方式简单高效,适合高并发和性能要求高的场景。

    // 存储价格为分
    int priceInCents = 9999; // 99.99元
    
  • 复杂场景:如果需要进行精确的货币计算,尤其是在涉及多个货币单位、复杂的财务计算时,推荐使用 BigDecimal 类型。这种方式精度高、灵活性强,适合大多数电商和财务系统。

    BigDecimal price = new BigDecimal("99.99");
    

实际应用

假设我们有一个简单的电商系统,需要存储商品价格并进行一些基本的操作。以下是使用 intBigDecimal 两种方式的示例:

使用 int 类型存储价格

public class Product {
    private int priceInCents; // 价格以分为单位

    // Getters and Setters
    public int getPriceInCents() {
        return priceInCents;
    }

    public void setPriceInCents(int priceInCents) {
        this.priceInCents = priceInCents;
    }

    // 转换为元
    public BigDecimal getPriceInYuan() {
        return BigDecimal.valueOf(priceInCents, 2);
    }
}

使用 BigDecimal 类型存储价格

import java.math.BigDecimal;

public class Product {
    private BigDecimal price; // 价格以元为单位

    // Getters and Setters
    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

总结

  • 性能和简单性优先:使用 int 类型存储价格,以最小货币单位存储,适合高并发和性能要求高的场景。
  • 精度和灵活性优先:使用 BigDecimal 类型存储价格,适合需要精确计算和处理复杂财务逻辑的场景。

2、在电商系统中,商品SKU编码生成策略和实现代码

在电商系统中,SKU(Stock Keeping Unit)编码是非常重要的,它用于唯一标识每个具体的商品。以下是一些常见的 SKU 编码生成策略和相应的实现代码:

  1. 策略概述:

a) 类别 + 品牌 + 属性 + 序列号 b) 时间戳 + 随机数 c) 前缀 + 商品ID + 属性组合 d) UUID

  1. 实现代码:

以下是几种不同策略的 Java 实现示例:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.UUID;

public class SkuGenerator {

    // 策略1:类别 + 品牌 + 属性 + 序列号
    public static String generateSku1(String category, String brand, String attribute, int sequence) {
        return String.format("%s%s%s%06d", 
            category.substring(0, 2).toUpperCase(),
            brand.substring(0, 2).toUpperCase(),
            attribute.substring(0, 2).toUpperCase(),
            sequence);
    }

    // 策略2:时间戳 + 随机数
    public static String generateSku2() {
        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String random = String.format("%04d", new Random().nextInt(10000));
        return timestamp + random;
    }

    // 策略3:前缀 + 商品ID + 属性组合
    public static String generateSku3(String prefix, long productId, String... attributes) {
        StringBuilder sku = new StringBuilder(prefix);
        sku.append(String.format("%08d", productId));
        for (String attr : attributes) {
            sku.append(attr.substring(0, 1).toUpperCase());
        }
        return sku.toString();
    }

    // 策略4:UUID(去掉连字符)
    public static String generateSku4() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static void main(String[] args) {
        // 测试
        System.out.println(generateSku1("Electronics", "Sony", "Television", 1));
        System.out.println(generateSku2());
        System.out.println(generateSku3("PROD", 12345, "Red", "Large", "Cotton"));
        System.out.println(generateSku4());
    }
}
  1. 策略说明:

a) 类别 + 品牌 + 属性 + 序列号:

  • 优点:易读性好,可以从 SKU 直接获取商品信息
  • 缺点:如果属性变化,可能需要重新生成 SKU

b) 时间戳 + 随机数:

  • 优点:保证唯一性,生成简单
  • 缺点:不包含商品信息,可能较长

c) 前缀 + 商品ID + 属性组合:

  • 优点:包含商品信息,长度适中
  • 缺点:如果商品属性较多,可能需要选择性包含

d) UUID:

  • 优点:保证全局唯一,生成简单
  • 缺点:较长,不包含商品信息
  1. 实施建议:
  • 选择策略时,需要考虑业务需求、系统规模、可读性和性能等因素。
  • 确保 SKU 的唯一性,可以使用数据库唯一索引进行约束。
  • 考虑 SKU 的长度,过长的 SKU 可能不便于管理和使用。
  • 如果使用包含商品信息的策略,需要考虑信息变更时的处理方法。
  • 在生成 SKU 时,最好使用事务来确保原子性,特别是在高并发环境下。
  1. 代码优化:

在实际应用中,你可能需要进行以下优化:

  • 使用缓存来存储已生成的 SKU,避免重复。
  • 实现分布式锁来处理并发问题。
  • 添加错误处理和日志记录。
  • 将 SKU 生成逻辑封装在一个服务中,便于统一管理和修改。

选择哪种策略取决于你的具体业务需求。你可能还需要根据实际情况对这些策略进行调整和组合。

当然可以。为了生成短小且美观的 SKU,我们可以采用一些压缩信息的技巧。以下是一个生成较短 SKU 的策略和实现:

  1. 策略概述:

    • 使用商品类别的首字母(1-2个字符)
    • 使用品牌的首字母或缩写(1-2个字符)
    • 使用年份的后两位
    • 使用一个短的序列号(3-4位)
    • 可选:添加一个校验位
  2. 实现代码:

import java.time.Year;
import java.util.concurrent.atomic.AtomicInteger;

public class ShortSkuGenerator {

    private static final AtomicInteger sequence = new AtomicInteger(0);

    public static String generateSku(String category, String brand) {
        StringBuilder sku = new StringBuilder();

        // 添加类别缩写(1-2个字符)
        sku.append(category.substring(0, Math.min(category.length(), 2)).toUpperCase());

        // 添加品牌缩写(1-2个字符)
        sku.append(brand.substring(0, Math.min(brand.length(), 2)).toUpperCase());

        // 添加年份后两位
        sku.append(String.format("%02d", Year.now().getValue() % 100));

        // 添加序列号(4位,循环使用)
        sku.append(String.format("%04d", sequence.getAndIncrement() % 10000));

        // 添加校验位
        sku.append(generateChecksum(sku.toString()));

        return sku.toString();
    }

    private static char generateChecksum(String input) {
        int sum = 0;
        for (int i = 0; i < input.length(); i++) {
            sum += input.charAt(i);
        }
        return (char) ((sum % 26) + 'A');
    }

    public static void main(String[] args) {
        // 测试
        System.out.println(generateSku("Electronics", "Sony"));
        System.out.println(generateSku("Clothing", "Nike"));
        System.out.println(generateSku("Books", "Penguin"));
        System.out.println(generateSku("Food", "Nestle"));
    }
}
  1. 输出示例:
ELSO230000A
CLNI230001B
BOPE230002C
FONE230003D
  1. 策略说明:
  • 类别和品牌缩写:使用前 1-2 个字符来表示,这样可以保持 SKU 的简短。
  • 年份:使用年份的后两位,可以在一定程度上反映商品的"年代"。
  • 序列号:使用 4 位数字,可以表示 10000 个不同的商品。使用 AtomicInteger 确保线程安全。
  • 校验位:添加一个简单的校验位,可以帮助检测 SKU 的输入错误。
  1. 优点:
  • 短小:总长度为 9-10 个字符,便于记忆和使用。
  • 信息丰富:包含类别、品牌、年份信息。
  • 唯一性:通过序列号保证同类商品的唯一性。
  • 美观:使用大写字母和数字的组合,看起来整洁。
  • 可检验:包含校验位,可以进行简单的错误检测。
  1. 注意事项:
  • 确保类别和品牌的缩写在系统中是唯一的,避免冲突。
  • 序列号是循环使用的,如果商品数量超过 10000,需要考虑其他策略或扩展序列号长度。
  • 在实际应用中,你可能需要将生成的 SKU 存储在数据库中并确保其唯一性。
  • 考虑将 SKU 生成逻辑封装在一个服务中,并添加必要的同步机制以处理并发情况。

这个方案生成的 SKU 既短小又包含了足够的信息,适合大多数中小型电商系统使用。对于大型系统或有特殊需求的场景,你可能需要进一步调整这个策略。