feat(*):基本代码完成

This commit is contained in:
mingchen 2025-04-18 09:11:40 +08:00
commit 6d0159ad34
352 changed files with 25184 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

32
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="common" />
<module name="xxl-job" />
<module name="mysql" />
<module name="api" />
<module name="mvc" />
<module name="rabbitmq" />
<module name="es" />
<module name="redis" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="api" options="-parameters" />
<module name="common" options="-parameters" />
<module name="es" options="-parameters" />
<module name="mvc" options="-parameters" />
<module name="mysql" options="-parameters" />
<module name="rabbitmq" options="-parameters" />
<module name="redis" options="-parameters" />
<module name="xxl-job" options="-parameters" />
</option>
</component>
</project>

17
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/es/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/mvc/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/mysql/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/parent/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/parent/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/rabbitmq/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/redis/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/xxl-job/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://maven.aliyun.com/nexus/content/repositories/central/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

12
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
</project>

View File

@ -0,0 +1,57 @@
2025-04-17 09:39:41.724 INFO 5255 --- [main] com.mingchen.BlogApiApplicationTests : Starting BlogApiApplicationTests on mingchendeMac-mini.local with PID 5255 (started by mingchen in /Users/mingchen/code/java/other/legalAid/api)
2025-04-17 09:39:41.724 DEBUG 5255 --- [main] com.mingchen.BlogApiApplicationTests : Running with Spring Boot v2.2.7.RELEASE, Spring v5.2.6.RELEASE
2025-04-17 09:39:41.724 INFO 5255 --- [main] com.mingchen.BlogApiApplicationTests : The following profiles are active: dev
2025-04-17 09:39:41.975 INFO 5255 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2025-04-17 09:39:41.977 INFO 5255 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-04-17 09:39:41.991 INFO 5255 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9ms. Found 0 Redis repository interfaces.
2025-04-17 09:39:42.303 ERROR 5255 --- [main] c.b.m.core.MybatisConfiguration : mapper[com.mingchen.mapper.CategoriesMapper.getAllCategoriesWithLaws] is ignored, because it exists, maybe from xml file
2025-04-17 09:39:42.999 INFO 5255 --- [main] n.b.p.u.AbstractUserAgentAnalyzerDirect : - Loaded 90 files in 390 msec using expression: classpath*:UserAgents/**/*.yaml
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion :
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : /-----------------------------------------------------------\
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : | Yauaa 5.20 (v5.20 @ 2020-11-22T15:39:16Z) |
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : +-----------------------------------------------------------+
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : | For more information: https://yauaa.basjes.nl |
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : | Copyright (C) 2013-2020 Niels Basjes - License Apache 2.0 |
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion : \-----------------------------------------------------------/
2025-04-17 09:39:43.000 INFO 5255 --- [main] n.b.parse.useragent.utils.YauaaVersion :
2025-04-17 09:39:43.006 INFO 5255 --- [main] n.b.p.u.AbstractUserAgentAnalyzerDirect : Building all needed matchers for the requested 11 fields.
2025-04-17 09:39:43.150 INFO 5255 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2025-04-17 09:39:43.312 INFO 5255 --- [main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2025-04-17 09:39:43.315 INFO 5255 --- [main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2025-04-17 09:39:43.316 INFO 5255 --- [main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@665902eb
2025-04-17 09:39:43.367 INFO 5255 --- [main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3ad9f06a, org.springframework.security.web.context.SecurityContextPersistenceFilter@68769265, org.springframework.security.web.header.HeaderWriterFilter@5d6de24e, org.springframework.web.filter.CorsFilter@72cb5d7d, org.springframework.security.web.authentication.logout.LogoutFilter@4603845b, com.mingchen.common.config.JwtLoginFilter@19b89a23, com.mingchen.common.config.JwtFilter@6d1a4522, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@68c1d547, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7951a08c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1835b24b, org.springframework.security.web.session.SessionManagementFilter@6dd0d9a2, org.springframework.security.web.access.ExceptionTranslationFilter@5680228, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@b88d294]
2025-04-17 09:39:43.391 INFO 5255 --- [main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2025-04-17 09:39:43.391 INFO 5255 --- [main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2025-04-17 09:39:43.395 INFO 5255 --- [main] com.mingchen.BlogApiApplicationTests : Started BlogApiApplicationTests in 1.84 seconds (JVM running for 2.462)
2025-04-17 09:39:43.499 INFO 5255 --- [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-04-17 09:39:43.996 INFO 5255 --- [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-04-17 09:39:44.000 DEBUG 5255 --- [main] c.m.m.C.getAllCategoriesWithLaws : ==> Preparing: SELECT c.category_id, c.name, c.description, l.law_id, l.category_id AS law_category_id, l.title, l.summary, l.content, l.effective_date, l.reference_url FROM categories c LEFT JOIN laws l ON c.category_id = l.category_id ORDER BY c.category_id, l.law_id
2025-04-17 09:39:44.009 DEBUG 5255 --- [main] c.m.m.C.getAllCategoriesWithLaws : ==> Parameters:
2025-04-17 09:39:44.078 DEBUG 5255 --- [main] c.m.m.C.getAllCategoriesWithLaws : <== Total: 5
2025-04-17 09:39:44.088 INFO 5255 --- [SpringContextShutdownHook] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED paused.
2025-04-17 09:39:44.088 INFO 5255 --- [SpringContextShutdownHook] o.s.s.quartz.SchedulerFactoryBean : Shutting down Quartz Scheduler
2025-04-17 09:39:44.089 INFO 5255 --- [SpringContextShutdownHook] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED shutting down.
2025-04-17 09:39:44.089 INFO 5255 --- [SpringContextShutdownHook] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED paused.
2025-04-17 09:39:44.089 INFO 5255 --- [SpringContextShutdownHook] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED shutdown complete.
2025-04-17 09:39:44.089 INFO 5255 --- [SpringContextShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2025-04-17 09:39:44.098 INFO 5255 --- [SpringContextShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-04-17 09:39:44.102 INFO 5255 --- [SpringContextShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2025-04-17 09:42:54.720 INFO 5460 --- [main] com.mingchen.BlogApiApplicationTests : Starting BlogApiApplicationTests on mingchendeMac-mini.local with PID 5460 (started by mingchen in /Users/mingchen/code/java/other/legalAid/api)
2025-04-17 09:42:54.722 DEBUG 5460 --- [main] com.mingchen.BlogApiApplicationTests : Running with Spring Boot v2.2.7.RELEASE, Spring v5.2.6.RELEASE
2025-04-17 09:42:54.722 INFO 5460 --- [main] com.mingchen.BlogApiApplicationTests : The following profiles are active: dev
2025-04-17 09:42:54.991 INFO 5460 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2025-04-17 09:42:54.993 INFO 5460 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2025-04-17 09:42:55.011 INFO 5460 --- [main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 12ms. Found 0 Redis repository interfaces.
2025-04-17 09:42:55.355 ERROR 5460 --- [main] c.b.m.core.MybatisConfiguration : mapper[com.mingchen.mapper.CategoriesMapper.getAllCategoriesWithLaws] is ignored, because it exists, maybe from xml file

202
api/pom.xml Normal file
View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mingchen</groupId>
<artifactId>api</artifactId>
<version>0.0.1</version>
<name>api</name>
<url>https://mingchen.com/</url>
<description>API</description>
<properties>
<java.version>1.8</java.version>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>mingchen</groupId>
<artifactId>common</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.4.3</version>
</dependency>
<!-- spring aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!-- mybatis分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<optional>true</optional>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Markdown 转 HTML -->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.15.2</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-heading-anchor</artifactId>
<version>0.15.2</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.15.2</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-strikethrough</artifactId>
<version>0.15.2</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-task-list-items</artifactId>
<version>0.15.2</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
<!-- 解析客户端操作系统、浏览器 -->
<dependency>
<groupId>nl.basjes.parse.useragent</groupId>
<artifactId>yauaa</artifactId>
<version>5.20</version>
</dependency>
<!-- devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version> <!-- 或者最新的版本 -->
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version> <!-- 或者最新的版本 -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
package com.mingchen;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.mingchen.mapper")
public class BlogApiApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApiApplication.class, args);
}
}

View File

@ -0,0 +1,25 @@
package com.mingchen.api.open;
import com.mingchen.model.vo.Result;
import com.mingchen.service.impl.PlatformServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/admin/ai")
public class ApiController {
@Autowired
private PlatformServiceImpl platformService;
@PostMapping()
public Result getChat(@RequestParam String content) {
return platformService.getChat(content);
}
@GetMapping("/test")
public Result test(){
return Result.ok("test");
}
}

View File

@ -0,0 +1,30 @@
package com.mingchen.api.open;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ICategoriesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@RestController
@RequestMapping("admin/categories")
public class CategoriesController {
@Autowired
private ICategoriesService categoriesService;
@GetMapping()
public Result getCategories(){
return categoriesService.getCategories();
}
}

View File

@ -0,0 +1,20 @@
package com.mingchen.api.open;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@RestController
@RequestMapping("/laws")
public class LawsController {
}

View File

@ -0,0 +1,33 @@
package com.mingchen.api.open;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mingchen.entity.LearningResources;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ILearningResourcesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@RestController
@RequestMapping("admin/learningResources")
public class LearningResourcesController {
@Autowired
private ILearningResourcesService learningResourcesService;
@GetMapping("/page")
public Result getLearningResources(@RequestParam(defaultValue = "1") int current,
@RequestParam(defaultValue = "10") int size){
return learningResourcesService.getLearningResources(current, size);
}
}

View File

@ -0,0 +1,35 @@
package com.mingchen.api.open;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ILegalAidResourcesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@RestController
@RequestMapping("admin/legalAidResources")
public class LegalAidResourcesController {
@Autowired
private ILegalAidResourcesService legalAidResourcesService;
@GetMapping("/page")
public Result getLegalAidResources(@RequestParam(defaultValue = "1") int current,
@RequestParam(defaultValue = "10") int size){
return legalAidResourcesService.getLegalAidResources(current,size);
}
}

View File

@ -0,0 +1,50 @@
package com.mingchen.api.open;
import com.itmingchen.common.utils.JwtTool;
import com.mingchen.entity.User;
import com.mingchen.model.dto.LoginInfoDTO;
import com.mingchen.model.vo.Result;
import com.mingchen.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 前台登录
* @Date: 2024-09-02
*/
@RestController
@RequestMapping("/admin/login")
public class LoginController {
@Autowired
UserService userService;
@Resource
private JwtTool jwtTool;
/**
* 登录成功后签发博主身份Token
*
* @param loginInfoDTO
* @return
*/
@PostMapping()
public Result login(@RequestBody LoginInfoDTO loginInfoDTO) {
// User user = userService.findUserByUsernameAndPassword(loginInfoDTO.getUsername(), loginInfoDTO.getPassword());
// if (!"ROLE_admin".equals(user.getRole())) {
// return Result.create(403, "无权限");
// }
User user = userService.findUserByEmailAndPassword(loginInfoDTO);
user.setPassword(null);
String jwt = jwtTool.createToken(user.getUserId(), user.getEmail());
Map<String, Object> map = new HashMap<>();
map.put("user", user);
map.put("token", jwt);
return Result.ok("登录成功", map);
}
}

View File

@ -0,0 +1,41 @@
package com.mingchen.api.open;
import com.mingchen.model.dto.RegisterReqDTO;
import com.mingchen.model.vo.Result;
import com.mingchen.service.RegisterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName RegisterController
* @Description: 注册
* @Author: 3177583214@qq.com
*/
@RestController
@RequestMapping("/register")
public class RegisterController {
@Autowired
private RegisterService registerService;
/**
* 发送验证码
* @param userEmail 邮箱
*/
@PostMapping("/sendEmail")
public void send(@RequestParam String userEmail){
registerService.send(userEmail);
}
/**
* 注册
* @param registerReqDTO 注册信息
*/
@PostMapping()
public Result institutionRegister(@RequestBody RegisterReqDTO registerReqDTO) {
Result res = registerService.register(registerReqDTO);
return res;
}
}

View File

@ -0,0 +1,21 @@
package com.mingchen.api.open;
import com.mingchen.model.vo.Result;
import com.mingchen.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin")
public class UserController {
@Autowired
UserService userService;
@GetMapping("/getUserInfo")
public Result getUserInfo(){
return userService.getUserInfo();
}
}

View File

@ -0,0 +1,20 @@
package com.mingchen.api.sdk;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.mingchen.model.req.AiReq;
import com.mingchen.model.resp.AiResp;
import org.springframework.stereotype.Component;
@Component
public class AiService {
public AiResp getChatApi(String url, String token,AiReq aiReq){
HttpResponse resp = HttpRequest.post(url)
.header("Authorization","Bearer "+token)
.body(JSONUtil.toJsonStr(aiReq))
.timeout(200000)
.execute();
return JSONUtil.toBean(resp.body(), AiResp.class);
}
}

View File

@ -0,0 +1,48 @@
package com.mingchen.common.config;
import com.itmingchen.common.expcetions.ErrorRequestException;
import com.itmingchen.common.utils.StringUtils;
import com.mingchen.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserServiceImpl userService; // UserDetailsService 的实现
@Autowired
private PasswordEncoder passwordEncoder; // 密码编码器
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String email = authentication.getName(); // 提取用户名电子邮件
String password = authentication.getCredentials().toString(); // 提取密码
// 校验邮箱是否存在
if(StringUtils.isEmpty(email)) throw new ErrorRequestException("请输入邮箱");
// 校验密码是否存在
if(StringUtils.isEmpty(password)) throw new ErrorRequestException("请输入密码");
// 加载用户信息
UserDetails userDetails = userService.loadUserByUsername(email);
// 验证密码
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("密码错误,请重新输入");
}
// 如果密码匹配返回一个认证对象
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}

View File

@ -0,0 +1,70 @@
package com.mingchen.common.config;
import com.itmingchen.common.utils.JacksonUtils;
import com.itmingchen.common.utils.JwtTool;
import com.mingchen.model.vo.Result;
import io.jsonwebtoken.Claims;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;
/**
* @Description: JWT请求过滤器
* @Date: 2024-07-21
*/
public class JwtFilter extends GenericFilterBean {
// 定义放行的路径和对应方法
private static final Set<String> PERMIT_PATHS = Set.of(
"/register", // POST
"register/sendEmail" // POST
);
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//后台管理路径外的请求直接跳过
String path = request.getServletPath();
HttpMethod method = HttpMethod.resolve(request.getMethod());
// 统一判断路径和方法是否放行
if (PERMIT_PATHS.contains(path) && HttpMethod.POST == method) {
filterChain.doFilter(request, response);
return;
}
String jwt = request.getHeader("Authorization");
if (JwtTool.judgeTokenIsExist(jwt)) {
try {
Claims claims = JwtTool.getTokenBody(jwt);
String email = claims.getSubject();
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(email, null, authorities);
SecurityContextHolder.getContext().setAuthentication(token);
} catch (Exception e) {
e.printStackTrace();
response.setContentType("application/json;charset=utf-8");
Result result = Result.create(403, "凭证已失效,请重新登录!");
PrintWriter out = response.getWriter();
out.write(JacksonUtils.writeValueAsString(result));
out.flush();
out.close();
return;
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

View File

@ -0,0 +1,102 @@
package com.mingchen.common.config;
import com.itmingchen.common.expcetions.ErrorRequestException;
import com.itmingchen.common.utils.JacksonUtils;
import com.itmingchen.common.utils.JwtTool;
import com.mingchen.entity.User;
import com.mingchen.model.vo.Result;
import com.mingchen.service.LoginLogService;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: JWT登录过滤器
* @Date: 2024-07-21
*/
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {
LoginLogService loginLogService;
ThreadLocal<String> currentUsername = new ThreadLocal<>();
// 可以在这里进行增加配置管理 如进行用户登陆信息初始化
protected JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager, LoginLogService loginLogService) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(authenticationManager);
this.loginLogService = loginLogService;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
try {
if (!"POST".equals(request.getMethod())) {
throw new ErrorRequestException("请求方法错误");
}
User user = JacksonUtils.readValue(request.getInputStream(), User.class);
currentUsername.set(user.getEmail());
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
} catch (ErrorRequestException exception) {
response.setContentType("application/json;charset=utf-8");
Result result = Result.create(400, "非法请求");
PrintWriter out = response.getWriter();
out.write(JacksonUtils.writeValueAsString(result));
out.flush();
out.close();
}
return null;
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException {
// 更换验证方式 只在具体实现类更改逻辑 更符合编码设计
String jwt = JwtTool.generateToken(authResult.getName(), authResult.getAuthorities());
response.setContentType("application/json;charset=utf-8");
User user = (User) authResult.getPrincipal();
user.setPassword(null);
Map<String, Object> map = new HashMap<>();
map.put("user", user);
map.put("token", jwt);
Result result = Result.ok("登录成功", map);
PrintWriter out = response.getWriter();
out.write(JacksonUtils.writeValueAsString(result));
out.flush();
out.close();
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
response.setContentType("application/json;charset=utf-8");
String msg = exception.getMessage();
//登录不成功时会抛出对应的异常
if (exception instanceof LockedException) {
msg = "账号被锁定";
} else if (exception instanceof CredentialsExpiredException) {
msg = "密码过期";
} else if (exception instanceof AccountExpiredException) {
msg = "账号过期";
} else if (exception instanceof DisabledException) {
msg = "账号被禁用";
} else if (exception instanceof BadCredentialsException) {
msg = "用户名或密码错误";
}
PrintWriter out = response.getWriter();
out.write(JacksonUtils.writeValueAsString(Result.create(401, msg)));
out.flush();
out.close();
}
}

View File

@ -0,0 +1,31 @@
package com.mingchen.common.config;
import com.mingchen.model.vo.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.itmingchen.common.utils.JacksonUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Description: 未登录 拒绝访问
* @Date: 2024-07-21
*/
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = Result.create(403, "请登录");
out.write(JacksonUtils.writeValueAsString(result));
out.flush();
out.close();
}
}

View File

@ -0,0 +1,59 @@
package com.mingchen.common.config;
import com.mingchen.service.LoginLogService;
import com.mingchen.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @Description: Spring Security配置类
* @Date: 2024-07-19
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserServiceImpl userService;
@Autowired
LoginLogService loginLogService;
@Autowired
MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider); // 使用自定义的 AuthenticationProvider
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//禁用 csrf 防御
.csrf().disable()
//开启跨域支持
.cors().and()
//基于Token不创建会话
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
//放行获取网页标题后缀的请求
.antMatchers("/admin/webTitleSuffix").permitAll()
//任何 /admin 开头的路径下的请求都需要经过JWT验证
.antMatchers(HttpMethod.GET, "/admin/**").hasAnyRole("admin", "visitor")
.antMatchers("/admin/**").hasRole("admin")
//其它路径全部放行
.anyRequest().permitAll()
.and()
//自定义JWT过滤器
.addFilterBefore(new JwtLoginFilter("/admin/login", authenticationManager(), loginLogService), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
//未登录时返回json在前端执行重定向
.exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint);
}
}

View File

@ -0,0 +1,38 @@
package com.mingchen.common.config;
import com.mingchen.common.interceptor.AccessLimitInterceptor;
import com.mingchen.common.interceptor.UserContextInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Description: CORS跨域支持
* @Date: 2024-07-22
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
AccessLimitInterceptor accessLimitInterceptor;
@Autowired
UserContextInterceptor userContextInterceptor;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.maxAge(3600);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimitInterceptor);
registry.addInterceptor(userContextInterceptor);
}
}

View File

@ -0,0 +1,68 @@
package com.mingchen.common.config.configuration;
import com.itmingchen.common.utils.*;
import com.mingchen.common.properties.ApplicationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.annotation.Resource;
@Configuration
public class BeanConfiguration {
@Resource
private ApplicationProperties applicationProperties;
@Bean
public RedisUtils RedisUtils() {
return new RedisUtils();
}
@Bean
public RedisUtil RedisUtil() {
return new RedisUtil();
}
@Bean
public MailUtils MailUtils() {
return new MailUtils();
}
@Bean
public HashUtils HashUtils() {
return new HashUtils();
}
@Bean
public IpAddressUtils IpAddressUtils() {
return new IpAddressUtils();
}
@Bean
public JacksonUtils JacksonUtils() {
return new JacksonUtils();
}
@Bean
public JwtTool jwtTool() {
return new JwtTool(applicationProperties.getJwtKey());
}
@Bean
public UserAgentUtils UserAgentUtils() {
return new UserAgentUtils();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,22 @@
package com.mingchen.common.config.configuration;
import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class MyBatisConfig {
// 设置相关的参数信息下面有对参数的详细解释
@Bean
public PageHelper pageHelper() {
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
properties.setProperty("dialect", "Mysql");
properties.setProperty("offsetAsPageNum", "true");
properties.setProperty("rowBoundsWithCount", "true");
pageHelper.setProperties(properties);
return pageHelper;
}
}

View File

@ -0,0 +1,33 @@
package com.mingchen.common.config.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用 Jackson2JsonRedisSerializer 替换默认序列化
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
serializer.setObjectMapper(mapper);
// 设置 key value 的序列化器
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}

View File

@ -0,0 +1,30 @@
package com.mingchen.common.config.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
/**
* @Description: Redis序列化配置
* @Date: 2024-09-27
*/
@Configuration
public class RedisSerializeConfig {
/**
* 使用JSON序列化方式
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> jsonRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
template.setDefaultSerializer(serializer);
return template;
}
}

View File

@ -0,0 +1,17 @@
package com.mingchen.common.constant;
/**
* redis相关常量
*
* @author itcast
* @create 2023/8/15 14:58
**/
public class RedisConstants {
//分类名列表key
public static final String EMAIL_CODE_MAP = "emailCodeMap";
}

View File

@ -0,0 +1,74 @@
package com.mingchen.common.handler;
import com.itmingchen.common.expcetions.NotFoundException;
import com.itmingchen.common.expcetions.PersistenceException;
import com.mingchen.model.vo.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @Description: 对Controller层全局异常处理
* @RestControllerAdvice 捕获异常后返回json数据类型
* @Date: 2024-08-14
*/
@RestControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 捕获自定义的404异常
*
* @param request 请求
* @param e 自定义抛出的异常信息
* @return
*/
@ExceptionHandler(NotFoundException.class)
public Result notFoundExceptionHandler(HttpServletRequest request, NotFoundException e) {
logger.error("Request URL : {}, Exception :", request.getRequestURL(), e);
return Result.create(404, e.getMessage());
}
/**
* 捕获自定义的持久化异常
*
* @param request 请求
* @param e 自定义抛出的异常信息
* @return
*/
@ExceptionHandler(PersistenceException.class)
public Result persistenceExceptionHandler(HttpServletRequest request, PersistenceException e) {
logger.error("Request URL : {}, Exception :", request.getRequestURL(), e);
return Result.create(500, e.getMessage());
}
/**
* 捕获自定义的登录失败异常
*
* @param request 请求
* @param e 自定义抛出的异常信息
* @return
*/
@ExceptionHandler(UsernameNotFoundException.class)
public Result usernameNotFoundExceptionHandler(HttpServletRequest request, UsernameNotFoundException e) {
logger.error("Request URL : {}, Exception :", request.getRequestURL(), e);
return Result.create(401, "用户名或密码错误!");
}
/**
* 捕获其它异常
*
* @param request 请求
* @param e 异常信息
* @return
*/
@ExceptionHandler(Exception.class)
public Result exceptionHandler(HttpServletRequest request, Exception e) {
logger.error("Request URL : {}, Exception :", request.getRequestURL(), e);
return Result.create(500, e.getMessage());
}
}

View File

@ -0,0 +1,29 @@
package com.mingchen.common.interceptor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 访问控制
* @Date: 2024-04-04
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
/**
* 限制周期()
*/
int seconds();
/**
* 规定周期内限制次数
*/
int maxCount();
/**
* 触发限制时的消息提示
*/
String msg() default "操作频率过高";
}

View File

@ -0,0 +1,25 @@
package com.mingchen.common.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 访问控制拦截器
* @Author: mingchen
* @Date: 2024-04-04
*/
@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
}

View File

@ -0,0 +1,55 @@
package com.mingchen.common.interceptor;
import com.itmingchen.common.model.CurrentUserInfo;
import com.itmingchen.common.utils.JwtTool;
import com.mingchen.entity.User;
import com.mingchen.entity.UserContext;
import com.mingchen.service.impl.UserServiceImpl;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class UserContextInterceptor implements HandlerInterceptor {
@Autowired
private UserServiceImpl userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String jwt = request.getHeader("Authorization");
if (JwtTool.judgeTokenIsExist(jwt)) {
try {
// 解析 token
Claims claims = JwtTool.getTokenBody(jwt);
// 拿到 token 中的 email
String email = claims.getSubject();
// TODO 后续会改变从数据库获取用户信息的方式
// 调用方法获取用户信息
UserDetails userDetails = userService.loadUserByUsername(email);
User user = (User) userDetails;
// 将用户相关信息存入 ThreadLocal
CurrentUserInfo currentUserInfo = new CurrentUserInfo();
// 设置 email
currentUserInfo.setEmail(email);
// 设置用户ID
currentUserInfo.setId(user.getUserId());
UserContext.set(currentUserInfo);
}catch (Exception e){
e.printStackTrace();
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 必须清理ThreadLocal
UserContext.clear();
}
}

View File

@ -0,0 +1,16 @@
package com.mingchen.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author itcast
*/
@ConfigurationProperties(prefix = "mingchen")
@Configuration
@Data
public class ApplicationProperties {
private String jwtKey;
}

View File

@ -0,0 +1,44 @@
package com.mingchen.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("categories")
public class Categories implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 类别唯一标识主键
*/
@TableId(value = "category_id", type = IdType.AUTO)
private Integer categoryId;
/**
* 类别名称财产婚姻继承
*/
private String name;
/**
* 类别简要说明
*/
private String description;
}

View File

@ -0,0 +1,28 @@
package com.mingchen.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 当前用户信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CurrentUserInfo implements Serializable {
/**
* 当前用户id
*/
private Long id;
/**
* 当前用户id
*/
private String email;
}

View File

@ -0,0 +1,65 @@
package com.mingchen.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("laws")
public class Laws implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 法律法规唯一标识主键
*/
@TableId(value = "law_id", type = IdType.AUTO)
private Integer lawId;
/**
* 关联法律类别外键引用 Categories.category_id
*/
private Integer categoryId;
/**
* 法律标题婚姻法第XX条
*/
private String title;
/**
* 法律条款摘要面向老年人简化表述
*/
private String summary;
/**
* 法律条款全文
*/
private String content;
/**
* 生效日期
*/
private LocalDate effectiveDate;
/**
* 官方参考链接用于权威性验证
*/
private String referenceUrl;
}

View File

@ -0,0 +1,59 @@
package com.mingchen.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("learning_resources")
public class LearningResources implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 学习资源唯一标识主键
*/
@TableId(value = "resource_id", type = IdType.AUTO)
private Integer resourceId;
/**
* 课程标题婚姻法解读
*/
private String title;
/**
* 资源类型可选值视频音频
*/
private String type;
/**
* 视频/音频文件链接或存储路径
*/
private String url;
/**
* 时长5分钟
*/
private String duration;
/**
* 课程内容简介
*/
private String description;
}

View File

@ -0,0 +1,78 @@
package com.mingchen.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("legalAidResources")
public class LegalAidResources implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 法律援助资源唯一标识主键
*/
@TableId(value = "aid_id", type = IdType.AUTO)
private Integer aidId;
/**
* 机构/律师名称
*/
private String name;
/**
* 资源类型可选值律师机构
*/
private String type;
/**
* 联系电话
*/
private String phoneNumber;
/**
* 邮箱可选
*/
private String email;
/**
* 邮箱可选
*/
private String avatar;
/**
* 办公地址
*/
private String address;
/**
* 官方网站链接可选
*/
private String websiteUrl;
/**
* 是否紧急求助用于一键拨打
*/
private Boolean isEmergency;
/**
* 服务范围说明
*/
private String description;
}

View File

@ -0,0 +1,115 @@
package com.mingchen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* </p>
*
* @author mingchen
* @since 2025-03-05
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user")
public class User implements Serializable , UserDetails {
private static final long serialVersionUID = 1L;
@TableId(value = "user_id", type = IdType.AUTO)
private Long userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 头像地址
*/
private String avatar;
/**
* 邮箱
*/
private String email;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 角色访问权限
*/
private String role;
/**
* IP地址
*/
private String ip;
/**
* 地址
*/
private String address;
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorityList = new ArrayList<>();
authorityList.add(new SimpleGrantedAuthority(role));
return authorityList;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,42 @@
package com.mingchen.entity;
import com.itmingchen.common.model.CurrentUserInfo;
/**
* 用户信息上下文主要存储用户id
*
* @author itcast
*/
public class UserContext {
private static final ThreadLocal<CurrentUserInfo> THREAD_LOCAL_USER = new ThreadLocal<>();
/**
* 获取当前用户id
*
* @return 用户id
*/
public static String currentUserEmail() {
return THREAD_LOCAL_USER.get().getEmail();
}
public static CurrentUserInfo currentUser() {
return THREAD_LOCAL_USER.get();
}
/**
* 设置当前用户id
*
* @param currentUserInfo 当前用户信息
*/
public static void set(CurrentUserInfo currentUserInfo) {
THREAD_LOCAL_USER.set(currentUserInfo);
}
/**
* 清理当前线程中的用户信息
*/
public static void clear(){
THREAD_LOCAL_USER.remove();
}
}

View File

@ -0,0 +1,26 @@
package com.mingchen.mapper;
import com.mingchen.entity.Categories;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mingchen.model.dto.CategoryWithLaws;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Mapper
@Repository
public interface CategoriesMapper extends BaseMapper<Categories> {
// @Select("SELECT c.category_id, c.name, c.description, l.law_id, l.category_id AS law_category_id, l.title, l.summary, l.content, l.effective_date, l.reference_url " +
// "FROM categories c LEFT JOIN laws l ON c.category_id = l.category_id ORDER BY c.category_id, l.law_id")
List<CategoryWithLaws> getAllCategoriesWithLaws();
}

View File

@ -0,0 +1,16 @@
package com.mingchen.mapper;
import com.mingchen.entity.Laws;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface LawsMapper extends BaseMapper<Laws> {
}

View File

@ -0,0 +1,16 @@
package com.mingchen.mapper;
import com.mingchen.entity.LearningResources;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface LearningResourcesMapper extends BaseMapper<LearningResources> {
}

View File

@ -0,0 +1,16 @@
package com.mingchen.mapper;
import com.mingchen.entity.LegalAidResources;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface LegalAidResourcesMapper extends BaseMapper<LegalAidResources> {
}

View File

@ -0,0 +1,16 @@
package com.mingchen.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* @Description: 登录日志持久层接口
* @Date: 2024-12-03
*/
@Mapper
@Repository
public interface LoginLogMapper {
int deleteLoginLogById(Long id);
}

View File

@ -0,0 +1,12 @@
package com.mingchen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mingchen.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface RegisterMapper extends BaseMapper<User> {
}

View File

@ -0,0 +1,18 @@
package com.mingchen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mingchen.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* @Description: 用户持久层接口
* @Date: 2024-07-19
*/
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
User findByUsername(String username);
}

View File

@ -0,0 +1,98 @@
package com.mingchen.model.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.time.LocalDate;
import java.util.List;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* <p>
* 合并的实体类包含法律类别和相关法律法规
* </p>
*
* @author
* @since 2025-04-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("categories")
public class CategoryWithLaws implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 类别唯一标识主键
*/
@TableId(value = "category_id", type = IdType.AUTO)
private Integer categoryId;
/**
* 类别名称财产婚姻继承
*/
private String name;
/**
* 类别简要说明
*/
private String description;
/**
* 关联的法律法规列表
*/
@TableField(exist = false)
private List<Law> laws;
/**
* 内部类法律法规实体
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public static class Law implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 法律法规唯一标识主键
*/
@TableId(value = "law_id", type = IdType.AUTO)
private Integer lawId;
/**
* 关联法律类别外键引用 Categories.category_id
*/
private Integer categoryId;
/**
* 法律标题婚姻法第XX条
*/
private String title;
/**
* 法律条款摘要面向老年人简化表述
*/
private String summary;
/**
* 法律条款全文
*/
private String content;
/**
* 生效日期
*/
private LocalDate effectiveDate;
/**
* 官方参考链接用于权威性验证
*/
private String referenceUrl;
}
}

View File

@ -0,0 +1,19 @@
package com.mingchen.model.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* @Description: 登录账号密码
* @Date: 2024-09-02
*/
@NoArgsConstructor
@Getter
@Setter
@ToString
public class LoginInfoDTO {
private String email;
private String password;
}

View File

@ -0,0 +1,22 @@
package com.mingchen.model.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* @ClassName RegisterReqDTO
* @Description: 注册信息
* @Author: 3177583214@qq.com
*/
@NoArgsConstructor
@Getter
@Setter
@ToString
public class RegisterReqDTO {
private String email;
private String username;
private String password;
private String verifyCode;
}

View File

@ -0,0 +1,46 @@
package com.mingchen.model.req;
import cn.hutool.core.annotation.Alias;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class AiReq {
/**
*
*/
@Alias("model")
private String model;
/**
*
*/
@Alias("messages")
private List<MessagesDTO> messages;
/**
*
*/
@Alias("stream")
private Boolean stream;
@NoArgsConstructor
@Data
public static class MessagesDTO {
/**
*
*/
@Alias("role")
private String role;
/**
*
*/
@Alias("content")
private String content;
}
}

View File

@ -0,0 +1,134 @@
package com.mingchen.model.resp;
import cn.hutool.core.annotation.Alias;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class AiResp {
/**
*
*/
@Alias("choices")
private List<ChoicesDTO> choices;
/**
*
*/
@Alias("created")
private Integer created;
/**
*
*/
@Alias("id")
private String id;
/**
*
*/
@Alias("model")
private String model;
/**
*
*/
@Alias("object")
private String object;
/**
*
*/
@Alias("usage")
private UsageDTO usage;
@NoArgsConstructor
@Data
public static class UsageDTO {
/**
*
*/
@Alias("completionTokens")
private Integer completionTokens;
/**
*
*/
@Alias("promptTokens")
private Integer promptTokens;
/**
*
*/
@Alias("totalTokens")
private Integer totalTokens;
/**
*
*/
@Alias("promptTokensDetails")
private PromptTokensDetailsDTO promptTokensDetails;
/**
*
*/
@Alias("completionTokensDetails")
private CompletionTokensDetailsDTO completionTokensDetails;
@NoArgsConstructor
@Data
public static class PromptTokensDetailsDTO {
/**
*
*/
@Alias("cachedTokens")
private Integer cachedTokens;
}
@NoArgsConstructor
@Data
public static class CompletionTokensDetailsDTO {
/**
*
*/
@Alias("reasoningTokens")
private Integer reasoningTokens;
}
}
@NoArgsConstructor
@Data
public static class ChoicesDTO {
/**
*
*/
@Alias("finishReason")
private String finishReason;
/**
*
*/
@Alias("index")
private Integer index;
/**
*
*/
@Alias("logprobs")
private Object logprobs;
/**
*
*/
@Alias("message")
private MessageDTO message;
@NoArgsConstructor
@Data
public static class MessageDTO {
/**
*
*/
@Alias("content")
private String content;
/**
*
*/
@Alias("role")
private String role;
}
}
}

View File

@ -0,0 +1,26 @@
package com.mingchen.model.vo;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
/**
* @Description: 分页结果
* @Date: 2024-08-08
*/
@NoArgsConstructor
@Getter
@Setter
@ToString
public class PageResult<T> {
private Integer totalPage;//总页数
private List<T> list;//数据
public PageResult(Integer totalPage, List<T> list) {
this.totalPage = totalPage;
this.list = list;
}
}

View File

@ -0,0 +1,57 @@
package com.mingchen.model.vo;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* @Description: 封装响应结果
* @Date: 2024-07-19
*/
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Result {
private Integer code;
private String msg;
private Object data;
private Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
this.data = null;
}
private Result(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static Result ok(String msg, Object data) {
return new Result(200, msg, data);
}
public static Result ok(String msg) {
return new Result(200, msg);
}
public static Result error(String msg) {
return new Result(500, msg);
}
public static Result error() {
return new Result(500, "异常错误");
}
public static Result create(Integer code, String msg, Object data) {
return new Result(code, msg, data);
}
public static Result create(Integer code, String msg) {
return new Result(code, msg);
}
}

View File

@ -0,0 +1,18 @@
package com.mingchen.service;
import com.mingchen.entity.Categories;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mingchen.model.vo.Result;
/**
* <p>
* 服务类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface ICategoriesService extends IService<Categories> {
Result getCategories();
}

View File

@ -0,0 +1,16 @@
package com.mingchen.service;
import com.mingchen.entity.Laws;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface ILawsService extends IService<Laws> {
}

View File

@ -0,0 +1,19 @@
package com.mingchen.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mingchen.entity.LearningResources;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mingchen.model.vo.Result;
/**
* <p>
* 服务类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface ILearningResourcesService extends IService<LearningResources> {
Result getLearningResources(int current, int size);
}

View File

@ -0,0 +1,18 @@
package com.mingchen.service;
import com.mingchen.entity.LegalAidResources;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mingchen.model.vo.Result;
/**
* <p>
* 服务类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
public interface ILegalAidResourcesService extends IService<LegalAidResources> {
Result getLegalAidResources(int current, int size);
}

View File

@ -0,0 +1,6 @@
package com.mingchen.service;
public interface LoginLogService {
void deleteLoginLogById(Long id);
}

View File

@ -0,0 +1,18 @@
package com.mingchen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mingchen.entity.User;
import com.mingchen.model.dto.RegisterReqDTO;
import com.mingchen.model.vo.Result;
/**
* @ClassName RegisterService
* @Description: TODO
* @Author: 3177583214@qq.com
*/
public interface RegisterService extends IService<User> {
void send(String userEmail);
Result register(RegisterReqDTO registerReqDTO);
}

View File

@ -0,0 +1,16 @@
package com.mingchen.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mingchen.entity.User;
import com.mingchen.model.dto.LoginInfoDTO;
import com.mingchen.model.vo.Result;
public interface UserService extends IService<User> {
// User findUserByUsernameAndPassword(String username, String password);
User findUserByEmailAndPassword(LoginInfoDTO loginInfoDTO);
Result getUserInfo();
}

View File

@ -0,0 +1,36 @@
package com.mingchen.service.impl;
import com.mingchen.entity.Categories;
import com.mingchen.mapper.CategoriesMapper;
import com.mingchen.model.dto.CategoryWithLaws;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ICategoriesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Service
public class CategoriesServiceImpl extends ServiceImpl<CategoriesMapper, Categories> implements ICategoriesService {
@Autowired
private CategoriesMapper categoriesMapper;
@Override
public Result getCategories() {
try {
// 加入缓存
List<CategoryWithLaws> allCategoriesWithLaws = categoriesMapper.getAllCategoriesWithLaws();
return Result.ok("获取成功",allCategoriesWithLaws);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
}

View File

@ -0,0 +1,20 @@
package com.mingchen.service.impl;
import com.mingchen.entity.Laws;
import com.mingchen.mapper.LawsMapper;
import com.mingchen.service.ILawsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Service
public class LawsServiceImpl extends ServiceImpl<LawsMapper, Laws> implements ILawsService {
}

View File

@ -0,0 +1,44 @@
package com.mingchen.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mingchen.entity.LearningResources;
import com.mingchen.mapper.LearningResourcesMapper;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ILearningResourcesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Service
public class LearningResourcesServiceImpl extends ServiceImpl<LearningResourcesMapper, LearningResources> implements ILearningResourcesService {
@Autowired
private LearningResourcesMapper learningResourcesMapper;
@Override
public Result getLearningResources(int current, int size) {
PageHelper.startPage(current, size);
List<LearningResources> learningResources = learningResourcesMapper.selectList(null);
PageInfo<LearningResources> pageInfo = new PageInfo<>(learningResources);
return Result.ok("",pageInfo);
}
}

View File

@ -0,0 +1,38 @@
package com.mingchen.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mingchen.entity.LearningResources;
import com.mingchen.entity.LegalAidResources;
import com.mingchen.mapper.LegalAidResourcesMapper;
import com.mingchen.model.vo.Result;
import com.mingchen.service.ILegalAidResourcesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author mingchen
* @since 2025-04-17
*/
@Service
public class LegalAidResourcesServiceImpl extends ServiceImpl<LegalAidResourcesMapper, LegalAidResources> implements ILegalAidResourcesService {
@Autowired
private LegalAidResourcesMapper legalAidResourcesMapper;
@Override
public Result getLegalAidResources(int current, int size) {
PageHelper.startPage(current, size);
List<LegalAidResources> legalAidResources = legalAidResourcesMapper.selectList(null);
PageInfo<LegalAidResources> pageInfo = new PageInfo<>(legalAidResources);
return Result.ok("",pageInfo);
}
}

View File

@ -0,0 +1,36 @@
package com.mingchen.service.impl;
import com.itmingchen.common.expcetions.PersistenceException;
import com.itmingchen.common.utils.DateUtils;
import com.itmingchen.common.utils.UserAgentUtils;
import com.mingchen.mapper.LoginLogMapper;
import com.mingchen.service.LoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* @Description: 登录日志业务层实现
* @Date: 2024-12-03
*/
@Service
public class LoginLogServiceImpl implements LoginLogService {
@Autowired
LoginLogMapper loginLogMapper;
@Autowired
UserAgentUtils userAgentUtils;
@Transactional
@Override
public void deleteLoginLogById(Long id) {
Date date = DateUtils.addDays(1);
if (loginLogMapper.deleteLoginLogById(id) != 1) {
throw new PersistenceException("删除日志失败");
}
}
}

View File

@ -0,0 +1,30 @@
package com.mingchen.service.impl;
import com.mingchen.api.sdk.AiService;
import com.mingchen.model.req.AiReq;
import com.mingchen.model.resp.AiResp;
import com.mingchen.model.vo.Result;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@Service
public class PlatformServiceImpl {
public Result getChat(String content) {
try {
AiService aiService = new AiService();
AiReq.MessagesDTO messagesDTO = new AiReq.MessagesDTO();
messagesDTO.setContent(content).setRole("user");
ArrayList<AiReq.MessagesDTO> messagesDTOS = new ArrayList<>();
messagesDTOS.add(messagesDTO);
AiReq aiReq = new AiReq();
aiReq.setModel("gpt-3.5-turbo")
.setMessages(messagesDTOS)
.setStream(false);
AiResp chatApi = aiService.getChatApi("https://chatapi.littlewheat.com/v1/chat/completions", "sk-jSkqRJhJFIqZ3gcdjyqyAbpN8YfnAvYiVxbpzydqPckJSaG2", aiReq);
String resContent = chatApi.getChoices().get(0).getMessage().getContent();
return Result.ok("获取成功",resContent);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
}

View File

@ -0,0 +1,136 @@
package com.mingchen.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmingchen.common.expcetions.ErrorRequestException;
import com.itmingchen.common.utils.MailUtils;
import com.itmingchen.common.utils.RedisUtil;
import com.itmingchen.common.utils.StringUtils;
import com.mingchen.common.constant.RedisConstants;
import com.mingchen.entity.User;
import com.mingchen.mapper.RegisterMapper;
import com.mingchen.model.dto.RegisterReqDTO;
import com.mingchen.model.vo.Result;
import com.mingchen.service.RegisterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.regex.Pattern;
/**
* @ClassName RegisterServiceImpl
* @Description: TODO
* @Author: 3177583214@qq.com
*/
@Service
@Slf4j
public class RegisterServiceImpl extends ServiceImpl<RegisterMapper, User> implements RegisterService {
@Autowired
private MailUtils mailUtils;
@Autowired
private RedisUtil redisUtil;
@Autowired
private RegisterMapper registerMapper;
@Autowired
private PasswordEncoder passwordEncoder;
// RFC 5322 标准正则支持 99% 合法邮箱
private static final String EMAIL_REGEX =
"^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
private static final Pattern PATTERN = Pattern.compile(EMAIL_REGEX);
@Override
public void send(String userEmail) {
// 校验 email 是否存在
if(StringUtils.isEmpty(userEmail)){
log.debug("不能发送验证码,邮箱不存在");
throw new ErrorRequestException("不能发送验证码,邮箱不存在");
}
// 校验 email 是否符合格式
if (!isValid(userEmail)) {
log.debug("邮箱格式无效");
throw new ErrorRequestException("邮箱格式无效");
}
// 向对方邮箱发送验证码
SecureRandom secureRandom = new SecureRandom();
// 生成加密安全的随机数适用于验证码
String emailCode = String.format("%04d", secureRandom.nextInt(10000));
try {
// 发送验证码
mailUtils.sendSimpleMail(userEmail,"[Navigation] Please verify your device",emailCode);
log.info("验证码为:"+emailCode);
log.info("验证码已发送至邮箱");
} catch (Exception e) {
log.error("进入 catch 块", e);
throw new ErrorRequestException("发送验证码失败");
}
// 将验证码信息存入redis
HashMap<String, Object> map = new HashMap<>();
map.put(userEmail,emailCode);
// 若有相同的可以每一次都会进行更新新值所以这里不需要将相应的key删除
redisUtil.setHashPro(RedisConstants.EMAIL_CODE_MAP,map,1000);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Result register(RegisterReqDTO registerReqDTO) {
// 验证 验证码 是否存在 以及验证验证码和redis保存的值是否相同
String verifyCode = null;
try {
verifyCode = redisUtil.getHashItem(RedisConstants.EMAIL_CODE_MAP, registerReqDTO.getEmail()).toString();
} catch (Exception e) {
throw new ErrorRequestException("请获取验证码后登录");
}
if(StringUtils.isEmpty(registerReqDTO.getVerifyCode()) || !StringUtils.equals(registerReqDTO.getVerifyCode(), verifyCode)){
throw new ErrorRequestException("短信验证码失效,请重新获取验证码");
}
// 查询数据是否有相同的邮箱
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery().eq(User::getEmail, registerReqDTO.getEmail());
User userInfo = registerMapper.selectOne(userLambdaQueryWrapper);
if(userInfo!=null){
throw new ErrorRequestException("邮箱已被注册");
}
// 若没有可允许注册成功
User user = new User();
user.setEmail(registerReqDTO.getEmail())
.setUsername(registerReqDTO.getUsername())
.setPassword(passwordEncoder.encode(registerReqDTO.getPassword()))
.setCreateTime(LocalDateTime.now())
.setUpdateTime(LocalDateTime.now())
.setRole("ROLE_admin");
int insert = registerMapper.insert(user);
if(insert != 1){
throw new ErrorRequestException("注册失败,请重新尝试");
}
HashMap<String, Object> map = new HashMap<>();
registerReqDTO.setPassword(null);
map.put("register",registerReqDTO);
return Result.ok("注册成功",map);
}
public static boolean isValid(String email) {
if (email == null || email.isEmpty()) return false;
return PATTERN.matcher(email).matches();
}
}

View File

@ -0,0 +1,91 @@
package com.mingchen.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmingchen.common.expcetions.ErrorRequestException;
import com.itmingchen.common.utils.BeanUtils;
import com.itmingchen.common.utils.StringUtils;
import com.mingchen.common.constant.RedisConstants;
import com.mingchen.entity.User;
import com.mingchen.entity.UserContext;
import com.mingchen.mapper.UserMapper;
import com.mingchen.model.dto.LoginInfoDTO;
import com.mingchen.model.vo.Result;
import com.mingchen.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
/**
* @Description: 用户业务层接口实现类
* @Date: 2024-07-19
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery().eq(User::getEmail, email);
User user = userMapper.selectOne(userLambdaQueryWrapper);
user.setUsername(user.getEmail());
if (ObjectUtil.isNull(user)) {
throw new UsernameNotFoundException("邮箱未注册,请注册");
}
return user;
}
@Override
public User findUserByEmailAndPassword(LoginInfoDTO loginInfoDTO) {
// 校验邮箱是否存在
if(StringUtils.isEmpty(loginInfoDTO.getEmail())) throw new ErrorRequestException("请输入邮箱");
// 校验密码是否存在
if(StringUtils.isEmpty(loginInfoDTO.getPassword())) throw new ErrorRequestException("请输入密码");
// 通过唯一约束 email 获取 User 信息
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery().eq(User::getEmail, loginInfoDTO.getEmail());
User user = userMapper.selectOne(userLambdaQueryWrapper);
// 校验用户信息是否获取成功
if(ObjectUtil.isNull(user)) throw new ErrorRequestException("邮箱未注册,请注册");
// 比对验证码是否相同
if(!passwordEncoder.matches(loginInfoDTO.getPassword(), user.getPassword())) throw new ErrorRequestException("密码错误,请重新输入");
return user;
}
@Override
public Result getUserInfo() {
try {
Long id = UserContext.currentUser().getId();
String email = UserContext.currentUserEmail();
if(ObjectUtil.isNull(id)){
throw new ErrorRequestException("ID 为空 ,请重新请求");
}
User user = userMapper.selectById(id);
if(!StringUtils.equalsAnyIgnoreCase(user.getEmail(),email)){
throw new ErrorRequestException("用户数据存在错误,不能进行返回");
}
user.setPassword(null);
return Result.ok("用户信息成功返回",user);
} catch (Exception e) {
log.error(e.getMessage());
return Result.error(e.getMessage());
}
}
}

View File

@ -0,0 +1,56 @@
server.port=9092
#server.ssl.key-store = classpath:itmingchen.cn.jks
#server.ssl.key-store-password = 302iycod
#server.ssl.keyStoreType = JKS
# ????????????ip?????????? https://xxx.xxx
# ??cms
#custom.url.cms=https://121.40.211.28:8080
# ????
#custom.url.website=http://itmingchen.cn
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://113.44.0.193:3306/legal?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=317758lph
spring.redis.host=8.155.44.59
spring.redis.password=317758lph
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=10000ms
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-aliases-package=com.mingchen.mapper
logging.level.root=info
logging.level.com.mingchen=debug
logging.file.path=log/blog-dev
logging.level.com.baomidou.mybatisplus.core.override: TRACE
# 1000 * 60 * 60 * 24 * 3 = 3?
token.expireTime=259200000
# ??????????????????token???
token.secretKey=
mingchen.jwtKey=migngchensafeqwertyuiopasdfghjklzxcvbnm
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.username=3177583214@qq.com
spring.mail.password=iuozpvjwauuuddag
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.ssl.protocols=TLSv1.2
#spring.mail.properties.mail.debug=true

View File

@ -0,0 +1 @@
spring.profiles.active=dev

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--包含Spring boot对logback日志的默认配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
<appender name="TIME_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<!--<file>${LOG_FILE}.log</file>-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<maxHistory>365</maxHistory>
<!--Spring Boot默认情况下日志文件10M时会切分日志文件-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="TIME_FILE"/>
</root>
</configuration>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mingchen.mapper.CategoriesMapper">
<resultMap id="CategoryWithLawsResultMap" type="com.mingchen.model.dto.CategoryWithLaws">
<id property="categoryId" column="category_id"/>
<result property="name" column="name"/>
<result property="description" column="description"/>
<collection property="laws" ofType="com.mingchen.model.dto.CategoryWithLaws$Law">
<id property="lawId" column="law_id"/>
<result property="categoryId" column="law_category_id"/>
<result property="title" column="title"/>
<result property="summary" column="summary"/>
<result property="content" column="content"/>
<result property="effectiveDate" column="effective_date"/>
<result property="referenceUrl" column="reference_url"/>
</collection>
</resultMap>
<!-- 查询所有类别及其关联的法律法规 -->
<select id="getAllCategoriesWithLaws" resultMap="CategoryWithLawsResultMap">
SELECT
c.category_id,
c.name,
c.description,
l.law_id,
l.category_id AS law_category_id,
l.title,
l.summary,
l.content,
l.effective_date,
l.reference_url
FROM
categories c
LEFT JOIN
laws l ON c.category_id = l.category_id
ORDER BY
c.category_id, l.law_id
</select>
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mingchen.mapper.LawsMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mingchen.mapper.LearningResourcesMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mingchen.mapper.LegalAidResourcesMapper">
</mapper>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.mingchen.mapper.LoginLogMapper">
<!--删除日志-->
<delete id="deleteLoginLogById">
delete
from login_log
where id = #{id}
</delete>
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mingchen.mapper.UserMapper">
<!--按用户名查询User-->
<select id="findByUsername" resultType="com.mingchen.entity.User">
select *
from user
where username = #{username}
limit 1
</select>
</mapper>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="zh_CN" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="width: 550px;height: auto;border-radius: 5px;margin: 0 auto;border: 1px solid #ffb0b0;box-shadow: 0px 0px 20px #888888;position: relative;padding-bottom: 5px">
<div style="background-image: url(https://cdn.jsdelivr.net/gh/mingchen/JsDelivr/static/blog/mail.jpg);width: 550px;height: 300px;background-size: cover;background-repeat: no-repeat;border-radius: 5px 5px 0px 0px"></div>
<div style="width: 200px;height: 40px;background-color: rgb(255, 114, 114);margin-top: -20px;margin-left: 20px;box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.3);color: rgb(255, 255, 255);text-align: center;line-height: 40px"
th:text="|Dear: ${parentNickname}|"></div>
<div style="background-color: white;line-height: 180%;padding: 0 15px 12px;width: 520px;margin: 30px auto;color: #555555;font-family: 'Century Gothic', 'Trebuchet MS', 'Hiragino Sans GB', 微软雅黑, 'Microsoft Yahei', Tahoma, Helvetica, Arial, 'SimSun', sans-serif;font-size: 12px;margin-bottom: 0px">
<h2 style="border-bottom: 1px solid #ddd;font-size: 14px;font-weight: normal;padding: 13px 0 10px 8px">
您在<a style="text-decoration: none;color: #12addb" th:href="@{${url}}" target="_blank"
th:text="|《${title}》|"></a>的评论有了新的回复~
</h2>
<div style="padding: 0 12px 0 12px;margin-top: 18px">
<p>时间:<span style="border-bottom: 1px dashed #ccc"
th:text="${#dates.format(time,'yyyy-MM-dd HH:mm')}"></span></p>
<p>您的评论:</p>
<p style="background-color: #f5f5f5;border: 0px solid #ddd;padding: 10px 15px;margin: 18px 0"
th:text="${parentContent}"></p>
<p><strong th:text="${nickname}"></strong>&nbsp;给您的回复:</p>
<p style="background-color: #f5f5f5;border: 0px solid #ddd;padding: 10px 15px;margin: 18px 0"
th:text="${content}"></p>
</div>
</div>
<div style="color: #8c8c8c;font-family: 'Century Gothic', 'Trebuchet MS', 'Hiragino Sans GB', 微软雅黑, 'Microsoft Yahei', Tahoma, Helvetica, Arial, 'SimSun', sans-serif;font-size: 10px;width: 100%;text-align: center;word-wrap: break-word;margin-top: -30px">
<p style="padding: 20px">萤火虫消失之后,那光的轨迹仍久久地印在我的脑际。那微弱浅淡的光点,仿佛迷失方向的魂灵,在漆黑厚重的夜幕中彷徨。——《挪威的森林》村上春树</p>
</div>
<a style="text-decoration: none;color: #fff;width: 40%;text-align: center;background-color: #ff7272;height: 40px;line-height: 35px;box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.3);margin: -10px auto;display: block"
th:href="@{${url}}" target="_blank">查看回复的完整內容</a>
<div style="color: #8c8c8c;font-family: 'Century Gothic', 'Trebuchet MS', 'Hiragino Sans GB', 微软雅黑, 'Microsoft Yahei', Tahoma, Helvetica, Arial, 'SimSun', sans-serif;font-size: 10px;width: 100%;text-align: center;margin-top: 30px">
<p>本邮件为系统自动发送回复TD退订~</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="zh_CN" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="width: 550px;height: auto;border-radius: 5px;margin: 0 auto;border: 1px solid #ffb0b0;box-shadow: 0px 0px 20px #888888;position: relative;">
<div style="background-image: url(https://cdn.jsdelivr.net/gh/mingchen/JsDelivr/static/blog/mail.jpg);width: 550px;height: 250px;background-size: cover;background-repeat: no-repeat;border-radius: 5px 5px 0px 0px"></div>
<div style="background-color: white;line-height: 180%;padding: 0 15px 12px;width: 520px;color: #555555;font-family: 'Century Gothic', 'Trebuchet MS', 'Hiragino Sans GB', 微软雅黑, 'Microsoft Yahei', Tahoma, Helvetica, Arial, 'SimSun', sans-serif;font-size: 12px;margin: 10px auto 0px">
<h2 style="border-bottom: 1px solid #DDD;font-size: 14px;font-weight: normal;padding: 13px 0 10px 8px">
您的文章<a style="text-decoration: none;color: #12ADDB" th:href="@{${url}}" target="_blank"
th:text="|《${title}》|"></a>有了新的评论~</h2>
<div style="padding: 0 12px 0 12px;margin-top: 18px">
<p>时间:<span style="border-bottom: 1px dashed #ccc"
th:text="${#dates.format(time,'yyyy-MM-dd HH:mm')}"></span></p>
<p><strong th:text="${nickname}"></strong>&nbsp;给您的评论:</p>
<p style="background-color: #f5f5f5;border: 0px solid #DDD;padding: 10px 15px;margin: 18px 0"
th:text="${content}"></p>
<p>其他信息:</p>
<p style="background-color: #f5f5f5;border: 0px solid #DDD;padding: 10px 15px;margin: 18px 0"><span
th:text="|IP${ip}|"></span><br/><span th:text="|邮箱:${email}|"></span><br/><span
th:text="|状态:${status}|"></span> [<a th:href="@{${manageUrl}}" target="_blank">管理评论</a>]</p>
</div>
</div>
<a style="text-decoration: none;color: rgb(255, 255, 255);width: 40%;text-align: center;background-color: rgb(255, 114, 114);height: 40px;line-height: 40px;box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.3);display: block;margin: 0 auto 28px auto"
th:href="@{${url}}" target="_blank">查看回复的完整內容</a>
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
package com.mingchen;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.mingchen.entity.User;
import com.mingchen.mapper.CategoriesMapper;
import com.mingchen.mapper.UserMapper;
import com.mingchen.model.dto.CategoryWithLaws;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class BlogApiApplicationTests {
@Autowired
private UserMapper userMapper;
@Autowired
private CategoriesMapper categoriesMapper;
@Test
void test01() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery().eq(User::getEmail, "admin@gmail.com");
User user = userMapper.selectOne(userLambdaQueryWrapper);
System.out.println(user);
}
@Test
void test02() {
List<CategoryWithLaws> allCategoriesWithLaws = categoriesMapper.getAllCategoriesWithLaws();
System.out.println(allCategoriesWithLaws);
}
}

View File

@ -0,0 +1,56 @@
server.port=9092
#server.ssl.key-store = classpath:itmingchen.cn.jks
#server.ssl.key-store-password = 302iycod
#server.ssl.keyStoreType = JKS
# ????????????ip?????????? https://xxx.xxx
# ??cms
#custom.url.cms=https://121.40.211.28:8080
# ????
#custom.url.website=http://itmingchen.cn
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://113.44.0.193:3306/legal?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=317758lph
spring.redis.host=8.155.44.59
spring.redis.password=317758lph
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=10000ms
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-aliases-package=com.mingchen.mapper
logging.level.root=info
logging.level.com.mingchen=debug
logging.file.path=log/blog-dev
logging.level.com.baomidou.mybatisplus.core.override: TRACE
# 1000 * 60 * 60 * 24 * 3 = 3?
token.expireTime=259200000
# ??????????????????token???
token.secretKey=
mingchen.jwtKey=migngchensafeqwertyuiopasdfghjklzxcvbnm
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.username=3177583214@qq.com
spring.mail.password=iuozpvjwauuuddag
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.ssl.protocols=TLSv1.2
#spring.mail.properties.mail.debug=true

View File

@ -0,0 +1 @@
spring.profiles.active=dev

Some files were not shown because too many files have changed in this diff Show More