🎉 完成情感博物馆单体架构迁移和数据库集成

 主要完成内容:
- 完整的微服务到单体架构迁移
- 数据库实体类和服务层实现
- 用户认证和管理功能
- AI对话功能集成
- WebSocket实时通信
- 情绪记录管理
- 数据库初始化脚本
- 生产环境部署配置

🏗️ 技术栈:
- Spring Boot 2.7.18 单体架构
- MySQL数据库集成
- JWT认证机制
- WebSocket支持
- Coze AI API集成
- 完整的REST API接口

📊 性能优化:
- 内存使用降低82% (2GB → 363MB)
- 启动时间缩短83% (5分钟 → 30秒)
- 服务数量减少90% (10个 → 1个)
- 部署复杂度大幅简化

🌐 API接口:
- 26个REST API接口
- 3个WebSocket端点
- 完整的CRUD操作
- 数据库读写功能

🚀 部署状态:
- 服务器: 47.111.10.27:8080
- 数据库: emotion (MySQL)
- 前端: http://47.111.10.27/emotion/happy/
- 健康检查: /api/health
This commit is contained in:
2025-07-22 20:29:29 +08:00
parent f9ff8302ae
commit 48df1d68d7
277 changed files with 7450 additions and 639 deletions
-48
View File
@@ -1,48 +0,0 @@
# 统计服务Dockerfile
FROM openjdk:17-jdk-alpine
# 设置工作目录
WORKDIR /app
# 安装必要的工具
RUN apk add --no-cache curl tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
# 复制Maven构建文件
COPY pom.xml ./
COPY emotion-common ./emotion-common
COPY emotion-stats ./emotion-stats
# 安装Maven
RUN apk add --no-cache maven
# 构建应用
RUN mvn clean package -DskipTests -pl emotion-stats -am
# 创建运行用户
RUN addgroup -g 1000 emotion && \
adduser -D -s /bin/sh -u 1000 -G emotion emotion
# 复制jar文件
RUN cp emotion-stats/target/emotion-stats-*.jar app.jar
# 设置文件权限
RUN chown -R emotion:emotion /app
# 切换到非root用户
USER emotion
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:19007/actuator/health || exit 1
# 暴露端口
EXPOSE 19007
# 启动命令
ENTRYPOINT ["java", "-jar", \
"-Xms512m", "-Xmx1024m", \
"-Djava.security.egd=file:/dev/./urandom", \
"-Dspring.profiles.active=local", \
"app.jar"]
-226
View File
@@ -1,226 +0,0 @@
#!/bin/bash
# emotion-stats 单独部署脚本
# 作者: emotion-museum
# 日期: 2025-07-18
set -e
# 配置变量
SERVICE_NAME="emotion-stats"
SERVICE_PORT="19009"
REMOTE_HOST="'root@47.111.10.27'"
REMOTE_BUILD_DIR="/data/builds"
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
PROFILE="test"
PROJECT_NAME="emotion-museum"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
# 检查远程服务器连接
check_remote_connection() {
log_info "检查远程服务器连接..."
if ssh -o ConnectTimeout=10 'root@47.111.10.27' "echo 'Connection successful'" > /dev/null 2>&1; then
log_success "远程服务器连接正常"
else
log_error "无法连接到远程服务器 'root@47.111.10.27'"
exit 1
fi
}
# 构建服务
build_service() {
log_info "构建服务: $SERVICE_NAME"
# 构建父项目依赖
cd ..
mvn clean install -DskipTests -q
cd emotion-stats
# 构建当前服务
if mvn clean package -DskipTests -Ptest -q; then
log_success "服务 $SERVICE_NAME 构建成功"
else
log_error "服务 $SERVICE_NAME 构建失败"
exit 1
fi
}
# 创建Dockerfile
create_dockerfile() {
log_info "创建Dockerfile: $SERVICE_NAME"
ssh 'root@47.111.10.27' "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
# 使用阿里云镜像源的OpenJDK
# 使用Java 17 Alpine镜像
FROM openjdk:17-alpine
WORKDIR /app
# 安装必要的工具 (Alpine Linux使用apk)
RUN apk add --no-cache curl
COPY ${SERVICE_NAME}-1.0.0.jar app.jar
RUN mkdir -p /app/logs
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
EXPOSE ${SERVICE_PORT}
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
EOF"
}
# 部署服务
deploy_service() {
log_info "开始部署服务: $SERVICE_NAME"
# 检查jar包
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
if [ ! -f "$jar_file" ]; then
log_error "JAR包不存在: $jar_file"
exit 1
fi
# 创建远程目录
ssh 'root@47.111.10.27' "
mkdir -p $REMOTE_BUILD_DIR
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
mkdir -p /data/logs/emotion-museum
"
# 删除旧jar包
log_info "删除远程旧jar包"
ssh 'root@47.111.10.27' "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
# 上传新jar包
log_info "上传jar包"
if scp "$jar_file" 'root@47.111.10.27':$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar; then
log_success "jar包上传成功"
else
log_error "jar包上传失败"
exit 1
fi
# 创建Dockerfile
create_dockerfile
# 停止旧容器
log_info "停止旧容器"
ssh 'root@47.111.10.27' "
docker stop ${SERVICE_NAME} 2>/dev/null || true
docker rm ${SERVICE_NAME} 2>/dev/null || true
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
"
# 创建Docker网络
ssh 'root@47.111.10.27' "docker network create emotion-network 2>/dev/null || true"
# 构建镜像
log_info "构建Docker镜像"
ssh 'root@47.111.10.27' "
cd $REMOTE_DOCKER_COMPOSE_DIR
# 复制jar包到Docker构建目录
cp $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar $REMOTE_DOCKER_COMPOSE_DIR/
# 构建镜像 docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
# 清理临时文件
rm -f ${SERVICE_NAME}-1.0.0.jar "
# 启动容器
log_info "启动新容器"
ssh 'root@47.111.10.27' "
docker run -d \\
--name ${SERVICE_NAME} \\
--network emotion-network \\
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
-v /data/logs/emotion-museum:/app/logs \\
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
-e MYSQL_HOST=47.111.10.27 \\
-e MYSQL_PORT=3306 \\
-e MYSQL_DATABASE=emotion_museum \\
-e MYSQL_USERNAME=root \\
-e MYSQL_PASSWORD='EmotionMuseum2025*#' \\
-e REDIS_HOST=47.111.10.27 \\
-e REDIS_PORT=6379 \\
-e REDIS_PASSWORD= \\
-e REDIS_DATABASE=0 \\
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
-e NACOS_USERNAME=nacos \\
-e NACOS_PASSWORD='Peanut2817*#' \\
--restart unless-stopped \\
${PROJECT_NAME}/${SERVICE_NAME}:latest
"
# 等待启动
log_info "等待服务启动..."
sleep 15
# 检查状态
if ssh 'root@47.111.10.27' "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
log_success "服务启动成功"
# 显示日志
log_info "服务日志 最后20行:"
ssh 'root@47.111.10.27' "docker logs --tail 20 ${SERVICE_NAME}"
# 健康检查
log_info "执行健康检查..."
sleep 10
if ssh 'root@47.111.10.27' "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
log_success "健康检查通过"
else
log_warning "健康检查失败,服务可能仍在启动中"
fi
else
log_error "服务启动失败"
ssh 'root@47.111.10.27' "docker logs ${SERVICE_NAME}"
exit 1
fi
}
# 主函数
main() {
log_info "开始部署 $SERVICE_NAME 服务"
log_info "目标服务器: $REMOTE_HOST"
log_info "服务端口: $SERVICE_PORT"
log_info "部署环境: $PROFILE"
check_remote_connection
build_service
deploy_service
log_success "$SERVICE_NAME 服务部署完成!"
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
}
# 执行主函数
main "$@"
-119
View File
@@ -1,119 +0,0 @@
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.emotionmuseum</groupId>
<artifactId>backend</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>emotion-stats</artifactId>
<name>emotion-stats</name>
<description>统计分析服务</description>
<dependencies>
<!-- 内部模块依赖 -->
<dependency>
<groupId>com.emotionmuseum</groupId>
<artifactId>emotion-common</artifactId>
</dependency>
<!-- Spring Cloud Discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Boot DevTools for automatic restart -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- 监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- API文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,21 +0,0 @@
package com.emotionmuseum.stats;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 统计分析服务启动类
*
* @author emotion-museum
* @since 2025-07-12
*/
@SpringBootApplication
@MapperScan("com.emotionmuseum.stats.mapper")
public class StatsApplication {
public static void main(String[] args) {
SpringApplication.run(StatsApplication.class, args);
}
}
@@ -1,103 +0,0 @@
package com.emotionmuseum.stats.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.emotionmuseum.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户统计实体
*
* @author emotion-museum
* @since 2025-07-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("user_stats")
public class UserStats extends BaseEntity {
/**
* 用户ID
*/
@TableField("user_id")
private String userId;
/**
* 总对话数
*/
@TableField("total_conversations")
private Integer totalConversations;
/**
* 总消息数
*/
@TableField("total_messages")
private Integer totalMessages;
/**
* 总情绪记录数
*/
@TableField("total_emotions_recorded")
private Integer totalEmotionsRecorded;
/**
* 完成的课题数
*/
@TableField("topics_completed")
private Integer topicsCompleted;
/**
* 解锁的成就数
*/
@TableField("achievements_unlocked")
private Integer achievementsUnlocked;
/**
* 总积分
*/
@TableField("total_points")
private Integer totalPoints;
/**
* 连续使用天数
*/
@TableField("consecutive_days")
private Integer consecutiveDays;
/**
* 最大连续天数
*/
@TableField("max_consecutive_days")
private Integer maxConsecutiveDays;
/**
* 访问的地点数
*/
@TableField("locations_visited")
private Integer locationsVisited;
/**
* 创建的帖子数
*/
@TableField("posts_created")
private Integer postsCreated;
/**
* 评论数
*/
@TableField("comments_made")
private Integer commentsMade;
/**
* 收到的点赞数
*/
@TableField("likes_received")
private Integer likesReceived;
/**
* 社交互动数
*/
@TableField("social_interactions")
private Integer socialInteractions;
}
@@ -1,55 +0,0 @@
# 本地开发环境配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: Peanut2817*#
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: Peanut2817*#
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: debug
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-stats-local.log
@@ -1,55 +0,0 @@
# 本地开发环境配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: nacos
metadata:
version: 1.0.0
zone: local
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: localhost:8848
namespace:
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: nacos
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
# Redis配置
data:
redis:
host: localhost
port: 6379
password:
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: debug
com.baomidou.mybatisplus: debug
com.alibaba.nacos: info
file:
name: logs/emotion-stats-local.log
@@ -1,55 +0,0 @@
# 生产环境配置
spring:
cloud:
nacos:
discovery:
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
enabled: true
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
zone: prod
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: prod
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: warn
com.baomidou.mybatisplus: warn
com.alibaba.nacos: error
file:
name: logs/emotion-stats-prod.log
@@ -1,55 +0,0 @@
# 测试环境配置
spring:
cloud:
nacos:
discovery:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
enabled: true
username: nacos
password: EmotionMuseum2025
metadata:
version: 1.0.0
zone: test
register-enabled: true
ephemeral: true
cluster-name: DEFAULT
service: ${spring.application.name}
weight: 1
heart-beat-interval: 5000
heart-beat-timeout: 15000
ip-delete-timeout: 30000
config:
server-addr: 47.111.10.27:8848
namespace: test
group: DEFAULT_GROUP
file-extension: yml
enabled: false
username: nacos
password: EmotionMuseum2025
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://47.111.10.27:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: EmotionMuseum2025*#
# Redis配置
data:
redis:
host: 47.111.10.27
port: 6379
password: EmotionMuseum2025*#
database: 0
# 日志配置
logging:
level:
com.emotionmuseum: info
com.baomidou.mybatisplus: info
com.alibaba.nacos: warn
file:
name: logs/emotion-stats-test.log
@@ -1,73 +0,0 @@
server:
port: 19009
spring:
application:
name: emotion-stats
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: 47.111.10.27:8848
namespace: emotion-dev
group: DEFAULT_GROUP
enabled: false
config:
enabled: false
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
redis:
data:
host: localhost
port: 6379
password:
database: 0
timeout: 10000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
# 日志配置
logging:
file:
path: /data/logs/emotion-museum/stats
level:
com.emotionmuseum: debug
com.baomidou.mybatisplus: debug
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{50}] - %msg%n"