|
|
@@ -0,0 +1,162 @@
|
|
|
+package com.ruoyi.app.pay;
|
|
|
+
|
|
|
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
|
|
+import org.apache.ibatis.mapping.Environment;
|
|
|
+import org.apache.ibatis.session.Configuration;
|
|
|
+import org.apache.ibatis.session.SqlSession;
|
|
|
+import org.apache.ibatis.session.SqlSessionFactory;
|
|
|
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
|
|
|
+import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
|
|
|
+import org.h2.jdbcx.JdbcDataSource;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.springframework.beans.BeanWrapperImpl;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.sql.Connection;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.UUID;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+
|
|
|
+class RiderIncomeMapperTest {
|
|
|
+ private static final String NS = "com.ruoyi.system.mapper.RiderIncomeMapper.";
|
|
|
+ private SqlSessionFactory factory;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() throws Exception {
|
|
|
+ var source = new JdbcDataSource();
|
|
|
+ source.setURL("jdbc:h2:mem:" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1");
|
|
|
+ try (Connection connection = source.getConnection(); var sql = connection.createStatement()) {
|
|
|
+ sql.execute("CREATE TABLE user_billing (id BIGINT PRIMARY KEY, user_id BIGINT, user_type VARCHAR(8), "
|
|
|
+ + "type VARCHAR(8), state VARCHAR(8), dd_id VARCHAR(64), order_source VARCHAR(16), "
|
|
|
+ + "amount DOUBLE, behalf_amount DOUBLE, cretim TIMESTAMP)");
|
|
|
+ sql.execute("INSERT INTO user_billing VALUES "
|
|
|
+ + "(1,7,'2','0','0','same',NULL,60.10,300,'2026-01-01 00:00:00'),"
|
|
|
+ + "(2,7,'2','0','0','same','FLASH',120,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(3,7,'2','0','0','missing','',0,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(4,8,'2','0','0','other','FOOD',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(5,7,'2','2','0','withdraw','FOOD',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(6,7,'2','0','1','void','FOOD',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(7,7,'1','0','0','merchant','FOOD',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(8,7,'2','0','0','outside','FOOD',999,0,'2026-01-02 00:00:00'),"
|
|
|
+ + "(9,7,'2','0','0',NULL,'FOOD',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(10,7,'2','0','0','unknown','OTHER',999,0,'2026-01-01 12:00:00'),"
|
|
|
+ + "(11,7,'2','0','0','foreign','FOOD',0.20,0,'2026-01-01 23:59:59'),"
|
|
|
+ + "(12,7,'2','0','0','before','FOOD',999,0,'2025-12-31 23:59:59')");
|
|
|
+ }
|
|
|
+ var configuration = new Configuration(new Environment("test", new JdbcTransactionFactory(), source));
|
|
|
+ String resource = "mapper/Billing/RiderIncomeMapper.xml";
|
|
|
+ try (var input = getClass().getClassLoader().getResourceAsStream(resource)) {
|
|
|
+ assertNotNull(input, "骑手收入 Mapper 尚未实现");
|
|
|
+ new XMLMapperBuilder(input, configuration, resource, configuration.getSqlFragments()).parse();
|
|
|
+ }
|
|
|
+ factory = new SqlSessionFactoryBuilder().build(configuration);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Object> params(String source) {
|
|
|
+ var params = new HashMap<String, Object>();
|
|
|
+ params.put("riderId", 7L);
|
|
|
+ params.put("startTime", "2026-01-01 00:00:00");
|
|
|
+ params.put("endTime", "2026-01-01 23:59:59");
|
|
|
+ params.put("orderSource", source);
|
|
|
+ params.put("offset", 0L);
|
|
|
+ params.put("size", 100);
|
|
|
+ return params;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object value(Object row, String field) {
|
|
|
+ return new BeanWrapperImpl(row).getPropertyValue(field);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void summaryUsesOnlyOwnValidIncomeAndIgnoresCategoryAndCollection() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ for (String source : List.of("ALL", "FOOD", "FLASH")) {
|
|
|
+ Object result = session.selectOne(NS + "summarize", params(source));
|
|
|
+ assertEquals(new BigDecimal("180.30"), value(result, "totalIncome"));
|
|
|
+ assertEquals(4L, value(result, "totalOrderCount"));
|
|
|
+ assertEquals(3L, value(result, "foodOrderCount"));
|
|
|
+ assertEquals(1L, value(result, "flashOrderCount"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void listSeparatesSameOrderNumbersAndPaginatesWithStableTimeAndIdOrder() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ List<Object> all = session.selectList(NS + "selectPage", params("ALL"));
|
|
|
+ assertEquals(List.of(11L, 3L, 2L, 1L), all.stream().map(x -> value(x, "billId")).toList());
|
|
|
+ assertEquals("foreign", value(all.get(0), "orderNo"));
|
|
|
+ assertEquals("missing", value(all.get(1), "orderNo"), "无需订单表也能读取历史账单");
|
|
|
+ assertEquals("FOOD", value(all.get(1), "orderSource"));
|
|
|
+ assertEquals("same", value(all.get(2), "orderNo"));
|
|
|
+ assertEquals("FLASH", value(all.get(2), "orderSource"));
|
|
|
+ assertEquals("same", value(all.get(3), "orderNo"));
|
|
|
+ assertEquals("FOOD", value(all.get(3), "orderSource"));
|
|
|
+ assertEquals(new BigDecimal("300.00"), value(all.get(3), "collectedAmount"));
|
|
|
+ List<Object> food = session.selectList(NS + "selectPage", params("FOOD"));
|
|
|
+ assertEquals(3, food.size());
|
|
|
+ List<Object> flash = session.selectList(NS + "selectPage", params("FLASH"));
|
|
|
+ assertEquals(1, flash.size());
|
|
|
+ assertEquals(new BigDecimal("120.00"), value(flash.get(0), "incomeAmount"));
|
|
|
+ Map<String, Object> page = params("ALL"); page.put("offset", 1L); page.put("size", 2);
|
|
|
+ List<Object> rows = session.selectList(NS + "selectPage", page);
|
|
|
+ assertEquals(List.of(3L, 2L), rows.stream().map(x -> value(x, "billId")).toList());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void allTimeIncludesHistoryOutsideDailyRangeButStillExcludesOtherAccounts() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ var params = params("ALL"); params.put("startTime", null); params.put("endTime", null);
|
|
|
+ Object result = session.selectOne(NS + "summarize", params);
|
|
|
+ assertEquals(new BigDecimal("2178.30"), value(result, "totalIncome"));
|
|
|
+ assertEquals(6L, value(result, "totalOrderCount"));
|
|
|
+ assertEquals(5L, value(result, "foodOrderCount"));
|
|
|
+ assertEquals(1L, value(result, "flashOrderCount"));
|
|
|
+ assertEquals(6, session.selectList(NS + "selectPage", params).size());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void exactSecondRangeIncludesBothEndpointsWithoutExpandingToWholeDay() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ var params = params("ALL");
|
|
|
+ params.put("startTime", "2026-01-01 12:00:00");
|
|
|
+ params.put("endTime", "2026-01-01 12:00:00");
|
|
|
+ List<Object> rows = session.selectList(NS + "selectPage", params);
|
|
|
+ assertEquals(List.of(3L, 2L), rows.stream().map(x -> value(x, "billId")).toList());
|
|
|
+ Object sum = session.selectOne(NS + "summarize", params);
|
|
|
+ assertEquals(new BigDecimal("120.00"), value(sum, "totalIncome"));
|
|
|
+ assertEquals(2L, value(sum, "totalOrderCount"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void detailScopesByRiderAndRejectsNonIncome() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ for (long id : List.of(4L, 5L, 6L, 7L, 9L, 10L, 999L)) {
|
|
|
+ assertNull(session.selectOne(NS + "selectDetail", Map.of("riderId", 7L, "billId", id)));
|
|
|
+ }
|
|
|
+ Object row = session.selectOne(NS + "selectDetail", Map.of("riderId", 7L, "billId", 1L));
|
|
|
+ assertEquals(new BigDecimal("60.10"), value(row, "incomeAmount"));
|
|
|
+ assertEquals("same", value(row, "orderNo"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void noDataReturnsZeroSummaryAndEmptyPage() {
|
|
|
+ try (SqlSession session = factory.openSession()) {
|
|
|
+ var params = params("ALL"); params.put("riderId", 999L);
|
|
|
+ Object result = session.selectOne(NS + "summarize", params);
|
|
|
+ assertEquals(new BigDecimal("0.00"), value(result, "totalIncome"));
|
|
|
+ assertEquals(0L, value(result, "totalOrderCount"));
|
|
|
+ assertEquals(0L, value(result, "foodOrderCount"));
|
|
|
+ assertEquals(0L, value(result, "flashOrderCount"));
|
|
|
+ assertTrue(session.selectList(NS + "selectPage", params).isEmpty());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|