yinshaojie 9 ay önce
ebeveyn
işleme
8e7eeef44c
20 değiştirilmiş dosya ile 481 ekleme ve 167 silme
  1. 51
    97
      RuoYi-App/App.vue
  2. 13
    10
      RuoYi-App/components/custom-tabbar/index.vue
  3. 4
    0
      RuoYi-Vue/ruoyi-system/pom.xml
  4. 38
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtAuthenticationTokenFilter.java
  5. 22
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtConfigurer.java
  6. 37
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtFilter.java
  7. 30
    17
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/SecurityConfig.java
  8. 129
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/TokenProvider.java
  9. 32
    24
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/controller/NovelController.java
  10. 3
    1
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/AuthorApplicationMapper.java
  11. 2
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/CategoryMapper.java
  12. 2
    1
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/ChapterMapper.java
  13. 2
    1
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/NovelMapper.java
  14. 48
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/CustomUserDetailsService.java
  15. 9
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/IAuthService.java
  16. 18
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/NovelService.java
  17. 17
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/TokenService.java
  18. 5
    16
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/impl/AuthServiceImpl.java
  19. 16
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/impl/NovelServiceImpl.java
  20. 3
    0
      RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

+ 51
- 97
RuoYi-App/App.vue Dosyayı Görüntüle

@@ -1,19 +1,8 @@
1 1
 <template>
2 2
   <view class="app-container">
3 3
     <router-view />
4
-    <!-- 若依风格自定义菜单 -->
5
-    <view v-if="showMenu" class="ruoyi-tabbar">
6
-      <view 
7
-        v-for="(item, index) in menuItems" 
8
-        :key="index"
9
-        class="tab-item"
10
-        :class="{ active: activeIndex === index }"
11
-        @click="switchTab(item, index)"
12
-      >
13
-        <image class="icon" :src="activeIndex === index ? item.activeIcon : item.icon" />
14
-        <text class="text">{{ item.text }}</text>
15
-      </view>
16
-    </view>
4
+    <!-- 使用自定义TabBar组件 -->
5
+    <CustomTabbar v-if="showTabBar" />
17 6
   </view>
18 7
 </template>
19 8
 
@@ -26,60 +15,39 @@ export default {
26 15
   components: { CustomTabbar },
27 16
   data() {
28 17
     return {
29
-      activeIndex: 0,
30
-      showMenu: true,
31
-      menuItems: [
32
-        {
33
-          path: '/pages/index/index',
34
-          icon: '/static/tabbar/home.png',
35
-          activeIcon: '/static/tabbar/home_selected.png',
36
-          text: '首页'
37
-        },
38
-        {
39
-          path: '/pages/novel/list',
40
-          icon: '/static/tabbar/novel.png',
41
-          activeIcon: '/static/tabbar/novel_selected.png',
42
-          text: '小说'
43
-        },
44
-        {
45
-          path: '/pages/bookshelf/index',
46
-          icon: '/static/tabbar/bookshelf.png',
47
-          activeIcon: '/static/tabbar/bookshelf_selected.png',
48
-          text: '书架'
49
-        },
50
-        {
51
-          path: '/pages/me/index',
52
-          icon: '/static/tabbar/mine.png',
53
-          activeIcon: '/static/tabbar/mine_selected.png',
54
-          text: '我的'
55
-        }
18
+      showTabBar: false, // 默认不显示
19
+      tabBarPages: [
20
+        '/pages/index/index',
21
+        '/pages/novel/list',
22
+        '/pages/bookshelf/index',
23
+        '/pages/me/index'
56 24
       ]
57 25
     }
58 26
   },
59 27
   watch: {
60 28
     '$route.path': {
61 29
       immediate: true,
62
-      handler(path) {
63
-        // 确定当前激活的菜单项
64
-        this.activeIndex = this.menuItems.findIndex(item => 
65
-          path.startsWith(item.path)
30
+      handler(newPath) {
31
+        // 检查当前页面是否需要显示菜单
32
+        this.showTabBar = this.tabBarPages.some(path => 
33
+          newPath.includes(path)
66 34
         );
67 35
         
68
-        // 决定是否显示菜单
69
-        this.showMenu = this.activeIndex !== -1;
36
+        // 特殊处理:在小说阅读页面也显示菜单
37
+        if (newPath.includes('/pages/novel/read')) {
38
+          this.showTabBar = true;
39
+        }
40
+        
41
+        // 强制更新CustomTabbar组件
42
+        this.$nextTick(() => {
43
+          if (this.$refs.customTabbar) {
44
+            this.$refs.customTabbar.updateSelectedIndex();
45
+          }
46
+        });
70 47
       }
71 48
     }
72 49
   },
73
-  methods: {
74
-    switchTab(item, index) {
75
-      if (this.activeIndex === index) return;
76
-      
77
-      this.activeIndex = index;
78
-      uni.switchTab({
79
-        url: item.path
80
-      });
81
-    }
82
-  },
50
+
83 51
   onLaunch() {
84 52
     // 初始化主题
85 53
     this.initTheme()
@@ -89,13 +57,17 @@ export default {
89 57
     
90 58
     // 初始化 store 状态
91 59
     this.initStoreState()
92
-    // 检查登录状态
93
-    //this.checkLogin()
94
-      console.log('App launched, store:', this.$store)
95
-
96
-
60
+    console.log('App launched, store:', this.$store)
97 61
   },
98 62
   onShow() {
63
+    // 初始检查路由
64
+    this.$nextTick(() => {
65
+      const currentPath = this.$route.path;
66
+      this.showTabBar = this.tabBarPages.some(path => 
67
+        currentPath.includes(path)
68
+      );
69
+    });
70
+    
99 71
     // 检查云端阅读进度
100 72
     if (this.$store.getters.token) {
101 73
       this.syncReadingProgress()
@@ -212,49 +184,31 @@ export default {
212 184
         })
213 185
       }
214 186
     }
215
-  }
187
+  },
188
+    mounted() {
189
+      this.checkInitialRoute();
190
+    }
216 191
 }
217 192
 </script>
218 193
 
219 194
 <style lang="scss">
220 195
 /* 确保自定义TabBar有足够的空间 */
221 196
 .app-container {
222
-  padding-bottom: 100rpx;
197
+  padding-bottom: 120rpx !important; /* 增加底部空间 */
223 198
 }
224
-.ruoyi-tabbar {
225
-  position: fixed;
226
-  bottom: 0;
227
-  left: 0;
228
-  right: 0;
229
-  display: flex;
230
-  height: 100rpx;
231
-  background-color: #fff;
232
-  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
233
-  z-index: 9999;
234
-  border-top: 1rpx solid #eee;
235
-  
236
-  .tab-item {
237
-    flex: 1;
238
-    display: flex;
239
-    flex-direction: column;
240
-    align-items: center;
241
-    justify-content: center;
242
-    
243
-    .icon {
244
-      width: 44rpx;
245
-      height: 44rpx;
246
-      margin-bottom: 6rpx;
247
-    }
248
-    
249
-    .text {
250
-      font-size: 22rpx;
251
-      color: #666;
252
-    }
253
-        .active .text {
254
-          color: #3a8ee6; /* 若依主题蓝色 */
255
-          font-weight: 500;
256
-        }  
257
-	}
199
+
200
+/* 确保菜单不被其他元素覆盖 */
201
+.custom-tabbar {
202
+  z-index: 99999 !important;
203
+  position: fixed !important;
204
+  bottom: 0 !important;
205
+  left: 0 !important;
206
+  right: 0 !important;
207
+}
208
+
209
+/* 修复页面内容被遮挡的问题 */
210
+.page-content {
211
+  padding-bottom: 140rpx !important;
258 212
 }
259 213
 /* 确保tabbar显示 */
260 214
 uni-tabbar {

+ 13
- 10
RuoYi-App/components/custom-tabbar/index.vue Dosyayı Görüntüle

@@ -49,7 +49,7 @@ export default {
49 49
       ]
50 50
     }
51 51
   },
52
-  created() {
52
+  mounted() {
53 53
     this.updateSelectedIndex();
54 54
   },
55 55
   methods: {
@@ -61,6 +61,7 @@ export default {
61 61
         url: item.pagePath
62 62
       });
63 63
     },
64
+    // 添加 ref 引用以便外部调用
64 65
     updateSelectedIndex() {
65 66
       const pages = getCurrentPages();
66 67
       if (!pages.length) return;
@@ -69,7 +70,7 @@ export default {
69 70
       const currentRoute = currentPage.route;
70 71
       
71 72
       const index = this.list.findIndex(item => 
72
-        item.pagePath.includes(currentRoute)
73
+        currentRoute.includes(item.pagePath.replace('/pages/', '').replace('/index', ''))
73 74
       );
74 75
       
75 76
       if (index !== -1) {
@@ -87,10 +88,10 @@ export default {
87 88
   left: 0;
88 89
   right: 0;
89 90
   display: flex;
90
-  height: 100rpx;
91
+  height: 120rpx; /* 增加高度 */
91 92
   background-color: #ffffff;
92
-  box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
93
-  z-index: 9999;
93
+  box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1); /* 增强阴影 */
94
+  z-index: 99999;
94 95
 }
95 96
 
96 97
 .custom-tabbar-item {
@@ -99,21 +100,23 @@ export default {
99 100
   flex-direction: column;
100 101
   align-items: center;
101 102
   justify-content: center;
103
+  padding: 10rpx 0;
102 104
 }
103 105
 
104 106
 .custom-tabbar-icon {
105
-  width: 48rpx;
106
-  height: 48rpx;
107
-  margin-bottom: 6rpx;
107
+  width: 52rpx;
108
+  height: 52rpx;
109
+  margin-bottom: 8rpx;
108 110
 }
109 111
 
110 112
 .custom-tabbar-text {
111
-  font-size: 24rpx;
113
+  font-size: 26rpx;
112 114
   color: #7a7e83;
113 115
 }
114 116
 
115 117
 .custom-tabbar-item-active .custom-tabbar-text {
116
-  color: #3cc51f;
118
+  color: #3a8ee6; /* 使用若依主题蓝色 */
117 119
   font-weight: bold;
120
+  font-size: 28rpx;
118 121
 }
119 122
 </style>

+ 4
- 0
RuoYi-Vue/ruoyi-system/pom.xml Dosyayı Görüntüle

@@ -96,6 +96,10 @@
96 96
             <version>RELEASE</version>
97 97
             <scope>compile</scope>
98 98
         </dependency>
99
+        <dependency>
100
+            <groupId>com.ruoyi</groupId>
101
+            <artifactId>ruoyi-framework</artifactId>
102
+        </dependency>
99 103
     </dependencies>
100 104
 
101 105
 </project>

+ 38
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtAuthenticationTokenFilter.java Dosyayı Görüntüle

@@ -0,0 +1,38 @@
1
+package com.ruoyi.novel.config;
2
+
3
+
4
+import com.ruoyi.common.core.domain.model.LoginUser;
5
+import com.ruoyi.common.utils.SecurityUtils;
6
+import com.ruoyi.common.utils.StringUtils;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
9
+import org.springframework.security.core.context.SecurityContextHolder;
10
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
11
+import org.springframework.stereotype.Component;
12
+import org.springframework.web.filter.OncePerRequestFilter;
13
+
14
+import javax.servlet.FilterChain;
15
+import javax.servlet.ServletException;
16
+import javax.servlet.http.HttpServletRequest;
17
+import javax.servlet.http.HttpServletResponse;
18
+import java.io.IOException;
19
+
20
+@Component
21
+public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
22
+
23
+    @Autowired
24
+    private TokenProvider tokenService;
25
+
26
+    @Override
27
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
28
+            throws ServletException, IOException {
29
+        LoginUser loginUser = tokenService.getLoginUser(request);
30
+        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
31
+            tokenService.verifyToken(loginUser);
32
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
33
+            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
34
+            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
35
+        }
36
+        chain.doFilter(request, response);
37
+    }
38
+}

+ 22
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtConfigurer.java Dosyayı Görüntüle

@@ -0,0 +1,22 @@
1
+package com.ruoyi.novel.config;
2
+
3
+
4
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
5
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6
+import org.springframework.security.web.DefaultSecurityFilterChain;
7
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
8
+
9
+public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
10
+
11
+    private final TokenProvider tokenProvider;
12
+
13
+    public JwtConfigurer(TokenProvider tokenProvider) {
14
+        this.tokenProvider = tokenProvider;
15
+    }
16
+
17
+    @Override
18
+    public void configure(HttpSecurity http) {
19
+        JwtFilter customFilter = new JwtFilter(tokenProvider);
20
+        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
21
+    }
22
+}

+ 37
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/JwtFilter.java Dosyayı Görüntüle

@@ -0,0 +1,37 @@
1
+package com.ruoyi.novel.config;
2
+
3
+// 新增 JwtFilter.java
4
+import org.springframework.security.core.context.SecurityContextHolder;
5
+import org.springframework.web.filter.OncePerRequestFilter;
6
+import javax.servlet.FilterChain;
7
+import javax.servlet.ServletException;
8
+import javax.servlet.http.HttpServletRequest;
9
+import javax.servlet.http.HttpServletResponse;
10
+import java.io.IOException;
11
+
12
+public class JwtFilter extends OncePerRequestFilter {
13
+    private final TokenProvider tokenProvider;
14
+
15
+    public JwtFilter(TokenProvider tokenProvider) {
16
+        this.tokenProvider = tokenProvider;
17
+    }
18
+
19
+    @Override
20
+    protected void doFilterInternal(HttpServletRequest request,
21
+                                    HttpServletResponse response,
22
+                                    FilterChain filterChain)
23
+            throws ServletException, IOException {
24
+
25
+        String token = resolveToken(request);
26
+        if (token != null && tokenProvider.validateToken(token)) {
27
+            SecurityContextHolder.getContext().setAuthentication(
28
+                    tokenProvider.getAuthentication(token)
29
+            );
30
+        }
31
+        filterChain.doFilter(request, response);
32
+    }
33
+
34
+    private String resolveToken(HttpServletRequest request) {
35
+        // 从Header提取Token的逻辑
36
+    }
37
+}

+ 30
- 17
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/SecurityConfig.java Dosyayı Görüntüle

@@ -1,6 +1,9 @@
1 1
 package com.ruoyi.novel.config;
2 2
 
3
-import com.ruoyi.novel.utils.JwtTokenProvider;
3
+import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
4
+import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
5
+import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
6
+import com.ruoyi.novel.service.CustomUserDetailsService;
4 7
 import org.springframework.beans.factory.annotation.Autowired;
5 8
 import org.springframework.context.annotation.Bean;
6 9
 import org.springframework.context.annotation.Configuration;
@@ -13,8 +16,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
13 16
 import org.springframework.security.config.http.SessionCreationPolicy;
14 17
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
15 18
 import org.springframework.security.crypto.password.PasswordEncoder;
16
-import org.springframework.web.cors.CorsConfiguration;
17
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
19
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
18 20
 import org.springframework.web.filter.CorsFilter;
19 21
 
20 22
 @Configuration
@@ -23,14 +25,23 @@ import org.springframework.web.filter.CorsFilter;
23 25
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
24 26
 
25 27
     @Autowired
26
-    private JwtTokenProvider tokenProvider;
28
+    private CustomUserDetailsService customUserDetailsService;
27 29
 
28 30
     @Autowired
29
-    private CustomUserDetailsService userDetailsService;
31
+    private TokenProvider tokenProvider;
32
+
33
+    @Autowired
34
+    private CorsFilter corsFilter;
35
+
36
+    @Autowired
37
+    private AuthenticationEntryPointImpl unauthorizedHandler;
38
+
39
+    @Autowired
40
+    private LogoutSuccessHandlerImpl logoutSuccessHandler;
30 41
 
31 42
     @Override
32 43
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
33
-        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
44
+        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
34 45
     }
35 46
 
36 47
     @Bean
@@ -51,24 +62,26 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
51 62
                 .csrf().disable()
52 63
                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
53 64
                 .and()
65
+                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
66
+                .and()
54 67
                 .authorizeRequests()
55 68
                 .antMatchers("/api/auth/**").permitAll()
56
-                .antMatchers("/api/novel/**").permitAll() // 公开访问小说数据
57
-                .antMatchers("/admin/**").hasRole("ADMIN") // 管理员权限
69
+                //.antrimatchers("/api/novel/**").permitAll()
70
+                .antMatchers("/admin/**").hasRole("ADMIN")
58 71
                 .anyRequest().authenticated()
59 72
                 .and()
60 73
                 .apply(new JwtConfigurer(tokenProvider));
74
+
75
+        http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
76
+        http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
77
+
78
+        http.logout()
79
+                .logoutUrl("/api/auth/logout")
80
+                .logoutSuccessHandler(logoutSuccessHandler);
61 81
     }
62 82
 
63 83
     @Bean
64
-    public CorsFilter corsFilter() {
65
-        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
66
-        CorsConfiguration config = new CorsConfiguration();
67
-        config.setAllowCredentials(true);
68
-        config.addAllowedOrigin("*");
69
-        config.addAllowedHeader("*");
70
-        config.addAllowedMethod("*");
71
-        source.registerCorsConfiguration("/**", config);
72
-        return new CorsFilter(source);
84
+    public JwtAuthenticationTokenFilter authenticationTokenFilter() {
85
+        return new JwtAuthenticationTokenFilter();
73 86
     }
74 87
 }

+ 129
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/config/TokenProvider.java Dosyayı Görüntüle

@@ -0,0 +1,129 @@
1
+package com.ruoyi.novel.config;
2
+
3
+import com.ruoyi.common.constant.CacheConstants;
4
+import com.ruoyi.common.constant.Constants;
5
+import com.ruoyi.common.core.domain.model.LoginUser;
6
+import com.ruoyi.common.core.redis.RedisCache;
7
+import com.ruoyi.common.utils.SecurityUtils;
8
+import com.ruoyi.common.utils.StringUtils;
9
+import com.ruoyi.common.utils.uuid.IdUtils;
10
+import io.jsonwebtoken.Claims;
11
+import io.jsonwebtoken.Jwts;
12
+import io.jsonwebtoken.SignatureAlgorithm;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.beans.factory.annotation.Value;
15
+import org.springframework.stereotype.Component;
16
+
17
+import javax.servlet.http.HttpServletRequest;
18
+import java.util.HashMap;
19
+import java.util.Map;
20
+import java.util.concurrent.TimeUnit;
21
+
22
+@Component
23
+public class TokenProvider {
24
+    // 令牌秘钥
25
+    @Value("${token.secret}")
26
+    private String secret;
27
+
28
+    // 令牌有效期(默认30分钟)
29
+    @Value("${token.expireTime}")
30
+    private int expireTime;
31
+    // 令牌自定义标识
32
+    @Value("${token.header}")
33
+    private String header;
34
+    protected static final long MILLIS_SECOND = 1000;
35
+    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
36
+    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
37
+
38
+    @Autowired
39
+    private RedisCache redisCache;
40
+
41
+    /**
42
+     * 获取用户身份信息
43
+     */
44
+    public LoginUser getLoginUser(HttpServletRequest request) {
45
+        // 获取请求携带的令牌
46
+        String token = getToken(request);
47
+        if (StringUtils.isNotEmpty(token)) {
48
+            Claims claims = parseToken(token);
49
+            // 解析对应的权限以及用户信息
50
+            String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
51
+            String userKey = getTokenKey(uuid);
52
+            return redisCache.getCacheObject(userKey);
53
+        }
54
+        return null;
55
+    }
56
+    private String getToken(HttpServletRequest request)
57
+    {
58
+        String token = request.getHeader(header);
59
+        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
60
+        {
61
+            token = token.replace(Constants.TOKEN_PREFIX, "");
62
+        }
63
+        return token;
64
+    }
65
+    /**
66
+     * 创建令牌
67
+     */
68
+    public String createToken(LoginUser loginUser) {
69
+        String token = loginUser.getToken();
70
+        if (StringUtils.isEmpty(token)) {
71
+            token = IdUtils.fastUUID();
72
+            loginUser.setToken(token);
73
+
74
+//            token = StringUtils.uuid();
75
+//            loginUser.setToken(token);
76
+            refreshToken(loginUser);
77
+        }
78
+
79
+        Map<String, Object> claims = new HashMap<>();
80
+        claims.put(Constants.LOGIN_USER_KEY, token);
81
+        return createToken(claims);
82
+    }
83
+
84
+    /**
85
+     * 验证令牌有效期,相差不足20分钟,自动刷新缓存
86
+     */
87
+    public void verifyToken(LoginUser loginUser) {
88
+        long expireTime = loginUser.getExpireTime();
89
+        long currentTime = System.currentTimeMillis();
90
+        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
91
+            refreshToken(loginUser);
92
+        }
93
+    }
94
+
95
+    /**
96
+     * 刷新令牌有效期
97
+     */
98
+    public void refreshToken(LoginUser loginUser) {
99
+        loginUser.setLoginTime(System.currentTimeMillis());
100
+        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
101
+        // 根据uuid将loginUser缓存
102
+        String userKey = getTokenKey(loginUser.getToken());
103
+        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
104
+    }
105
+
106
+    /**
107
+     * 从数据声明生成令牌
108
+     */
109
+    private String createToken(Map<String, Object> claims) {
110
+        return Jwts.builder()
111
+                .setClaims(claims)
112
+                .signWith(SignatureAlgorithm.HS512, secret)
113
+                .compact();
114
+    }
115
+
116
+    /**
117
+     * 从令牌中获取数据声明
118
+     */
119
+    private Claims parseToken(String token) {
120
+        return Jwts.parser()
121
+                .setSigningKey(secret)
122
+                .parseClaimsJws(token)
123
+                .getBody();
124
+    }
125
+
126
+    private String getTokenKey(String uuid) {
127
+        return CacheConstants.LOGIN_TOKEN_KEY + uuid;
128
+    }
129
+}

+ 32
- 24
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/controller/NovelController.java Dosyayı Görüntüle

@@ -11,8 +11,10 @@ import com.ruoyi.common.core.page.TableSupport;
11 11
 import com.ruoyi.novel.domain.AuthorApplicationDTO;
12 12
 import com.ruoyi.novel.domain.Novel;
13 13
 import com.ruoyi.novel.mapper.NovelMapper;
14
+import com.ruoyi.novel.service.IAuthService;
14 15
 import com.ruoyi.novel.service.NovelSearchService;
15 16
 import com.ruoyi.novel.service.NovelService;
17
+import jdk.jfr.Category;
16 18
 import org.springframework.beans.factory.annotation.Autowired;
17 19
 import org.springframework.http.ResponseEntity;
18 20
 import org.springframework.web.bind.annotation.*;
@@ -25,21 +27,31 @@ import java.util.Map;
25 27
 @RequestMapping("/api/novel")
26 28
 public class NovelController extends BaseController {
27 29
 
30
+
28 31
     @Autowired
29
-    private NovelService novelService;
32
+    private final NovelService novelService;
30 33
 
31 34
     @Autowired
32
-    private IAuthService authService;
35
+    private final IAuthService authService;
33 36
     @Autowired
34 37
     private NovelSearchService searchService;
35 38
     @Autowired
36 39
     private NovelMapper novelMapper;
40
+    // 构造函数注入
41
+    public NovelController(NovelService novelService, IAuthService authService) {
42
+        this.novelService = novelService;
43
+        this.authService = authService;
44
+    }
45
+    // 添加所有缺失方法
46
+    @GetMapping("/categories")
47
+    public List<Category> getAllCategories() {
48
+        return novelService.getAllCategories();
49
+    }
50
+    @GetMapping("/hot")
51
+    public List<Novel> getHotNovels() {
52
+        return novelService.getHotNovels();
53
+    }
37 54
 
38
-    // 获取小说分类
39
-//    @GetMapping("/novel/categories")
40
-//    public ResponseEntity<?> getCategories() {
41
-//        return ResponseEntity.ok(novelService.getAllCategories());
42
-//    }
43 55
     // 获取分类列表
44 56
     @GetMapping("/categories")
45 57
     public AjaxResult getCategories() {
@@ -51,11 +63,6 @@ public class NovelController extends BaseController {
51 63
 //    }
52 64
     // 获取热门小说
53 65
 // 获取热门小说
54
-@GetMapping("/hot")
55
-public TableDataInfo getHotNovels() {
56
-    List<Novel> list = novelService.getHotNovels();
57
-    return getDataTable(list);
58
-}
59 66
 //    @GetMapping("/novel/hot")
60 67
 //    public ResponseEntity<?> getHotNovels() {
61 68
 //        return ResponseEntity.ok(novelService.getHotNovels());
@@ -66,8 +73,8 @@ public TableDataInfo getHotNovels() {
66 73
 public TableDataInfo getNovelsByCategory(@RequestParam(required = false) Long categoryId) {
67 74
     return novelService.getNovelsByCategory(categoryId);
68 75
 }
69
-//    @GetMapping("/novel/list")
70
-//    public ResponseEntity<?> getNovelsByCategory(@RequestParam(required = false) Integer categoryId) {
76
+//    @GetMapping("/list")
77
+//    public ResponseEntity<?> getNovelsByCategory(@RequestParam(required = false) Long categoryId) {
71 78
 //        return ResponseEntity.ok(novelService.getNovelsByCategory(categoryId));
72 79
 //    }
73 80
     // 获取小说章节列表
@@ -102,16 +109,17 @@ public AjaxResult getChapterContent(@PathVariable Long chapterId) {
102 109
 
103 110
 
104 111
     // 提交作家申请
105
-    @PostMapping("/author/apply")
106
-    public AjaxResult applyAuthor(@RequestBody AuthorApplicationDTO dto,
107
-                                  @RequestHeader("Authorization") String token) {
108
-        try {
109
-            Long userId = authService.getUserIdFromToken(token);
110
-            novelService.submitAuthorApplication(dto, userId);
111
-            return AjaxResult.success("申请已提交");
112
-        } catch (Exception e) {
113
-            return AjaxResult.error(e.getMessage());
114
-        }
112
+    @PostMapping("/apply-author")
113
+    public ResponseEntity<?> submitAuthorApplication(
114
+            @RequestBody AuthorApplicationDTO dto,
115
+            @RequestHeader("Authorization") String token) {
116
+
117
+        // 修复token处理
118
+        String cleanToken = token.replace("Bearer ", "");
119
+        Long userId = authService.getUserIdFromToken(cleanToken);
120
+
121
+        novelService.submitAuthorApplication(dto, userId);
122
+        return ResponseEntity.ok().build();
115 123
     }
116 124
     @GetMapping("/list")
117 125
     public TableDataInfo list(Novel novel) {

+ 3
- 1
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/AuthorApplicationMapper.java Dosyayı Görüntüle

@@ -1,10 +1,12 @@
1 1
 package com.ruoyi.novel.mapper;
2 2
 
3 3
 import com.ruoyi.novel.domain.AuthorApplication;
4
+import org.apache.ibatis.annotations.Mapper;
4 5
 
5 6
 import java.util.List;
6 7
 
7
-// AuthorApplicationMapper.java
8
+// AuthorApplicationMapper.java\
9
+@Mapper
8 10
 public interface AuthorApplicationMapper {
9 11
     AuthorApplication selectAuthorApplicationById(Long id);
10 12
     AuthorApplication selectPendingApplication(Long userId);

+ 2
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/CategoryMapper.java Dosyayı Görüntüle

@@ -1,10 +1,12 @@
1 1
 package com.ruoyi.novel.mapper;
2 2
 
3 3
 import jdk.jfr.Category;
4
+import org.apache.ibatis.annotations.Mapper;
4 5
 
5 6
 import java.util.List;
6 7
 
7 8
 // CategoryMapper.java
9
+@Mapper
8 10
 public interface CategoryMapper {
9 11
     List<Category> selectCategoryList(Category category);
10 12
 }

+ 2
- 1
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/ChapterMapper.java Dosyayı Görüntüle

@@ -1,9 +1,10 @@
1 1
 package com.ruoyi.novel.mapper;
2 2
 
3 3
 import com.ruoyi.novel.domain.Chapter;
4
+import org.apache.ibatis.annotations.Mapper;
4 5
 
5 6
 import java.util.List;
6
-
7
+@Mapper
7 8
 public interface ChapterMapper {
8 9
     Chapter selectChapterById(Long id);
9 10
     int insertChapter(Chapter chapter);

+ 2
- 1
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/mapper/NovelMapper.java Dosyayı Görüntüle

@@ -2,13 +2,14 @@ package com.ruoyi.novel.mapper;
2 2
 
3 3
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 4
 import com.ruoyi.novel.domain.Novel;
5
+import org.apache.ibatis.annotations.Mapper;
5 6
 import org.apache.ibatis.annotations.Param;
6 7
 import org.apache.ibatis.annotations.Select;
7 8
 import org.apache.ibatis.annotations.Update;
8 9
 
9 10
 import java.util.List;
10 11
 import java.util.Map;
11
-
12
+@Mapper
12 13
 // NovelMapper.java
13 14
 public interface NovelMapper extends BaseMapper<Novel> {
14 15
     List<Novel> selectNovelList(Novel novel);

+ 48
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/CustomUserDetailsService.java Dosyayı Görüntüle

@@ -0,0 +1,48 @@
1
+package com.ruoyi.novel.service;
2
+
3
+import com.ruoyi.common.core.domain.entity.SysUser;
4
+import com.ruoyi.common.core.domain.model.LoginUser;
5
+import com.ruoyi.common.enums.UserStatus;
6
+import com.ruoyi.common.exception.ServiceException;
7
+import com.ruoyi.common.utils.StringUtils;
8
+import com.ruoyi.framework.web.service.SysPermissionService;
9
+import com.ruoyi.system.service.ISysUserService;
10
+import org.slf4j.Logger;
11
+import org.slf4j.LoggerFactory;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.security.core.userdetails.UserDetails;
14
+import org.springframework.security.core.userdetails.UserDetailsService;
15
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
16
+import org.springframework.stereotype.Service;
17
+
18
+@Service
19
+public class CustomUserDetailsService implements UserDetailsService {
20
+    private static final Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);
21
+
22
+    @Autowired
23
+    private ISysUserService userService;
24
+
25
+    @Autowired
26
+    private SysPermissionService permissionService;
27
+
28
+    @Override
29
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
30
+        SysUser user = userService.selectUserByUserName(username);
31
+        if (StringUtils.isNull(user)) {
32
+            log.info("登录用户:{} 不存在.", username);
33
+            throw new ServiceException("登录用户:" + username + " 不存在");
34
+        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
35
+            log.info("登录用户:{} 已被删除.", username);
36
+            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
37
+        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
38
+            log.info("登录用户:{} 已被停用.", username);
39
+            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
40
+        }
41
+
42
+        return createLoginUser(user);
43
+    }
44
+
45
+    public UserDetails createLoginUser(SysUser user) {
46
+        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
47
+    }
48
+}

+ 9
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/IAuthService.java Dosyayı Görüntüle

@@ -0,0 +1,9 @@
1
+package com.ruoyi.novel.service;
2
+
3
+import com.ruoyi.common.core.domain.model.LoginUser;
4
+
5
+// IAuthService.java
6
+public interface IAuthService {
7
+    LoginUser getLoginUser(String token);
8
+    Long getUserIdFromToken(String token);
9
+}

+ 18
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/NovelService.java Dosyayı Görüntüle

@@ -1,6 +1,10 @@
1 1
 package com.ruoyi.novel.service;
2 2
 
3
+import com.ruoyi.novel.domain.AuthorApplication;
4
+import com.ruoyi.novel.domain.AuthorApplicationDTO;
5
+import com.ruoyi.novel.domain.Chapter;
3 6
 import com.ruoyi.novel.domain.Novel;
7
+import jdk.jfr.Category;
4 8
 import org.springframework.transaction.annotation.Transactional;
5 9
 
6 10
 import java.util.List;
@@ -13,4 +17,18 @@ public interface NovelService {
13 17
     int updateNovel(Novel novel);
14 18
     int deleteNovelByIds(Long[] ids);
15 19
     Novel selectNovelById(Long id);
20
+
21
+    void processNovelUpload(NovelUploadDTO dto);
22
+    void processChapterUpload(ChapterUploadDTO dto);
23
+    List<AuthorApplication> getAuthorApplications();
24
+    void approveAuthorApplication(Long applicationId);
25
+    void rejectAuthorApplication(Long applicationId);
26
+
27
+    // 添加所有缺失方法声明
28
+    List<Category> getAllCategories();
29
+    List<Novel> getHotNovels();
30
+    List<Novel> getNovelsByCategory(Long categoryId);
31
+    List<Chapter> getChaptersByNovelId(Long novelId);
32
+    String getChapterContent(Long chapterId);
33
+    void submitAuthorApplication(AuthorApplicationDTO dto, Long userId);
16 34
 }

+ 17
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/TokenService.java Dosyayı Görüntüle

@@ -0,0 +1,17 @@
1
+package com.ruoyi.novel.service;
2
+
3
+import com.ruoyi.common.core.domain.model.LoginUser;
4
+import org.springframework.stereotype.Service;
5
+
6
+@Service
7
+public class TokenService {
8
+    // 添加缺失方法
9
+    public LoginUser getLoginUser(String token) {
10
+        // 实际解析逻辑
11
+        return new LoginUser(parseUserId(token), parseUserDetails(token));
12
+    }
13
+
14
+    public Long parseUserId(String token) {
15
+        // 解析用户ID的实现
16
+    }
17
+}

+ 5
- 16
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/impl/AuthServiceImpl.java Dosyayı Görüntüle

@@ -1,6 +1,7 @@
1 1
 package com.ruoyi.novel.service.impl;
2 2
 
3 3
 import com.ruoyi.common.core.domain.model.LoginUser;
4
+import com.ruoyi.novel.service.IAuthService;
4 5
 import com.ruoyi.novel.service.UserRepository;
5 6
 import com.ruoyi.novel.utils.JwtTokenProvider;
6 7
 import org.springframework.beans.factory.annotation.Autowired;
@@ -8,7 +9,7 @@ import org.springframework.security.core.token.TokenService;
8 9
 import org.springframework.stereotype.Service;
9 10
 
10 11
 @Service
11
-public class AuthServiceImpl implements IAuthService  {
12
+public class AuthServiceImpl implements IAuthService {
12 13
 
13 14
     @Autowired
14 15
     private JwtTokenProvider tokenProvider;
@@ -19,24 +20,12 @@ public class AuthServiceImpl implements IAuthService  {
19 20
     private TokenService tokenService;
20 21
 
21 22
     @Override
22
-    public Long getUserIdFromToken(String token) {
23
-        // 若依标准方式获取用户ID
24
-        LoginUser loginUser = tokenService.getLoginUser(token);
25
-        if (loginUser != null && loginUser.getUser() != null) {
26
-            return loginUser.getUser().getUserId();
27
-        }
28
-        throw new RuntimeException("无效的Token或用户不存在");
23
+    public LoginUser getLoginUser(String token) {
24
+        return tokenService.getLoginUser(token);
29 25
     }
30
-    @Autowired
31
-    private TokenService tokenService;
32 26
 
33 27
     @Override
34 28
     public Long getUserIdFromToken(String token) {
35
-        // 若依标准方式获取用户ID
36
-        LoginUser loginUser = tokenService.getLoginUser(token);
37
-        if (loginUser != null && loginUser.getUser() != null) {
38
-            return loginUser.getUser().getUserId();
39
-        }
40
-        throw new RuntimeException("无效的Token或用户不存在");
29
+        return tokenService.parseUserId(token);
41 30
     }
42 31
 }

+ 16
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/service/impl/NovelServiceImpl.java Dosyayı Görüntüle

@@ -4,6 +4,7 @@ import com.ruoyi.common.constant.HttpStatus;
4 4
 import com.ruoyi.common.core.domain.entity.SysUser;
5 5
 import com.ruoyi.common.core.page.PageDomain;
6 6
 import com.ruoyi.common.core.page.TableDataInfo;
7
+import com.ruoyi.common.utils.DateUtils;
7 8
 import com.ruoyi.common.utils.PageUtils;
8 9
 import com.ruoyi.common.utils.StringUtils;
9 10
 import com.ruoyi.novel.config.TableSupport;
@@ -342,5 +343,20 @@ public class NovelServiceImpl implements NovelService {
342 343
         return novelMapper.selectById(id);
343 344
     }
344 345
 
346
+    @Override
347
+    public void processNovelUpload(NovelUploadDTO dto) {
348
+
349
+    }
350
+
351
+    @Override
352
+    public void processChapterUpload(ChapterUploadDTO dto) {
353
+
354
+    }
355
+
356
+    @Override
357
+    public List<AuthorApplication> getAuthorApplications() {
358
+        return null;
359
+    }
360
+
345 361
     // 其他CRUD方法...
346 362
 }

+ 3
- 0
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java Dosyayı Görüntüle

@@ -1,6 +1,8 @@
1 1
 package com.ruoyi.system.mapper;
2 2
 
3 3
 import java.util.List;
4
+
5
+import org.apache.ibatis.annotations.Mapper;
4 6
 import org.apache.ibatis.annotations.Param;
5 7
 import com.ruoyi.common.core.domain.entity.SysUser;
6 8
 
@@ -9,6 +11,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
9 11
  * 
10 12
  * @author ruoyi
11 13
  */
14
+@Mapper
12 15
 public interface SysUserMapper
13 16
 {
14 17
     /**

Loading…
İptal
Kaydet