package cn.iocoder.yudao.module.qcreport.service.instance; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.time.LocalDate; import java.util.Locale; import static org.junit.jupiter.api.Assertions.assertEquals; /** * 报告编号生成规则。 *
* 这里守的是「编号一定能被自己读懂」:{@link ReportNoGenerator#nextSequence} 是对 * {@link ReportNoGenerator#format} 的逆运算,两者格式一旦对不上, * 表现是流水号悄悄从 1 重来,然后撞唯一索引、重试 5 次后报「编号冲突」—— * 用户看到的是「出不了件」,很难联想到是编号格式的问题。 */ class ReportNoGeneratorTest { private static final LocalDate DAY = LocalDate.of(2026, 9, 18); @Test @DisplayName("前缀与编号格式") void prefixAndFormat() { assertEquals("QR20260918-", ReportNoGenerator.prefix(DAY)); assertEquals("QR20260918-0001", ReportNoGenerator.format(DAY, 1)); assertEquals("QR20260918-0042", ReportNoGenerator.format(DAY, 42)); assertEquals("QR20260918-9999", ReportNoGenerator.format(DAY, 9999)); } @Test @DisplayName("序号超过 9999 时位宽自然加宽,不截断也不回绕") void formatWidensBeyondFourDigits() { assertEquals("QR20260918-10000", ReportNoGenerator.format(DAY, 10_000)); assertEquals("QR20260918-100000", ReportNoGenerator.format(DAY, 100_000)); } @Test @DisplayName("编号里的数字与年份不受服务器 locale 影响") void independentOfDefaultLocale() { Locale original = Locale.getDefault(); try { // 泰历会把 2026 年写成 2569;非拉丁数字字形会写出阿拉伯-印度数字 Locale.setDefault(Locale.forLanguageTag("th-TH-u-ca-buddhist-nu-thai")); assertEquals("QR20260918-0007", ReportNoGenerator.format(DAY, 7)); } finally { Locale.setDefault(original); } } @Test @DisplayName("同一天有编号时序号递增") void nextSequenceIncrements() { assertEquals(2, ReportNoGenerator.nextSequence("QR20260918-0001", DAY)); assertEquals(43, ReportNoGenerator.nextSequence("QR20260918-0042", DAY)); assertEquals(10_000, ReportNoGenerator.nextSequence("QR20260918-9999", DAY)); // 位宽加宽之后仍要能接着往下走 assertEquals(100_001, ReportNoGenerator.nextSequence("QR20260918-100000", DAY)); } @Test @DisplayName("还没有编号时从 1 开始") void nextSequenceStartsAtOneWhenAbsent() { assertEquals(1, ReportNoGenerator.nextSequence(null, DAY)); } @Test @DisplayName("拿到的编号属于别的日期时,按新的一天从 1 开始") void nextSequenceResetsOnOtherDay() { assertEquals(1, ReportNoGenerator.nextSequence("QR20260917-0042", DAY)); assertEquals(1, ReportNoGenerator.nextSequence("QR20260919-0042", DAY)); } @Test @DisplayName("格式不认识的编号一律归 1,而不是抛异常或硬解析") void nextSequenceFallsBackOnUnknownFormat() { // 不是本生成器的前缀 assertEquals(1, ReportNoGenerator.nextSequence("QC-2026-0001", DAY)); // 尾部不是数字 assertEquals(1, ReportNoGenerator.nextSequence("QR20260918-ABCD", DAY)); // 尾部混了数字与字母 assertEquals(1, ReportNoGenerator.nextSequence("QR20260918-00A1", DAY)); // 位数不足 4 位 assertEquals(1, ReportNoGenerator.nextSequence("QR20260918-1", DAY)); // 位数超过上限(本生成器产出不了这么长的尾巴,说明不是它写的) assertEquals(1, ReportNoGenerator.nextSequence("QR20260918-1234567890", DAY)); // 只有前缀、没有流水 assertEquals(1, ReportNoGenerator.nextSequence("QR20260918-", DAY)); // 空串 assertEquals(1, ReportNoGenerator.nextSequence("", DAY)); } }