家庭理财管理系统-后端
Go to file
Bunny a7e481ca4c feat: 添加账单并上传文件 2025-01-02 22:51:39 +08:00
common refactor: 实体类移动 2025-01-01 16:19:09 +08:00
dao feat: 添加账单并上传文件 2025-01-02 22:51:39 +08:00
service feat: 添加账单并上传文件 2025-01-02 22:51:39 +08:00
LICENSE init 2024-11-11 12:03:29 +08:00
.gitignore refactor: 财务管理删除不需要的代码 2024-11-16 19:13:45 +08:00
ReadMe.md feat: 添加账单并上传文件 2025-01-02 22:51:39 +08:00
pom.xml feat: 导出账单 2024-11-26 23:10:38 +08:00

ReadMe.md

环境搭建

Ubuntu环境搭建

sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt update
sudo apt upgrade
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加Docker官方GPG密钥
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装docker
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 默认情况下只有root用户和docker组的用户才能运行Docker命令。我们可以将当前用户添加到docker组以避免每次使用Docker时都需要使用sudo设置完成后退出当前用户之后再进入既可
sudo usermod -aG docker $USER
# 运行docker
sudo systemctl start docker
# 安装工具
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# 重启docker
sudo service docker restart

避免每次操作都要输入sudo

# 创建分组一般
sudo groupadd docker
# 将当前用户添加到分组
sudo usermod -aG docker $USER
# 重启终端生效
exit

配置Ubuntu静态IP地址

打开配置文件

sudo vim /etc/netplan/00-installer-config.yaml

修改配置文件

network:
  ethernets:
    ens33:
      dhcp4: false
      addresses: [192.168.3.130/24]
      optional: true
      routes:
        - to: default
          via: 192.168.3.1
      nameservers:
        addresses: [8.8.8.8]
  version: 2

应用配置

sudo netplan apply
sudo reboot

安装docker内容

sudo mkdir /etc/docker
sudo touch /etc/docker/daemon.json
sudo chmod 777 /etc/docker/daemon.json
sudo vim /etc/docker/daemon.json

必要的 deaemon.json

{
    "registry-mirrors": [
         "https:\/\/jockerhub.com",
         "https://dockerhub.icu",
         "https://docker.1panel.live",
         "https://gwsg6nw9.mirror.aliyuncs.com",
         "https://registry.docker-cn.com",
         "http://hub-mirror.c.163.com",
         "http://f1361db2.m.daocloud.io",
         "https://mirror.ccs.tencentyun.com",
         "https://phtv51hj.mirror.aliyuncs.com",
         "https://gwsg6nw9.mirror.aliyuncs.com"
    ]
}

重启docker

sudo systemctl restart docker.socket

安装Redis

配置文件创建

mkdir ~/docker/docker_data/redis_master/ -p
vim  ~/docker/docker_data/redis_master/redis.conf

Redis配置文件

daemonize no
requirepass 123456
appendonly yes
tcp-keepalive 300

安装Redis镜像

docker run -p 6379:6379 --name redis_master \
-v ~/docker/docker_data/redis_master/redis.conf:/etc/redis/redis.conf \
-v ~/docker/docker_data/redis_master/data:/data \
--restart=always -d redis:7.0.10  --appendonly yes

安装Minio

docker run -d \
  -p 9000:9000 \
  -p 9090:9090 \
  --name minio_master --restart=always \
  -v ~/docker/docker_data/minio/data:/data \
  -e "MINIO_ROOT_USER=bunny" \
  -e "MINIO_ROOT_PASSWORD=02120212" \
  minio/minio server /data --console-address ":9090"

安装MySQL

# 创建3306配置文件
sudo mkdir -p ~/docker/docker_data/mysql/mysql_master/etc
sudo vim ~/docker/docker_data/mysql/mysql_master/etc/my.cnf

my.cnf 配置

[mysqld]
skip-host-cache
skip-name-resolve
secure-file-priv=/var/lib/mysql-files
user=mysql

# 设置字符集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

# 设置服务器ID如果是复制集群确保每个节点的ID唯一
server-id=1

# 启用二进制日志
log-bin=mysql-bin

# 设置表名不区分大小写
lower_case_table_names = 1

启动MySQL

docker stop mysql_master
docker rm mysql_master

docker run --name mysql_master -p 3306:3306 \
-v  ~/docker/docker_data/mysql/mysql_master/etc/my.cnf:/etc/my.cnf \
-v  ~/docker/docker_data/mysql/mysql_master/data:/var/lib/mysql \
-v  ~/docker/docker_data/mysql/mysql_master/backup:/backup \
--restart=always --privileged=true \
   -e MYSQL_ROOT_PASSWORD=123456 \
   -e TZ=Asia/Shanghai \
   mysql:8.0.33 --lower-case-table-names=1

安装MongoDB

创建数据库

docker stop mongodb
docker rm mongodb

docker run -it \
	--name mongodb \
	--restart=always \
    --privileged \
    -p 27017:27017 \
    -v ~/docker/docker_data/mongo/data:/data/db \
    -v ~/docker/docker_data/mongo/conf:/data/configdb \
    -v ~/docker/docker_data/mongo/logs:/data/log/  \
    -d mongo:latest \
    -f /data/configdb/mongod.conf

需要设置权限

mkdir -p ~/docker/docker_data/mongo/conf
mkdir -p ~/docker/docker_data/mongo/logs
chmod 777 ~/docker/docker_data/mongo/logs
chmod 777 ~/docker/docker_data/mongo/conf

cd ~/docker/docker_data/mongo/logs
touch mongod.log
chmod 777 mongod.log

cd ~/docker/docker_data/mongo/conf
vim mongod.conf

配置文件

# 数据库文件存储位置
dbpath = /data/db
# log文件存储位置
logpath = /data/log/mongod.log
# 使用追加的方式写日志
logappend = true
# 是否以守护进程方式运行
# fork = true
# 全部ip可以访问
bind_ip = 0.0.0.0
# 端口号
port = 27017
# 是否启用认证
auth = true
# 设置oplog的大小(MB)
oplogSize=2048

设置账户密码

#进入容器
docker exec -it mongodb /bin/bash

#进入mongodb shell
mongosh --port 27017

#切换到admin库
use admin

#创建账号/密码
db.createUser({ user: 'admin', pwd: '02120212', roles: [ { role: "root", db: "admin" } ] });
# db.createUser({ user: 'admin', pwd: '123456', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });

项目特点

RBAC

采用RBAC设计原设计数据表如下

img

登录功能

可以选择邮箱登录或者是密码直接登录,两者不互用。

账号登录

image-20241105212146456

业务需求

  • 用户输入用户名和密码进行登录

实现思路

  • 用户输入账号和密码和数据库中账号密码进行比对,成功后进行页面跳转
  • 如果账户禁用会显示账户已封禁

后端实现文件位置

  • 拦截请求为/admin/login的请求之后进行登录验证的判断

image-20241105212722043

邮箱登录

image-20241105212255972

业务需求

  • 用户输入邮箱账号、密码、邮箱验证码之后进行登录

实现思路

  • 需要验证用户输入的邮箱格式是否正确。
  • 在未输入验证码的情况下输入密码会提示用户同时后端也会进行验证。如果输入了邮箱验证码但是Redis中不存在或已过期会提示邮箱验证码不存在或已过期。
  • 之后对邮箱账号和密码进行判断包括邮箱验证码进行判断
  • 判断逻辑如下,文件位置如上图所示。
/**
 * * 自定义验证
 * 判断邮箱验证码是否正确
 */
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
    ObjectMapper objectMapper = new ObjectMapper();
    try {
        loginDto = objectMapper.readValue(request.getInputStream(), LoginDto.class);

        // type不能为空
        String type = loginDto.getType();
        if (!StringUtils.hasText(type)) {
            out(response, Result.error(ResultCodeEnum.REQUEST_IS_EMPTY));
            return null;
        }

        String emailCode = loginDto.getEmailCode();
        String username = loginDto.getUsername();
        String password = loginDto.getPassword();

        // 如果有邮箱验证码,表示是邮箱登录
        if (type.equals("email")) {
            emailCode = emailCode.toLowerCase();
            Object redisEmailCode = redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username));
            if (redisEmailCode == null) {
                out(response, Result.error(ResultCodeEnum.EMAIL_CODE_EMPTY));
                return null;
            }

            // 判断用户邮箱验证码是否和Redis中发送的验证码
            if (!emailCode.equals(redisEmailCode.toString().toLowerCase())) {
                out(response, Result.error(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING));
                return null;
            }
        }

        Authentication authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        return getAuthenticationManager().authenticate(authenticationToken);
    } catch (IOException e) {
        out(response, Result.error(ResultCodeEnum.ILLEGAL_DATA_REQUEST));
        return null;
    }
}

功能介绍

首页功能

业务需求

  1. 要求统计当前数据已消费金额、收入 金额、支出占比、盈利金额
  2. 根据收入支出数据展示双轴图数据
  3. 根据收入支出数据进行排行榜
  4. 展示收入支出表格数据

实现

计算当前月份总收入、总支出、盈利金额、支出占比

分析概览中展示当前的支出为红色柱状图;绿色折线图为收入

排行榜展示当前月份收入或支出最高的金额

image-20250101164041383

日期选择器可以筛选当前年的月份和本周数据,选择后展示数据也会发生变化

image-20250101164422293

选择日期为12月1日~12月7日

image-20250101164613379

消息功能

业务需求

  1. 消息页面的展示,包含删除、批量删除、选中已读和当前页面所有消息都标为已读

  2. 当用户对左侧菜单点击时可以过滤出消息内容,展示不同的消息类型

  3. 可以点击已读和全部进行筛选消息

  4. 可以根据标题进行搜搜

  5. 包含分页

实现

整个页面刷新会第一次进入都会获取消息数据,获取之后计算有多少消息,包含所有消息分类下的所有消息数,在页面的顶部展示

image-20250101164852021

第一次刷新页面后会获取消息,之后还会在下方展示,展示消息大概信息内容

image-20250101164931942

功能菜单,首页图表展示部分功能已经由这个模板作者设计好,其中需要注意的是,如果要查看历史消息或者是进入消息页面可以双击image-20241105213346408既可进入消息页面,消息页面中会有消息的阅读情况,是否已读或维度

image-20250101164958864

消息未读时,会显示未拆封信件图标

image-20250101165123316

点击这个消息进入详情页,针对不同的编辑器会显示不同的效果,当前是使用富文本编辑器,效果图如下

image-20250101165230016

阅读完成后不会再提示该消息,刷新页面后也不会再出现已读消息,阅读完成后图标也会发生变化,出现拆封信件图标样式

image-20250101165352744

消息已读并不会影响其他用户消息,不会出现当前用户读取消息后其他用户消息也已读

财务管理

业务需求

  1. 对账单表CURD查询
  2. 导出用户数据表
  3. 添加账单方式
    1. 手动添加
    2. Excel表添加
  4. 首页导航栏出也要有入口可以添加

实现

首页导航栏点击也可以弹出记账功能弹窗

image-20250101171718156

手动导入

记账功能弹窗

image-20250101171751011

文件导入

使用文件导入,因为每个用户类别不一样,详见账单分类管理,要根据这些分类生成对应的枚举 ,否则用户不知道自己有哪些类别,类别不一致导入数据时会找不到字段。

后端需要查询当前用户的数据库找到属于这个用户的字段填充到下面的Excel表中

image-20250101172238607

效果示例

image-20250101172352120

之后用户添加时表格中会有数据校验,如果这个字段不存在会报警告

image-20250101172447687

点击文件导入,弹出上传文件的弹窗,选择要上传文件或者直接拖动文件到窗口之后点击确认就可以导入。

image-20250101171821403

image-20250101171833551

如果数据导入失败会显示导入失败的数据条目到消息中,会在消息中显示

image-20250101172639305

用户管理

image-20241106002713514

需求分析

  1. 用户操作需要包含CURD的操作
  2. 为了方便在用户中需要给出快速禁用当前用户按钮
  3. 需要显示用户头像、性别、最后登录的IP地址和归属地
  4. 在左侧中需要包含部分查询
  5. 可以根据点击的部门查询当前部门下的用户
  6. 根据用户可以强制下线、管理员可以修改用户密码、为用户分配角色

image-20241106002908657

实现思路

上传头像

前端需要剪裁图片内容进行上传后端将前端上传的头像存储到Minio中在上传头像中可以有几菜单可以看到功能菜单。

image-20241106003056116

右击时可以看到功能菜单,如上传、下载等功能

image-20241106003154056

重置密码

重置密码需要判断当前用户密码是否是符合指定的密码格式,并且会根据当前输入密码计算得分如果当前密码复杂则得分越高那么密码强度越强

image-20241106003256994

重置密码组件在前端的公共组件文件中

image-20241106003426573

分配角色

  • 给用户分配了admin角色后其他路由绑定和权限设置就不再需要了因为后端会根据admin角色在前端用户信息中设置通用权限码**::**::*::*,表示前端用户可以访问所有权限并查看所有内容。

  • 管理员有权对用户进行角色分配,这涉及到许多操作,包括菜单显示和接口访问权限。角色与权限相关联,角色也与菜单相关联。

  • 当用户访问菜单时,会根据其角色看到其所属的菜单内容。随后,角色与权限接口相关联,根据用户的权限来决定是否显示操作按钮。后端会根据用户的权限验证其是否可以访问当前接口。

  • 用户登录或刷新页面时会重新获取用户信息,用户信息中包含角色和权限信息。利用角色和权限信息与前端传递的路径进行比对判断,如果用户包含菜单角色,则可以访问。如果用户包含前端路由中的权限,则表示该权限可以访问。后端也会进行权限判断,以防止通过接口文档等方式访问。

  • 分配好角色后,菜单会根据当前路由角色匹配用户角色,从而根据用户角色显示相应的菜单内容。

image-20241106004533031

角色管理

角色管理包含CURD和权限分配操作

image-20241106132548236

业务需求

用户对角色进行CURD操作点击权限设置时让用户分配权限

实现思路

  1. 在设计的表中,如果存在相同的角色码,系统会提示用户当前角色已经存在。

image-20241106132938024

  1. 后端会根据角色的ID分配权限的ID列表。

image-20241106135600255

  1. 后端在角色权限表中会根据角色的ID分配权限内容。在角色权限表中会先删除当前角色所有的权限内容然后再进行权限内容的重新分配。
public void assignPowersToRole(AssignPowersToRoleDto dto) {
    List<Long> powerIds = dto.getPowerIds();
    Long roleId = dto.getRoleId();

    // 删除这个角色下所有权限
    baseMapper.deleteBatchRoleIdsWithPhysics(List.of(roleId));

    // 保存分配数据
    List<RolePower> rolePowerList = powerIds.stream().map(powerId -> {
        RolePower rolePower = new RolePower();
        rolePower.setRoleId(roleId);
        rolePower.setPowerId(powerId);
        return rolePower;
    }).toList();
    saveBatch(rolePowerList);

    // 找到所有和当前更新角色相同的用户
    List<Long> roleIds = userRoleMapper.selectList(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getRoleId, roleId))
            .stream().map(UserRole::getUserId).toList();

    // 根据Id查找所有用户
    List<AdminUser> adminUsers = userMapper.selectList(Wrappers.<AdminUser>lambdaQuery().in(!roleIds.isEmpty(), AdminUser::getId, roleIds));

    // 用户为空时不更新Redis的key
    if (adminUsers.isEmpty()) return;

    // 更新Redis中用户信息
    List<Long> userIds = adminUsers.stream().map(AdminUser::getId).toList();
    roleFactory.updateUserRedisInfo(userIds);
}

权限管理

image-20241106135954104

image-20241106140006176

在权限配置中,添加/修改权限时的请求地址为后端接口的请求地址,请求地址使用了【正则表达式】判断和【antpath】方式填写

正则表达式

作用和用法:

  • 作用:正则表达式用于描述字符串的特征,可以用来匹配、查找、替换等字符串操作。
  • 用法在Java中可以使用java.util.regex包来支持正则表达式的使用。例如,可以使用PatternMatcher类来编译和匹配正则表达式。

示例:

// 匹配邮箱地址的正则表达式示例
String emailRegex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b";
String email = "example@email.com";

Pattern pattern = Pattern.compile(emailRegex);
Matcher matcher = pattern.matcher(email);

if (matcher.find()) {
    System.out.println("Valid email address");
} else {
    System.out.println("Invalid email address");
}

Ant Path

作用和用法:

  • 作用Ant Path是Spring框架中用来匹配URL路径的一种模式匹配方式类似于Unix系统中的路径匹配规则。
  • 用法在Spring中Ant Path可以用来匹配URL路径例如在配置Spring的URL映射时可以使用Ant Path来指定匹配规则。

示例:

// Ant Path示例
String pattern = "/users/*/profile";
String path = "/users/123/profile";

AntPathMatcher matcher = new AntPathMatcher();
if (matcher.match(pattern, path)) {
    System.out.println("Pattern matched!");
} else {
    System.out.println("Pattern not matched!");
}

Ant Path中支持一些通配符例如*匹配任意字符(除了路径分隔符),**匹配任意字符包括路径分隔符。Ant Path是一种方便的路径匹配方式可以用来匹配URL路径、文件路径等。

业务需求

  1. 对权限表进行CURD操作
  2. 在表格中点击新增时父级id为当前点击行的id

实现思路

点击当前行父级id为当前的行的id

image-20241106140420845

权限判断实现方式

后端判断方式

判断权限是否可以访问,后端实现判断逻辑

image-20241106003921315

前端判断方式

角色分配方式有下面几种想洗参考:https://pure-admin.github.io/vue-pure-admin/#/permission/button/router文档页面

  1. 使用标签方式

image-20241106004247600

  1. 使用函数方式

image-20241106004310635

  1. 使用指令方式

image-20241106005252328

在前端utils文件夹下有auth.ts文件里面包含了权限码信息,如果当前菜单属性中包含这个权限码表示可以访问这个权限

image-20241106004433489

image-20241106004500855

菜单管理

image-20241106140545328

菜单路由

在做菜单返回时必须要了解角色和权限表

image-20241105213516679

需求分析

  1. 从数据库中返回出所有的菜单,其中需要整合成前端所要的形式,需要包含rolesauths,及其其它参数。

  2. 用户需要根据自己的角色访问不同的菜单。

  3. 如果当前用户不可以访问某些按钮需要隐藏。

  4. 用户通过其它手段访问如swagger、knife4j、apifox、postman这种工具访问需要做权限验证如果当前用户不满足访问这些接口后端需要拒绝。

  5. 如果已经添加了菜单名称、路由等级、路由路径会提示xxx已存在image-20241106132818902

  6. 在数据库中为部分字段建立了唯一索引

image-20241106132908309

实现思路

  1. 角色和权限哪些可以访问的页面交给前端,在模板中已经设计好,如果用户访问了自己看不到的菜单会出现403页面;判断方式是根据后端返回的菜单中如果包含当前用户的角色就表示可以访问当前的菜单,如果用户信息中没有这个角色则表示不可以访问这个页面。
  2. 页面是否可以访问只是在操作上,如果用户通过接口访问是阻止不了的,所以这时后端需要在后端中进行判断,当前的访问路径是否是被允许的,也就是这个用户是否有这个权限,权限表设计中包含了请求路径
  3. 后端需要判断用户请求这个接口是否有权访问

整合成前端格式返回需要递归,后端根据当前用户访问的菜单需要进行递归菜单数据之后返回前端,并将这些菜单绑定的角色放置在roles中,之后根据角色查询全新啊相关内容,要将权限内容放置在auths中.

如果包含子菜单需要防止在children数组中,后端实现时如果没有子菜单默认是空数组而不是null

大致如下:

{
"menuType": 0,
"title": "admin_user",
"path": "/system/admin-user",
"component": "/system/adminUser/index",
"meta": {
    "icon": "ic:round-manage-accounts",
    "title": "admin_user",
    "rank": 2,
    "roles": [
        "admin",
        "all_page",
        "system",
        "test"
    ],
    "auths": [
        "message::updateMessage",
        "menuIcon::getMenuIconList",
        "admin::messageReceived",
        "config::getWebConfig",
        "admin::config",
        "i18n::getI18n",
        ....
    ],
    "frameSrc": ""
},
"children": [],
"id": "1841803086252548097",
"parentId": "1",
"name": "admin_user",
"rank": 2
}

部门管理

image-20241106140738517

image-20241106140728748

业务需求

  1. 包含CURD
  2. 在用户管理中可以选择对应的部门

实现思路

  1. CURD接口文件如下

image-20241106140826034

  1. 管理员为用户分配部门

image-20241106140942278

菜单图标

image-20241106141037894

image-20241106141102601

业务需求

  1. 用户在菜单中可以选择存储在数据库中的图标内容
  2. 包含CURD内容

实现思路

后端需要返回接口格式实体类如下

public class MenuIconVo extends BaseUserVo {

    @Schema(name = "iconCode", title = "icon类名")
    private String iconCode;

    @Schema(name = "iconName", title = "icon 名称")
    private String iconName;

}

image-20241106141521051

前端封装好的组件

image-20241106141626697

邮箱相关

业务需求

  1. 邮件用户配置CURD
  2. 邮件模板CURD
  3. 邮件用户中只能有一个是默认的,如果当前修改其它项需要将其它已经启用改为不启用
  4. 邮件模板需要绑定邮件用户

实现思路

邮件模板中添加或者修改时前端需要返回所有的邮件模板用户添加或者修改时将用户ID存储在邮件模板的数据字段中

image-20241106141920350

web配置

image-20241106142001190

系统监控

服务监控

从SpringBoot的Actuator中获取信息页面采用响应式

image-20241106142208794

系统缓存

当前内容被SpringBoot缓存会显示在这

image-20241106142253759

定时任务

采用Quarter持久化存储所有的可以使用的定时任务都在这

image-20241106142429924

页面展示

image-20241106142449033

多语言管理

image-20241106142531047

image-20241106142544172

日志管理

image-20241106142606017

image-20241106142614917

消息管理

管理员可以发送消息告诉xxx用户在主页中会显示image-20241106142908363

之后点击时会看到消息封面、标题、简介、消息等级、消息等级内容

image-20241106142949366

消息类型

image-20241106143008098

包含CURD用户编辑消息发送时可以在选择

image-20241106144017015

同时在用户消息栏中也会显示对应内容

image-20241106144050996

前端判断逻辑如下

image-20241106144146081

消息编辑

提供md编辑器和富文本编辑器

image-20241106144223976

image-20241106144246068

消息接受用户,如果不填写表示全部的用户,填写后会根据填写的内容存储在用户接受表中image-20241106144522442

image-20241106144449463

消息等级是显示消息样式颜色,文字内容为消息简介内容

image-20241106144407453

消息接收管理

根据上面所选的接受用户会出现在下面的用户接受表中,可以对当前用户是否已读进行修改

image-20241106144307885

消息发送管理

之前编辑过的消息都会在这

image-20241106144317746

环境部署

使用Docker进行部署后端接口地址以/admin开头,但前端默认请求前缀为/api,因此在请求时需要进行替换。详细内容请参考以下【项目部署】说明。

配置相关

docker文件

# 使用官方的 Nginx 镜像作为基础镜像
FROM nginx

# 删除默认的 Nginx 配置文件
RUN rm /etc/nginx/conf.d/default.conf

# 将自定义的 Nginx 配置文件复制到容器中
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 设置时区,构建镜像时执行的命令
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" > /etc/timezone

# 创建一个目录来存放前端项目文件
WORKDIR /usr/share/nginx/html

# 将前端项目打包文件复制到 Nginx 的默认静态文件目录
COPY dist/ /usr/share/nginx/html
# 复制到nginx目录下
COPY dist/ /etc/nginx/html

# 暴露 Nginx 的默认端口
EXPOSE 80

# 自动启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

NGINX文件

在请求中会使用代理所以会拿不到用户真实的IP地址素以在要NGINX侠做下面的配置这样用户在访问时就可以拿到真实的IP了

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

如果需要使用https协议

COPY bunny-web.site.csr /etc/nginx/bunny-web.site.csr
COPY bunny-web.site.key /etc/nginx/bunny-web.site.key
COPY bunny-web.site_bundle.crt /etc/nginx/bunny-web.site_bundle.crt
COPY bunny-web.site_bundle.pem /etc/nginx/bunny-web.site_bundle.pem

NGINX的文件

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen       80;
    listen       [::]:80;
    server_name  localhost;

    location / {
        root   /etc/nginx/html;
        index  index.html index.htm;
        try_files $uri /index.html;
    }

    # 后端跨域请求
    location ~/admin/ {
        proxy_pass http://172.17.0.1:8000;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    error_page  404              404.html;

    location = /50x.html {
        root   html;
    }
}

项目部署

使用WebStorm进行项目部署项目上线时默认端口为80。因此Docker默认暴露的IP端口也应为80NGINX中默认暴露的端口也是80三者应一一对应。

若无法下载请先使用pnpm下载。若不需使用pnpm请删除或修改相应内容。

image-20241026025057129

docker配置

image-20241026024116090

配置环境

设置启动端口号和项目地址机器后端请求地址

image-20241026024813858

配置线上环境

设置项目启动端口号,线上环境默认请求路径为/admin需在NGINX中将访问请求前缀更改为/admin

image-20241026024940747

image-20241026024243785

配置开发环境

开发环境默认IP为7000若与本地项目端口冲突请修改。后端请求地址为7070。

前端设置的请求前缀为/api,但后端接受的前缀为/admin,因此需在服务中修改此内容。

image-20241026024318644

修改请求路径

image-20241026031651591

部署命令

docker build -f Dockerfile -t bunny_auth_web:1.0.0 . && docker run -p 80:80 --name bunny_auth_web --restart always bunny_auth_web:1.0.0