shinnosuke
2026-09-18 c2f0ec3b3891bc177bdc58c2a111ab9c4fea911d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.talentcard.server.business.merchant;
 
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
 
import com.talentcard.server.business.PageResult;
import com.talentcard.server.business.merchant.auth.MerchantPrincipal;
import com.talentcard.server.common.BusinessException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
 
@SpringBootTest
@ActiveProfiles("test")
class MerchantManagementServiceTests {
 
    @Autowired
    private JdbcTemplate jdbc;
 
    @Autowired
    private MerchantManagementService service;
 
    private Long merchantId;
    private Long storeId;
    private MerchantPrincipal principal;
 
    @BeforeEach
    void prepareMerchant() {
        String suffix = String.valueOf(System.nanoTime());
        jdbc.update("insert into merchant(merchant_name, address, business_status, status, can_login_merchant_app, deleted) values (?, ?, 'OPEN', 1, 1, 0)",
                "Management test merchant " + suffix, "测试商户地址");
        merchantId = jdbc.queryForObject("select max(id) from merchant", Long.class);
        jdbc.update("insert into merchant_store(merchant_id, store_name, address, status, deleted) values (?, ?, ?, 1, 0)",
                merchantId, "测试门店", "测试门店地址");
        storeId = jdbc.queryForObject("select max(id) from merchant_store", Long.class);
        principal = new MerchantPrincipal(1000L, merchantId, storeId, "operator");
        insertProduct("ONLINE", 1, 2);
        insertProduct("OFFLINE", 0, 2);
        insertProduct("PENDING", 0, 1);
    }
 
    @Test
    void returnsMerchantScopedProfileAndProducts() {
        Map<String, Object> profile = service.profile(principal);
        PageResult<Map<String, Object>> online = service.products(principal, "online", 1, 10);
        PageResult<Map<String, Object>> offline = service.products(principal, "offline", 1, 10);
        PageResult<Map<String, Object>> review = service.products(principal, "review", 1, 10);
 
        assertThat(profile).containsEntry("address", "测试门店地址").containsEntry("businessStatus", "open").containsEntry("onlineProductCount", 1L);
        assertThat(online.total()).isEqualTo(1);
        assertThat(offline.total()).isEqualTo(1);
        assertThat(review.total()).isEqualTo(3);
    }
 
    @Test
    void updatesBusinessHoursAndRejectsOverlaps() {
        Map<String, Object> saved = service.updateBusinessHours(principal, new MerchantBusinessHoursRequest(List.of(
                new MerchantBusinessHoursRequest.Period("08:00", "12:00"),
                new MerchantBusinessHoursRequest.Period("14:00", "18:00")
        )));
 
        assertThat(saved.get("periods")).asList().hasSize(2);
        assertThatThrownBy(() -> service.updateBusinessHours(principal, new MerchantBusinessHoursRequest(List.of(
                new MerchantBusinessHoursRequest.Period("08:00", "12:00"),
                new MerchantBusinessHoursRequest.Period("11:00", "18:00")
        )))).isInstanceOf(BusinessException.class);
    }
 
    @Test
    void updatingProductResubmitsForReviewAndPreventsPendingProductFromGoingOnline() {
        Long productId = jdbc.queryForObject("select id from merchant_product where merchant_id = ? and product_no like 'MANAGEMENT-ONLINE-%'", Long.class, merchantId);
        Map<String, Object> updated = service.updateProduct(principal, productId, new MerchantProductUpdateRequest(
                "更新后的商品", new BigDecimal("99.90"), "https://example.com/product.jpg", List.of("https://example.com/product-1.jpg"),
                "更新后的图文详情", Map.of("validity", "30天", "usage", "到店核销")
        ));
 
        assertThat(updated).containsEntry("status", "offline").containsEntry("auditStatus", "pending");
        assertThatThrownBy(() -> service.updateProductStatus(principal, productId, new MerchantProductStatusRequest("online")))
                .isInstanceOf(BusinessException.class);
    }
 
    /**
     * 体检医院在后台团单里只有「商品名称 / 封面图 / 可预约时间 / 检查项目」,没有商品金额。
     * 商家端照这套字段保存时:可预约时间要落到 purchase_notice.validPeriod,金额允许为空并按 0(免费)落库。
     */
    @Test
    void savesHealthCheckProductWithoutPriceAndKeepsReservationWindow() {
        jdbc.update("update merchant set merchant_type_code = 'medical' where id = ?", merchantId);
        Long productId = jdbc.queryForObject("select id from merchant_product where merchant_id = ? and product_no like 'MANAGEMENT-ONLINE-%'", Long.class, merchantId);
 
        Map<String, Object> updated = service.updateProduct(principal, productId, new MerchantProductUpdateRequest(
                "查血", null, "https://example.com/cover.jpg", List.of(),
                "[{\"name\":\"查血\",\"meaning\":\"检查血脂\",\"gender\":[\"男\",\"女\"]}]",
                Map.of("validPeriod", "2026-09-16 至 2026-09-18")
        ));
 
        assertThat(updated).containsEntry("merchantTypeCode", "medical");
        assertThat((BigDecimal) updated.get("salePrice")).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat((Map<String, String>) updated.get("purchaseNotice"))
                .containsEntry("validPeriod", "2026-09-16 至 2026-09-18");
        Map<String, Object> detail = service.product(principal, productId);
        assertThat(String.valueOf(detail.get("detailText"))).contains("检查血脂");
    }
 
    /** 非体检医院仍然要有商品金额:商家端少了金额字段时不能静默按免费存下去。 */
    @Test
    void rejectsMissingPriceForNonHealthCheckMerchant() {
        Long productId = jdbc.queryForObject("select id from merchant_product where merchant_id = ? and product_no like 'MANAGEMENT-ONLINE-%'", Long.class, merchantId);
 
        assertThatThrownBy(() -> service.updateProduct(principal, productId, new MerchantProductUpdateRequest(
                "普通商品", null, "https://example.com/cover.jpg", List.of("https://example.com/product-1.jpg"),
                "图文详情", Map.of()
        ))).isInstanceOf(BusinessException.class).hasMessageContaining("商品金额");
    }
 
    private void insertProduct(String marker, int status, int auditStatus) {
        jdbc.update("""
                insert into merchant_product(merchant_id, product_no, product_type, product_name, cover_url, original_price, sale_price,
                                             audit_status, status, submit_time, deleted)
                values (?, ?, 'food', ?, '/static/test.jpg', 100.00, 80.00, ?, ?, now(), 0)
                """, merchantId, "MANAGEMENT-" + marker + "-" + System.nanoTime(), marker + " product", auditStatus, status);
    }
}