fix: 修复后端兼容性和前后端配置问题

后端修复:
- Spring Boot 降级到 3.1.12 以兼容 MyBatis-Plus 3.5.6
- 添加 RedisConfig 配置 RedisTemplate Bean
- 修复数据库连接字符编码 characterEncoding=UTF-8
- 添加健康检查接口 /api/v1/health 到认证白名单
- 实体字段同步数据库: WorkLog 添加 recordTime, LogTemplate 添加 templateContent/instruction
- 修复 logback 滚动策略配置
- 密码验证临时改为明文比对(测试用)

前端修复:
- API baseURL 统一修正为 /wlog/api/v1
- Vite 配置添加 base 路径 (/wladmin/, /wlmobile/)

脚本修复:
- stop.sh/status.sh 使用动态 APP_HOME 获取路径
This commit is contained in:
zhangjf 2026-02-24 22:47:14 +08:00
parent ba9e2c12c8
commit e36ac36af5
26 changed files with 105 additions and 59 deletions

View File

@ -3,9 +3,12 @@
# 工作日志服务平台 - 应用状态查看脚本
# ====================================================
# 获取脚本所在目录的上级目录作为应用根目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_HOME="$(cd "${SCRIPT_DIR}/.." && pwd)"
# 应用配置
APP_NAME="worklog-api"
APP_HOME="/opt/worklog/${APP_NAME}"
PID_FILE="${APP_HOME}/${APP_NAME}.pid"
# 颜色输出

View File

@ -3,9 +3,12 @@
# 工作日志服务平台 - 应用停止脚本
# ====================================================
# 获取脚本所在目录的上级目录作为应用根目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_HOME="$(cd "${SCRIPT_DIR}/.." && pwd)"
# 应用配置
APP_NAME="worklog-api"
APP_HOME="/opt/worklog/${APP_NAME}"
PID_FILE="${APP_HOME}/${APP_NAME}.pid"
# 检查 PID 文件是否存在

View File

@ -8,7 +8,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<version>3.1.12</version>
<relativePath/>
</parent>
@ -22,7 +22,7 @@
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<hutool.version>5.8.25</hutool.version>
<springdoc.version>2.3.0</springdoc.version>
<mysql.version>8.0.33</mysql.version>

View File

@ -21,13 +21,14 @@ public class PasswordUtil {
}
/**
* 校验密码
* 校验密码临时明文比对
*
* @param rawPassword 明文密码
* @param encodedPassword 加密后的密码
* @return 是否匹配
*/
public static boolean matches(String rawPassword, String encodedPassword) {
return ENCODER.matches(rawPassword, encodedPassword);
// TODO: 临时使用明文比对生产环境需恢复 BCrypt 验证
return rawPassword.equals(encodedPassword);
}
}

View File

@ -0,0 +1,33 @@
package com.wjbl.worklog.config;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis 配置类
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// Key 使用 String 序列化
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// Value 使用 JSON 序列化
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}

View File

@ -24,7 +24,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
private static final String[] WHITE_LIST = {
"/api/v1/auth/login",
"/api/v1/auth/logout",
"/api/v1/auth/health",
"/api/v1/health",
"/swagger-ui.html",
"/swagger-ui/**",
"/v3/api-docs/**",

View File

@ -30,7 +30,12 @@ public class LogTemplate implements Serializable {
/**
* 模板内容Markdown格式
*/
private String content;
private String templateContent;
/**
* 使用说明
*/
private String instruction;
/**
* 状态0-禁用1-启用

View File

@ -34,9 +34,9 @@ public class WorkLog implements Serializable {
private LocalDate logDate;
/**
* 日志标题
* 记录时间
*/
private String title;
private LocalDateTime recordTime;
/**
* 日志内容Markdown格式

View File

@ -22,13 +22,6 @@ public class LogCreateDTO implements Serializable {
@Schema(description = "日志日期,默认当天")
private LocalDate logDate;
/**
* 日志标题
*/
@NotBlank(message = "标题不能为空")
@Schema(description = "日志标题", requiredMode = Schema.RequiredMode.REQUIRED)
private String title;
/**
* 日志内容Markdown格式
*/

View File

@ -14,12 +14,6 @@ public class LogUpdateDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 日志标题
*/
@Schema(description = "日志标题")
private String title;
/**
* 日志内容Markdown格式
*/

View File

@ -27,4 +27,10 @@ public class TemplateCreateDTO implements Serializable {
*/
@Schema(description = "模板内容Markdown格式")
private String content;
/**
* 使用说明
*/
@Schema(description = "使用说明")
private String instruction;
}

View File

@ -25,4 +25,10 @@ public class TemplateUpdateDTO implements Serializable {
*/
@Schema(description = "模板内容Markdown格式")
private String content;
/**
* 使用说明
*/
@Schema(description = "使用说明")
private String instruction;
}

View File

@ -42,7 +42,7 @@ public class LogServiceImpl implements LogService {
WorkLog workLog = new WorkLog();
workLog.setUserId(currentUserId);
workLog.setLogDate(logDate);
workLog.setTitle(dto.getTitle());
workLog.setRecordTime(LocalDateTime.now());
workLog.setContent(dto.getContent());
workLog.setTemplateId(dto.getTemplateId());
workLog.setDeleted(0);
@ -77,9 +77,6 @@ public class LogServiceImpl implements LogService {
}
// 更新日志
if (dto.getTitle() != null) {
workLog.setTitle(dto.getTitle());
}
if (dto.getContent() != null) {
workLog.setContent(dto.getContent());
}

View File

@ -37,7 +37,8 @@ public class TemplateServiceImpl implements TemplateService {
// 创建模板
LogTemplate template = new LogTemplate();
template.setTemplateName(dto.getTemplateName());
template.setContent(dto.getContent());
template.setTemplateContent(dto.getContent());
template.setInstruction(dto.getInstruction());
template.setStatus(1); // 默认启用
template.setDeleted(0);
@ -74,7 +75,10 @@ public class TemplateServiceImpl implements TemplateService {
// 更新模板内容
if (dto.getContent() != null) {
template.setContent(dto.getContent());
template.setTemplateContent(dto.getContent());
}
if (dto.getInstruction() != null) {
template.setInstruction(dto.getInstruction());
}
// 设置审计字段
@ -142,7 +146,13 @@ public class TemplateServiceImpl implements TemplateService {
*/
private TemplateVO convertToVO(LogTemplate template) {
TemplateVO vo = new TemplateVO();
BeanUtils.copyProperties(template, vo);
vo.setId(template.getId());
vo.setTemplateName(template.getTemplateName());
vo.setContent(template.getTemplateContent());
vo.setInstruction(template.getInstruction());
vo.setStatus(template.getStatus());
vo.setCreatedTime(template.getCreatedTime());
vo.setUpdatedTime(template.getUpdatedTime());
return vo;
}
}

View File

@ -34,12 +34,6 @@ public class LogVO implements Serializable {
@Schema(description = "日志日期")
private LocalDate logDate;
/**
* 日志标题
*/
@Schema(description = "日志标题")
private String title;
/**
* 日志内容Markdown格式
*/

View File

@ -33,6 +33,12 @@ public class TemplateVO implements Serializable {
@Schema(description = "模板内容Markdown格式")
private String content;
/**
* 使用说明
*/
@Schema(description = "使用说明")
private String instruction;
/**
* 状态0-禁用1-启用
*/

View File

@ -22,7 +22,7 @@ spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://localhost:3306/worklog?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: worklog
password: Wlog@123
type: com.zaxxer.hikari.HikariDataSource

View File

@ -24,11 +24,9 @@
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/app-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
@ -40,11 +38,9 @@
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/sql-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>

View File

@ -43,7 +43,6 @@ class LogServiceTest {
testLog.setId("log-id-123");
testLog.setUserId("user-id-123");
testLog.setLogDate(LocalDate.now());
testLog.setTitle("今日工作日志");
testLog.setContent("今天完成了xxx任务");
testLog.setDeleted(0);
@ -57,7 +56,6 @@ class LogServiceTest {
void createLog_success() {
// Given
LogCreateDTO dto = new LogCreateDTO();
dto.setTitle("新日志");
dto.setContent("日志内容");
dto.setLogDate(LocalDate.now());
@ -69,7 +67,7 @@ class LogServiceTest {
// Then
assertNotNull(result);
assertEquals("日志", result.getTitle());
assertEquals("日志内容", result.getContent());
verify(workLogDataService).save(any(WorkLog.class));
}
@ -78,7 +76,6 @@ class LogServiceTest {
void createLog_alreadyExists() {
// Given
LogCreateDTO dto = new LogCreateDTO();
dto.setTitle("新日志");
dto.setContent("日志内容");
dto.setLogDate(LocalDate.now());
@ -94,7 +91,6 @@ class LogServiceTest {
void updateLog_success_ownLog() {
// Given
LogUpdateDTO dto = new LogUpdateDTO();
dto.setTitle("更新后的标题");
dto.setContent("更新后的内容");
when(workLogDataService.getById("log-id-123")).thenReturn(testLog);
@ -105,7 +101,7 @@ class LogServiceTest {
// Then
assertNotNull(result);
assertEquals("更新后的标题", result.getTitle());
assertEquals("更新后的内容", result.getContent());
}
@Test
@ -118,7 +114,7 @@ class LogServiceTest {
otherUserLog.setLogDate(LocalDate.now());
LogUpdateDTO dto = new LogUpdateDTO();
dto.setTitle("更新后的标题");
dto.setContent("更新后的内容");
when(workLogDataService.getById("other-log-id")).thenReturn(otherUserLog);
@ -140,7 +136,7 @@ class LogServiceTest {
otherUserLog.setLogDate(LocalDate.now());
LogUpdateDTO dto = new LogUpdateDTO();
dto.setTitle("管理员更新");
dto.setContent("管理员更新内容");
when(workLogDataService.getById("other-log-id")).thenReturn(otherUserLog);
when(workLogDataService.updateById(any(WorkLog.class))).thenReturn(true);
@ -150,7 +146,7 @@ class LogServiceTest {
// Then
assertNotNull(result);
assertEquals("管理员更新", result.getTitle());
assertEquals("管理员更新内容", result.getContent());
}
@Test
@ -193,7 +189,7 @@ class LogServiceTest {
// Then
assertNotNull(result);
assertEquals("日工作日志", result.getTitle());
assertEquals("天完成了xxx任务", result.getContent());
}
@Test

View File

@ -42,7 +42,7 @@ class TemplateServiceTest {
testTemplate = new LogTemplate();
testTemplate.setId("template-id-123");
testTemplate.setTemplateName("日报模板");
testTemplate.setContent("# 今日工作\n\n## 完成事项\n\n## 明日计划");
testTemplate.setTemplateContent("# 今日工作\n\n## 完成事项\n\n## 明日计划");
testTemplate.setStatus(1);
testTemplate.setDeleted(0);

View File

@ -5,7 +5,7 @@ import { TOKEN_KEY } from '@/utils/constants'
// 创建 axios 实例
const service: AxiosInstance = axios.create({
baseURL: '/wlmobile/api/v1',
baseURL: '/wlog/api/v1',
timeout: 15000
})

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';
// https://vitejs.dev/config/
export default defineConfig({
base: '/wlmobile/',
plugins: [vue()],
resolve: {
alias: {

View File

@ -4,6 +4,7 @@ import { fileURLToPath, URL } from 'node:url'
// https://vitejs.dev/config/
export default defineConfig({
base: '/wlmobile/',
plugins: [vue()],
resolve: {
alias: {

View File

@ -5,7 +5,7 @@ import { TOKEN_KEY } from '@/utils/constants'
// 创建 axios 实例
const service: AxiosInstance = axios.create({
baseURL: '/wladmin/api/v1',
baseURL: '/wlog/api/v1',
timeout: 15000
})

View File

@ -4,6 +4,7 @@ import path from 'path'
// https://vite.dev/config/
export default defineConfig({
base: '/wladmin/',
plugins: [vue()],
resolve: {
alias: {