- Published on
电商系统-实战业务笔记录
int
类型和 BigDecimal
类型来存储价格各有优缺点。以下是对比分析
1、在电商系统中,使用 int
类型存储价格
使用 优点
性能:
int
类型的数值计算速度快,内存占用少,操作简单高效。- 适合高并发场景下的计算需求。
简单性:
- 使用
int
类型操作简单,不涉及浮点数运算的复杂性。 - 适合存储最小货币单位(例如:分、厘),避免浮点数的舍入误差。
- 使用
精度:
- 使用整数存储货币值可以避免浮点数计算中的精度问题。
缺点
可读性:
- 存储最小货币单位(例如分)可能会影响数据的可读性和易理解性,需要转换为实际金额(例如元)进行展示和计算。
扩展性:
- 如果需要支持不同的货币单位或进行更加复杂的财务计算,使用
int
类型会显得局限。
- 如果需要支持不同的货币单位或进行更加复杂的财务计算,使用
BigDecimal
类型存储价格
使用 优点
精度和准确性:
BigDecimal
提供高精度的运算,适合财务相关的计算,避免浮点数的精度问题。- 可以精确表示货币值,避免由于浮点数舍入导致的误差。
可读性和易用性:
BigDecimal
类型更容易理解和操作,适合直接存储和展示实际金额。- 方便进行货币的算术运算和格式化操作。
灵活性:
- 支持更复杂的货币计算需求,如利息计算、折扣计算、税费计算等。
- 可以定义舍入模式,处理不同场景下的舍入需求。
缺点
性能:
- 相对于
int
类型,BigDecimal
的计算和内存开销更大,操作更复杂。 - 在高并发场景下可能存在性能瓶颈,需要进行性能优化。
- 相对于
复杂性:
- 使用
BigDecimal
需要注意初始化和舍入模式,操作相对复杂。 - 对于简单场景,可能显得过于繁琐。
- 使用
使用场景和推荐
简单场景:如果价格只需要精确到
最小货币单位(如分)
,且不需要复杂的货币计算,可以考虑使用int
类型。这种方式简单高效,适合高并发和性能要求高的场景。// 存储价格为分 int priceInCents = 9999; // 99.99元
复杂场景:如果需要进行精确的货币计算,尤其是在涉及多个货币单位、复杂的财务计算时,推荐使用
BigDecimal
类型。这种方式精度高、灵活性强,适合大多数电商和财务系统。BigDecimal price = new BigDecimal("99.99");
实际应用
假设我们有一个简单的电商系统,需要存储商品价格并进行一些基本的操作。以下是使用 int
和 BigDecimal
两种方式的示例:
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 编码生成策略和相应的实现代码:
- 策略概述:
a) 类别 + 品牌 + 属性 + 序列号 b) 时间戳 + 随机数 c) 前缀 + 商品ID + 属性组合 d) UUID
- 实现代码:
以下是几种不同策略的 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());
}
}
- 策略说明:
a) 类别 + 品牌 + 属性 + 序列号:
- 优点:易读性好,可以从 SKU 直接获取商品信息
- 缺点:如果属性变化,可能需要重新生成 SKU
b) 时间戳 + 随机数:
- 优点:保证唯一性,生成简单
- 缺点:不包含商品信息,可能较长
c) 前缀 + 商品ID + 属性组合:
- 优点:包含商品信息,长度适中
- 缺点:如果商品属性较多,可能需要选择性包含
d) UUID:
- 优点:保证全局唯一,生成简单
- 缺点:较长,不包含商品信息
- 实施建议:
- 选择策略时,需要考虑业务需求、系统规模、可读性和性能等因素。
- 确保 SKU 的唯一性,可以使用数据库唯一索引进行约束。
- 考虑 SKU 的长度,过长的 SKU 可能不便于管理和使用。
- 如果使用包含商品信息的策略,需要考虑信息变更时的处理方法。
- 在生成 SKU 时,最好使用事务来确保原子性,特别是在高并发环境下。
- 代码优化:
在实际应用中,你可能需要进行以下优化:
- 使用缓存来存储已生成的 SKU,避免重复。
- 实现分布式锁来处理并发问题。
- 添加错误处理和日志记录。
- 将 SKU 生成逻辑封装在一个服务中,便于统一管理和修改。
选择哪种策略取决于你的具体业务需求。你可能还需要根据实际情况对这些策略进行调整和组合。
当然可以。为了生成短小且美观的 SKU,我们可以采用一些压缩信息的技巧。以下是一个生成较短 SKU 的策略和实现:
策略概述:
- 使用商品类别的首字母(1-2个字符)
- 使用品牌的首字母或缩写(1-2个字符)
- 使用年份的后两位
- 使用一个短的序列号(3-4位)
- 可选:添加一个校验位
实现代码:
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"));
}
}
- 输出示例:
ELSO230000A
CLNI230001B
BOPE230002C
FONE230003D
- 策略说明:
- 类别和品牌缩写:使用前 1-2 个字符来表示,这样可以保持 SKU 的简短。
- 年份:使用年份的后两位,可以在一定程度上反映商品的"年代"。
- 序列号:使用 4 位数字,可以表示 10000 个不同的商品。使用 AtomicInteger 确保线程安全。
- 校验位:添加一个简单的校验位,可以帮助检测 SKU 的输入错误。
- 优点:
- 短小:总长度为 9-10 个字符,便于记忆和使用。
- 信息丰富:包含类别、品牌、年份信息。
- 唯一性:通过序列号保证同类商品的唯一性。
- 美观:使用大写字母和数字的组合,看起来整洁。
- 可检验:包含校验位,可以进行简单的错误检测。
- 注意事项:
- 确保类别和品牌的缩写在系统中是唯一的,避免冲突。
- 序列号是循环使用的,如果商品数量超过 10000,需要考虑其他策略或扩展序列号长度。
- 在实际应用中,你可能需要将生成的 SKU 存储在数据库中并确保其唯一性。
- 考虑将 SKU 生成逻辑封装在一个服务中,并添加必要的同步机制以处理并发情况。
这个方案生成的 SKU 既短小又包含了足够的信息,适合大多数中小型电商系统使用。对于大型系统或有特殊需求的场景,你可能需要进一步调整这个策略。