소스 검색

会员管理代码

wwh 1 주 전
부모
커밋
7189203c0e

+ 2 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/mall/constant/MallConfigConstants.java

@@ -37,6 +37,8 @@ public final class MallConfigConstants
37 37
 
38 38
     public static final String MSG_DEFAULT_AVATAR_REQUIRED = "请上传会员默认头像";
39 39
 
40
+    public static final String MSG_DEFAULT_AVATAR_URL_INVALID = "会员默认头像须为有效的图片地址";
41
+
40 42
     public static final String MSG_AUTO_CONFIRM_DAYS_INVALID = "自动确认收货天数须在 1~90 之间";
41 43
 
42 44
     public static final String MSG_AFTERSALE_DAYS_INVALID = "售后申请限制天数须在 1~365 之间";

+ 28 - 5
baqing-shop/src/main/java/com/ruoyi/web/modules/mall/service/impl/MallConfigServiceImpl.java

@@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
4 4
 import org.springframework.stereotype.Service;
5 5
 import org.springframework.transaction.annotation.Transactional;
6 6
 import com.ruoyi.common.utils.StringUtils;
7
+import com.ruoyi.framework.config.ServerConfig;
7 8
 import com.ruoyi.web.modules.mall.constant.MallConfigConstants;
8 9
 import com.ruoyi.web.modules.mall.domain.BizMallConfig;
9 10
 import com.ruoyi.web.modules.mall.dto.MallSettingSaveDTO;
@@ -24,6 +25,9 @@ public class MallConfigServiceImpl implements IMallConfigService, IMallConfigFac
24 25
     @Autowired
25 26
     private BizMallConfigMapper mallConfigMapper;
26 27
 
28
+    @Autowired
29
+    private ServerConfig serverConfig;
30
+
27 31
     @Override
28 32
     public MallSettingVO getSettings()
29 33
     {
@@ -45,7 +49,10 @@ public class MallConfigServiceImpl implements IMallConfigService, IMallConfigFac
45 49
         update.setShowSales(dto.getShowSales());
46 50
         update.setAutoConfirmDays(dto.getAutoConfirmDays());
47 51
         update.setAftersaleLimitDays(dto.getAftersaleLimitDays());
48
-        update.setDefaultAvatar(dto.getDefaultAvatar().trim());
52
+        String avatarUrl = MallConfigSupport.normalizeDefaultAvatarUrl(
53
+                dto.getDefaultAvatar().trim(), serverConfig.getUrl());
54
+        MallConfigSupport.validateDefaultAvatarUrl(avatarUrl);
55
+        update.setDefaultAvatar(avatarUrl);
49 56
         update.setUpdateBy(operator);
50 57
         mallConfigMapper.update(update);
51 58
     }
@@ -77,7 +84,7 @@ public class MallConfigServiceImpl implements IMallConfigService, IMallConfigFac
77 84
             vo.setPsbLink(config.getPsbLink());
78 85
         }
79 86
         vo.setShowSales(MallConfigSupport.isYes(config.getShowSales()));
80
-        vo.setDefaultAvatar(StringUtils.isNotEmpty(config.getDefaultAvatar()) ? config.getDefaultAvatar() : null);
87
+        vo.setDefaultAvatar(resolveDefaultAvatarUrl(config.getDefaultAvatar()));
81 88
         vo.setCurrencySymbol(config.getCurrencySymbol());
82 89
         return vo;
83 90
     }
@@ -112,8 +119,7 @@ public class MallConfigServiceImpl implements IMallConfigService, IMallConfigFac
112 119
     @Override
113 120
     public String getDefaultAvatarUrl()
114 121
     {
115
-        String avatar = requireConfig().getDefaultAvatar();
116
-        return StringUtils.isNotEmpty(avatar) ? avatar : null;
122
+        return resolveDefaultAvatarUrl(requireConfig().getDefaultAvatar());
117 123
     }
118 124
 
119 125
     @Override
@@ -159,10 +165,27 @@ public class MallConfigServiceImpl implements IMallConfigService, IMallConfigFac
159 165
         vo.setAftersaleLimitDays(config.getAftersaleLimitDays());
160 166
         vo.setPaidOrderCancelable(Boolean.FALSE);
161 167
         vo.setShippedOrderCancelable(Boolean.FALSE);
162
-        vo.setDefaultAvatar(config.getDefaultAvatar());
168
+        vo.setDefaultAvatar(resolveDefaultAvatarUrl(config.getDefaultAvatar()));
163 169
         return vo;
164 170
     }
165 171
 
172
+    private String resolveDefaultAvatarUrl(String stored)
173
+    {
174
+        if (StringUtils.isEmpty(stored))
175
+        {
176
+            return null;
177
+        }
178
+        String serverUrl = null;
179
+        try
180
+        {
181
+            serverUrl = serverConfig.getUrl();
182
+        }
183
+        catch (Exception ignored)
184
+        {
185
+        }
186
+        return MallConfigSupport.normalizeDefaultAvatarUrl(stored, serverUrl);
187
+    }
188
+
166 189
     private String trimToNull(String value)
167 190
     {
168 191
         if (StringUtils.isEmpty(value))

+ 41 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/mall/support/MallConfigSupport.java

@@ -45,6 +45,47 @@ public final class MallConfigSupport
45 45
         validateOptionalUrl(dto.getPsbLink());
46 46
     }
47 47
 
48
+    /**
49
+     * 将会员默认头像规范为可访问的 HTTP(S) 地址;已是完整 URL 则原样返回。
50
+     */
51
+    public static String normalizeDefaultAvatarUrl(String avatar, String serverBaseUrl)
52
+    {
53
+        if (StringUtils.isEmpty(avatar))
54
+        {
55
+            return avatar;
56
+        }
57
+        String trimmed = avatar.trim();
58
+        if (trimmed.startsWith("http://") || trimmed.startsWith("https://"))
59
+        {
60
+            return trimmed;
61
+        }
62
+        if (StringUtils.isEmpty(serverBaseUrl))
63
+        {
64
+            return trimmed;
65
+        }
66
+        String base = serverBaseUrl.endsWith("/")
67
+                ? serverBaseUrl.substring(0, serverBaseUrl.length() - 1)
68
+                : serverBaseUrl;
69
+        if (!trimmed.startsWith("/"))
70
+        {
71
+            trimmed = "/" + trimmed;
72
+        }
73
+        return base + trimmed;
74
+    }
75
+
76
+    public static void validateDefaultAvatarUrl(String url)
77
+    {
78
+        if (StringUtils.isEmpty(url))
79
+        {
80
+            throw new ServiceException(MallConfigConstants.MSG_DEFAULT_AVATAR_REQUIRED);
81
+        }
82
+        String trimmed = url.trim();
83
+        if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://"))
84
+        {
85
+            throw new ServiceException(MallConfigConstants.MSG_DEFAULT_AVATAR_URL_INVALID);
86
+        }
87
+    }
88
+
48 89
     public static boolean isYes(String flag)
49 90
     {
50 91
         return MallConfigConstants.FLAG_YES.equals(flag);

+ 32 - 0
baqing-shop/src/test/java/com/ruoyi/web/modules/mall/service/MallConfigServiceImplTest.java

@@ -8,10 +8,12 @@ import static org.mockito.Mockito.verify;
8 8
 import static org.mockito.Mockito.when;
9 9
 import org.junit.jupiter.api.Test;
10 10
 import org.junit.jupiter.api.extension.ExtendWith;
11
+import org.mockito.ArgumentCaptor;
11 12
 import org.mockito.InjectMocks;
12 13
 import org.mockito.Mock;
13 14
 import org.mockito.junit.jupiter.MockitoExtension;
14 15
 import com.ruoyi.common.exception.ServiceException;
16
+import com.ruoyi.framework.config.ServerConfig;
15 17
 import com.ruoyi.web.modules.mall.constant.MallConfigConstants;
16 18
 import com.ruoyi.web.modules.mall.domain.BizMallConfig;
17 19
 import com.ruoyi.web.modules.mall.dto.MallSettingSaveDTO;
@@ -25,6 +27,9 @@ class MallConfigServiceImplTest
25 27
     @Mock
26 28
     private BizMallConfigMapper mallConfigMapper;
27 29
 
30
+    @Mock
31
+    private ServerConfig serverConfig;
32
+
28 33
     @InjectMocks
29 34
     private MallConfigServiceImpl mallConfigService;
30 35
 
@@ -63,12 +68,39 @@ class MallConfigServiceImplTest
63 68
     void updateSettings_success()
64 69
     {
65 70
         when(mallConfigMapper.selectById(MallConfigConstants.CONFIG_ID)).thenReturn(config());
71
+        when(serverConfig.getUrl()).thenReturn("http://localhost:8020");
66 72
 
67 73
         mallConfigService.updateSettings(validDto(), "admin");
68 74
 
69 75
         verify(mallConfigMapper).update(any(BizMallConfig.class));
70 76
     }
71 77
 
78
+    @Test
79
+    void updateSettings_normalizesRelativeAvatarToHttpUrl()
80
+    {
81
+        when(mallConfigMapper.selectById(MallConfigConstants.CONFIG_ID)).thenReturn(config());
82
+        when(serverConfig.getUrl()).thenReturn("http://localhost:8020");
83
+        MallSettingSaveDTO dto = validDto();
84
+        dto.setDefaultAvatar("/profile/upload/2026/05/26/a.png");
85
+
86
+        mallConfigService.updateSettings(dto, "admin");
87
+
88
+        ArgumentCaptor<BizMallConfig> captor = ArgumentCaptor.forClass(BizMallConfig.class);
89
+        verify(mallConfigMapper).update(captor.capture());
90
+        assertEquals("http://localhost:8020/profile/upload/2026/05/26/a.png", captor.getValue().getDefaultAvatar());
91
+    }
92
+
93
+    @Test
94
+    void getDefaultAvatarUrl_resolvesLegacyRelativePath()
95
+    {
96
+        BizMallConfig c = config();
97
+        c.setDefaultAvatar("/profile/upload/default.png");
98
+        when(mallConfigMapper.selectById(MallConfigConstants.CONFIG_ID)).thenReturn(c);
99
+        when(serverConfig.getUrl()).thenReturn("http://localhost:8020");
100
+
101
+        assertEquals("http://localhost:8020/profile/upload/default.png", mallConfigService.getDefaultAvatarUrl());
102
+    }
103
+
72 104
     private MallSettingSaveDTO validDto()
73 105
     {
74 106
         MallSettingSaveDTO dto = new MallSettingSaveDTO();

+ 45 - 0
baqing-shop/src/test/java/com/ruoyi/web/modules/mall/support/MallConfigSupportTest.java

@@ -0,0 +1,45 @@
1
+package com.ruoyi.web.modules.mall.support;
2
+
3
+import static org.junit.jupiter.api.Assertions.assertEquals;
4
+import static org.junit.jupiter.api.Assertions.assertThrows;
5
+import org.junit.jupiter.api.Test;
6
+import com.ruoyi.common.exception.ServiceException;
7
+import com.ruoyi.web.modules.mall.constant.MallConfigConstants;
8
+
9
+class MallConfigSupportTest
10
+{
11
+    @Test
12
+    void normalizeDefaultAvatarUrl_keepsHttpUrl()
13
+    {
14
+        assertEquals("https://cdn.example.com/a.png",
15
+                MallConfigSupport.normalizeDefaultAvatarUrl("https://cdn.example.com/a.png", "http://localhost:8020"));
16
+    }
17
+
18
+    @Test
19
+    void normalizeDefaultAvatarUrl_prependsServerBase()
20
+    {
21
+        assertEquals("http://localhost:8020/profile/upload/a.png",
22
+                MallConfigSupport.normalizeDefaultAvatarUrl("/profile/upload/a.png", "http://localhost:8020"));
23
+    }
24
+
25
+    @Test
26
+    void validateDefaultAvatarUrl_rejectsRelativePath()
27
+    {
28
+        assertThrows(ServiceException.class,
29
+                () -> MallConfigSupport.validateDefaultAvatarUrl("/profile/upload/a.png"));
30
+    }
31
+
32
+    @Test
33
+    void validateDefaultAvatarUrl_acceptsHttpUrl()
34
+    {
35
+        MallConfigSupport.validateDefaultAvatarUrl("https://example.com/a.png");
36
+    }
37
+
38
+    @Test
39
+    void validateDefaultAvatarUrl_emptyThrowsRequiredMessage()
40
+    {
41
+        ServiceException ex = assertThrows(ServiceException.class,
42
+                () -> MallConfigSupport.validateDefaultAvatarUrl(""));
43
+        assertEquals(MallConfigConstants.MSG_DEFAULT_AVATAR_REQUIRED, ex.getMessage());
44
+    }
45
+}