diff --git a/README.md b/README.md index 2f48e4f..16c90b0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ ![架构图](./doc/jgt.png) - ## CI/CD 自动化集群部署 ![cicd](./doc/newci.png) @@ -247,11 +246,13 @@ export default class Header extends TSX()(Vue) implements Props { - 2: 在1台Linux服务器上,部署公司自己的 Docker Registry 镜像存储库 - 3: 准本 N台运行 前端各模块 的Linux服务器,并提前组成N个Docker Swarm 集群,以供前端各模块部署使用 - 4: 准本 >=3台运行 Nacos服务中心 的Linux服务器,并提前组成Docker Swarm 集群,提前运行好,确保服务可用 - - [Nacos 部署文档](./docker/nacos-cluster/README.md) + - [Nacos 部署文档](./docker/nacos-cluster/README.md) - 5: 准本 一台 运行 traefik 网关的Linux服务器,,提前运行好,确保服务可用 - - [Traefik 部署文档](./docker/traefik/README.md) - + - [Traefik 部署文档](./docker/traefik/README.md) **前端工作:** -详细请看 [app-product的README.md](./front-end/app-product/README.md) 内的帮助文档!!! +详细请看 各前端模块 存储库内的 帮助文档!!! + +- [商品模块 app-product](https://gitee.com/bmycode/app-product) + diff --git a/front-end/app-product/.dockerignore b/front-end/app-product/.dockerignore deleted file mode 100644 index ac4c8c6..0000000 --- a/front-end/app-product/.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -node_modules -dist -.git -.DS_Store -*.log -docker/ # 排除 docker 配置文件夹本身 -.editorconfig -.eslintrc.js -.prettierrc - -.vscode -.idea -*.swp -*.swo diff --git a/front-end/app-product/.env.development b/front-end/app-product/.env.development deleted file mode 100644 index 6911310..0000000 --- a/front-end/app-product/.env.development +++ /dev/null @@ -1,26 +0,0 @@ -# 请求协议 http or https(建议线上配置证书后,使用https) -VITE_PREFIX=http:// - -# 内网 中的 Nacos 配置中心 ip:端口 | 域名 -#VITE_NACOS_ADDRESS=cluster.nacos.com -VITE_NACOS_ADDRESS=192.168.139.84:8848 - - -# 注册到 Nacos 的服务名称(微前端模块名称 -VITE_SERVER_NAME=app-product -# 注册到 Nacos 的端口,也是前端运行的端口 -VITE_SERVER_PORT=1301 - - -# -# 说明:项目要调用哪些 微前端 模块 -# 格式:VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供) -# 作用:通过名称从Nacos配置中心发现模块地址和端口,给 federation 实时加载 -# - -# shared 共享资源模块 -VITE_MODEL_SHARED_NAME=app-shared - - - - diff --git a/front-end/app-product/.env.production b/front-end/app-product/.env.production deleted file mode 100644 index 82ba25e..0000000 --- a/front-end/app-product/.env.production +++ /dev/null @@ -1,23 +0,0 @@ -# 请求协议 http or https(建议线上配置证书后,使用https) -VITE_PREFIX=http:// - -# 内网 中的 Nacos 配置中心 ip:端口 | 域名 -#VITE_NACOS_ADDRESS=cluster.nacos.com -VITE_NACOS_ADDRESS=192.168.139.84:8848 - - -# 注册到 Nacos 的服务名称(微前端模块名称 -VITE_SERVER_NAME=app-product -# 注册到 Nacos 的端口,也是前端运行的端口 -VITE_SERVER_PORT=1301 - - -# -# 说明:项目要调用哪些 微前端 模块 -# 格式:VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供) -# 作用:通过名称从Nacos配置中心发现模块地址和端口,给 federation 实时加载 -# - -# shared 共享资源模块 -VITE_MODEL_SHARED_NAME=app-shared - diff --git a/front-end/app-product/.gitea/workflows/build.yml b/front-end/app-product/.gitea/workflows/build.yml deleted file mode 100644 index 567ba82..0000000 --- a/front-end/app-product/.gitea/workflows/build.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: build and push - -# 当代码推送到 main 分支时触发 -on: - push: - branches: [ main ] - -jobs: - build-and-push: - runs-on: ubuntu-latest - - steps: - - name: 拉取最新代码 - uses: actions/checkout@v4 - - - name: 设置 Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: 登录到自己的 Docker 仓库 - uses: docker/login-action@v3 - with: - registry: registry.bmycode.help - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - - name: 构建并推送镜像 - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: | - registry.bmycode.help/app-product:latest - registry.bmycode.help/app-product:${{ github.sha }} - - - - name: 替换镜像标签为 github.sha - run: | - sed -i "s|{{IMAGE_TAG}}|${{ github.sha }}|g" app-product.yml - - - name: 上传 app-product.yml 到前端集群管理机 - uses: appleboy/scp-action@v1 - with: - host: ${{ vars.SSH_MANAGE }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - port: ${{ secrets.PORT }} - source: "app-product.yml" - target: "/home/bmy/" - - - name: 登录前端集群,拉镜像 - uses: appleboy/ssh-action@v1 - with: - # 统一登录三台前端集群的linux,统一拉取刚推到仓库的最新镜像 - # 解决 Swarm 无法控制 工作机拉取镜像的问题! - host: ${{ vars.SSH_HOST_LIST }} - sync: true - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - port: ${{ secrets.PORT }} - script: | - # 拉取刚打包的最新镜像 - docker pull registry.bmycode.help/app-product:${{ github.sha }} - - - name: 管理节点部署到 Swarm - uses: appleboy/ssh-action@v1 - with: - host: ${{ vars.SSH_MANAGE }} - sync: true - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - port: ${{ secrets.PORT }} - script: | - # 切换到管理员,要不然没权限在服务器执行 docker 命令,部署到 Swarm 集群 - sudo docker stack deploy -c /home/bmy/app-product.yml app-product diff --git a/front-end/app-product/Dockerfile b/front-end/app-product/Dockerfile deleted file mode 100644 index 56b3f97..0000000 --- a/front-end/app-product/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:24-alpine AS builder -WORKDIR /app - -COPY package.json ./ - -# 直接使用 npm install 并解决依赖问题 -RUN npm install --no-audit --no-fund --legacy-peer-deps - -# 拷贝源码并构建 -COPY . . - -# 【前端修改】如果要以子路径 /app-product/ 部署,请用 base 指定前缀,同 .env中VITE_SERVER_NAME -RUN npx vite build --base=/app-product/ - -FROM nginx:alpine -# 构建产物放到 /usr/share/nginx/html/app-product -# nginx.conf 也需要修改 -COPY --from=builder /app/dist /usr/share/nginx/html/app-product -# 覆盖 Nginx 配置,支持 SPA 子路径 -COPY docker/nginx.conf /etc/nginx/conf.d/default.conf -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/front-end/app-product/README.md b/front-end/app-product/README.md deleted file mode 100644 index 9c5e811..0000000 --- a/front-end/app-product/README.md +++ /dev/null @@ -1,156 +0,0 @@ - -## app-product 商品模块(微前端模块) - -从项目中拆分出来,用于演示的微前端模块,主要实现 商品相关功能。本案例演示中提供: - -- 调用 微前端 app-shared 模块 -- 调接口,获取商品首页的数据和展示 -- 调接口,点击商品列表进入商品详情页 - - -## 运行 - -下面文档分为**开发**和**上线**两部分进行说明!**默认前端已经在电脑上安装过 Docker 环境!!** - -> 想要【开发阶段运行】,必须 先参考下面 【上线】-【任务1】配置好两个前端自己的环境变量文件!很简单! - -### 开发 - -```bash -# 安装依赖 -npm install - -# 开发阶段 运行 项目 -# PS:注意此命令运行的项目,【无法】被其他 微前端模块 使用,仅用于本地独立开发,独立测试 -npm run dev - - -# 启动 实时打包 & 实时预览 -# PS:注意此命令运行的项目,【可以】被其他 微前端模块 调用,可用于本地开发和微前端联合开发 -npm run dep -``` - -### 上线 - -代码 到 仓库的 **main 分支** 后会触发 全程自动化,实现自动打包前端代码,构建镜像,自动部署到公司内网的Linux服务器集群中! -提交到其他分支不触发自动化操作!具体什么规则,以 技术负责人 说的为准!请遵守规则! - - -**根目录核心文件说明** - -- `app-product.yml` - Dcoker Swarm 集群部署配置【不准动】 -- `docker-compose.yml` - 开发阶段容器编排【可选,不要就删除】 -- `Dockerfile` - 打包构建前端代码为镜像【根据需求改动】 -- `nginx.conf` - 镜像内前端被访问的Nginx配置【根据需求改动】 -- `act-runner.yml` - gitea action 的 runner【根据需求改动】 - -前端工作: - -前期大致工作内容如下,改好后,以后就不用再动了,很简单: - -- 任务1: 编辑 前端 .env.development 和 .env.production 环境变量(只需修改一次) -- 任务2: 确认或编辑 Dockerfile,nginx.conf -- 任务3: 编辑 act-runner.yml - ---- - -详细展开说明: - -**任务1:** - -主要修改两个环境变量文件 `.env.development`,`.env.production` 中的: - -```bash - -# 找技术负责人要 -# 情况1: 【内网模块】填写 Nacos 内网 ip 或 域名 即可 -# 情况2: 【外网模块】填写 traefik 公网 ip 或 域名 即可 -# .env.development, .env.production 中 可以不一样! -VITE_NACOS_ADDRESS=???? - - -# 这个参数很重要,前端可以自定义填写,填写后不允许再改,填完给 技术负责人 即可 -# 微前端应用的名称,遵守 app-xxxxx -# .env.development, .env.production 中 必须一样! -VITE_SERVER_NAME=app-??? - -#【ps】其他参数看环境变量内说明 -``` - ---- - -**任务2:** - -主要修改两个 `Dockerfile`,`nginx.conf`,改起来也很简单: - - -Dockerfile 修改教程: - -```bash - - -# 将 VITE_SERVER_NAME 替换为 上一步设置的 VITE_SERVER_NAME 的 数值 即可 -RUN npx vite build --base=/VITE_SERVER_NAME/ - -# 将 VITE_SERVER_NAME 替换为 上一步设置的 VITE_SERVER_NAME 的 数值 即可 -COPY --from=builder /app/dist /usr/share/nginx/html/VITE_SERVER_NAME -``` - - -nginx.conf 修改教程: - -```bash -# 看仔细了,有三处 VITE_SERVER_NAME 需要替换成 值!! -location /VITE_SERVER_NAME/ { - alias /usr/share/nginx/html/VITE_SERVER_NAME/; - try_files $uri $uri/ /VITE_SERVER_NAME/index.html; - add_header 'Access-Control-Allow-Origin' '*'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; -} -``` - ---- - -**任务3:** - -act-runner.yml 是 Gitea Action 的核心,Gitea会将Job委托给Runner来运行,**所以我们前端本地必须要先运行 act-runner.yml 文件**,这样在我们提交代码的时候 Gitea Action 的自动构建才会运行!! - -但是,运行 act-runner.yml 之前,需要先对 act-runner.yml 文件进行简单的配置: - -```bash - -# 核心 需要改动的配置 -# 可以直接让 仓库 的管理员 给你把下面参数之间配置好,当然你也可以自己配 - -# GITEA_INSTANCE_URL=这个前端项目模块的 git仓库地址 -# 格式:协议://域名 即可,例如:https://git.bmycode.help 或 https://gitee.com 等 - -# 项目仓库首页-设置-Actions-Runner 右上角可复制令牌 -# GITEA_RUNNER_REGISTRATION_TOKEN=注册令牌,来源三选一:实例,组织,全存储库 -environment: - - GITEA_INSTANCE_URL=https://git.bmycode.help - - GITEA_RUNNER_REGISTRATION_TOKEN=WFiO5JDwz7CHnk7fMGJYMcSITU81CLXuI7Ofg10e - -``` - -改好后,在**项目文件夹根目录**打开终端,通过下面命令运行 act-runner.yml 文件: - -```bash -docker-compose -f act-runner.yml up -d - -# 运行后,如果 在 docker desktop 的容器中找到 名字叫:gitea-act-runner 的容器,查看日志: - -# 有下面这行,则表示 runner 成功启动!!docker desktop 可以关闭了或者放后台,不用管了! -# level=debug msg="Successfully pinged the Gitea instance server" - -# 如果出问题了,找 技术负责人! -``` - ---- - - -**到这一步,上线 配置已经搞完,很简单吧!剩下的,你提交代码到仓库就行,项目会自动构建,自动打包发布到公司内网集群中部署和运行,不用你管了!!** - - diff --git a/front-end/app-product/act-runner.yml b/front-end/app-product/act-runner.yml deleted file mode 100644 index 72febb6..0000000 --- a/front-end/app-product/act-runner.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: '3.8' - -services: - gitea-actions-runner: - image: gitea/act_runner:latest - container_name: gitea-act-runner - restart: unless-stopped - volumes: - - runner-data:/data - - /var/run/docker.sock:/var/run/docker.sock - environment: - - GITEA_INSTANCE_URL=https://git.bmycode.help - - GITEA_RUNNER_REGISTRATION_TOKEN=WFiO5JDwz7CHnk7fMGJYMcSITU81CLXuI7Ofg10e - - GITEA_RUNNER_LABELS=ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest - -volumes: - runner-data: diff --git a/front-end/app-product/app-product.yml b/front-end/app-product/app-product.yml deleted file mode 100644 index 97e0705..0000000 --- a/front-end/app-product/app-product.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: "3.8" # Docker Compose 文件版本,3.8 支持 Swarm 模式的所有功能 - -services: - app-product: - image: registry.bmycode.help/app-product:{{IMAGE_TAG}} - ports: - - target: 80 # 容器内部端口 - published: 1301 # 宿主机对外暴露端口 - protocol: tcp # 协议类型 - mode: ingress # 模式:使用路由网格 - deploy: - replicas: 3 # 副本数量,在集群中运行 3 个容器实例 - placement: - max_replicas_per_node: 1 # 每个 Swarm 节点最多运行 1 个副本,确保高可用性 - restart_policy: - condition: any # 重启条件:任何情况下容器退出都重启 - delay: 5s # 重启延迟 5 秒 - max_attempts: 3 # 最大重启尝试次数 - update_config: # 滚动更新配置 - parallelism: 1 # 并行更新数量:一次只更新 1 个容器 - delay: 10s # 更新间隔:每个容器更新后等待 10 秒再更新下一个 - failure_action: rollback # 更新失败时自动回滚到上一版本 - order: start-first # 更新顺序:先启动新容器,确认健康后再停止旧容器 - rollback_config: # 回滚配置 - parallelism: 1 # 回滚时并行数量 - delay: 5s # 回滚延迟 - resources: # 资源限制 - limits: # 资源上限 - cpus: "0.50" # 最多使用 0.5 个 CPU 核心 - memory: "256M" # 最多使用 256MB 内存 - reservations: # 资源预留 - cpus: "0.25" # 预留 0.25 个 CPU 核心 - memory: "128M" # 预留 128MB 内存 - networks: - - frontend # 连接到 frontend 网络 - -networks: - frontend: # 定义 frontend 网络 - driver: overlay # 使用 overlay 网络驱动,支持跨主机通信 \ No newline at end of file diff --git a/front-end/app-product/docker-compose.yml b/front-end/app-product/docker-compose.yml deleted file mode 100644 index 3425db9..0000000 --- a/front-end/app-product/docker-compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: "3.8" - -services: - app-product: - build: - context: .. - dockerfile: docker/Dockerfile - - image: registry.bmycode.help/app-product:latest - container_name: app-product - ports: - - "1301:80" - restart: unless-stopped diff --git a/front-end/app-product/index.html b/front-end/app-product/index.html deleted file mode 100644 index 29d8565..0000000 --- a/front-end/app-product/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - Vite App - - - -
- - - - \ No newline at end of file diff --git a/front-end/app-product/nginx.conf b/front-end/app-product/nginx.conf deleted file mode 100644 index bd5a27d..0000000 --- a/front-end/app-product/nginx.conf +++ /dev/null @@ -1,14 +0,0 @@ -server { - listen 80; - server_name *.ddd.com; - - - location /app-product/ { - alias /usr/share/nginx/html/app-product/; - try_files $uri $uri/ /app-product/index.html; - add_header 'Access-Control-Allow-Origin' '*'; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; - } -} \ No newline at end of file diff --git a/front-end/app-product/package.json b/front-end/app-product/package.json deleted file mode 100644 index 0fbb814..0000000 --- a/front-end/app-product/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "app-product", - "version": "0.0.0", - "private": true, - "type": "module", - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "scripts": { - "dev": "vite", - "dep": "concurrently \"vite build --watch\" \"sleep 5 && npm run preview\"", - "preview": "vite preview --port 1301 --strictPort", - "build": "vite build", - "type-check": "vue-tsc --build" - }, - "dependencies": { - "less": "^4.4.1", - "pinia": "^3.0.3", - "vite-plugin-nacos": "^1.1.3", - "vue": "^3.5.22", - "vue-router": "^4.5.1" - }, - "devDependencies": { - "@element-plus/icons-vue": "^2.3.2", - "@tsconfig/node22": "^22.0.2", - "@types/node": "^24.6.1", - "@vitejs/plugin-vue": "^6.0.1", - "@vitejs/plugin-vue-jsx": "^5.1.1", - "@vue/tsconfig": "^0.8.1", - "element-plus": "^2.11.4", - "npm-run-all2": "^8.0.4", - "rollup-plugin-visualizer": "^6.0.3", - "typescript": "~5.9.3", - "vite": "^7.1.7", - "vite-federation": "^3.4.4", - "vite-plugin-vue-devtools": "^8.0.2", - "vue-tsc": "^3.1.0" - } -} \ No newline at end of file diff --git a/front-end/app-product/src/App.tsx b/front-end/app-product/src/App.tsx deleted file mode 100644 index 8fa3b43..0000000 --- a/front-end/app-product/src/App.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { defineComponent, onMounted } from 'vue' - -export default defineComponent({ - setup() { - - onMounted(() => { - console.log("import.meta.env: ", import.meta.env) - }) - - return () => ( - <> -
-

商品-联邦模块

-

此页面仅用于 商品模块 的单独开发和调试,可临时引入组件进行本地测试,实际运行时由 主应用 - 加载本项目代码

- -

本模块包含的功能:

- -

-
- - ) - } -}) \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/application/services/ProductApplicationService.ts b/front-end/app-product/src/domains/product/application/services/ProductApplicationService.ts deleted file mode 100644 index aa42975..0000000 --- a/front-end/app-product/src/domains/product/application/services/ProductApplicationService.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ErrorText } from "../../domain/enums/ErrorEnums"; -import { ProductRepository } from "../../ports/repositories/ProductRepository"; - -/** - * 商品应用服务 - 协调领域对象完成业务用例 - * 应用服务是领域层的外观,封装了领域逻辑的调用 - */ -export class ProductApplicationService { - // 依赖注入仓储接口,而不是具体实现 - constructor(private productRepository: ProductRepository) { } - - async getProductList() { - return await this.productRepository.findAll(); - } - - async getProductInfo(id: number) { - if (!id) { - throw new Error(ErrorText.IdNotNull) - } - return await this.productRepository.findById(id); - } -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/di/productModule.ts b/front-end/app-product/src/domains/product/di/productModule.ts deleted file mode 100644 index ee041aa..0000000 --- a/front-end/app-product/src/domains/product/di/productModule.ts +++ /dev/null @@ -1,38 +0,0 @@ -// 从商品联邦模块导入领域层资源 -import {ProductRepository} from "@domains/product/ports/repositories/ProductRepository"; -import {HttpProductRepository} from "@domains/product/infr/api/HttpProductRepository"; -import {ProductApplicationService} from "@domains/product/application/services/ProductApplicationService"; - -/** - * 商品领域的依赖注入配置 - * 将 接口 与 具体实现 绑定 - * - * 优点:是一个依赖工厂,它隐藏了对象创建的细节, - * 让业务代码只需要关心 “做什么”,而不用关心 “依赖从哪里来” - * 所有依赖关系都在di目录下集中配置,一目了然,后续维护时能快速找到所有依赖的创建逻辑。 - * - * 松耦合: - * 业务代码(ProductApplicationService)只依赖抽象接口(ProductRepository), - * 不依赖具体实现。如果未来需要改用其他数据来源(比如 WebSocket), - * 只需修改bindProductRepository的返回值,无需改动业务逻辑。 - * - * 易于测试: - * 测试时可以临时替换为模拟实现 - * productModule.bindProductRepository = () => new MockProductRepository(); - * 这样测试就可以脱离真实 API,使用预设的模拟数据。 - */ -export const product = { - - // 绑定 仓储抽象接口 到 HTTP具体实现 - bindProductRepository: (): ProductRepository => { - // return new MockProductRepository 直接从真实接口数据改为测试模拟数据 - return new HttpProductRepository; - }, - - // 创建应用服务实例 - createProductApplicationService: (): ProductApplicationService => { - return new ProductApplicationService( - product.bindProductRepository() - ); - } -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/domain/entities/Product.ts b/front-end/app-product/src/domains/product/domain/entities/Product.ts deleted file mode 100644 index 236f464..0000000 --- a/front-end/app-product/src/domains/product/domain/entities/Product.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { ErrorText } from "../enums/ErrorEnums" - -/** - * 商品实体 - 包含商品的核心属性和行为 - * 在DDD中,实体具有唯一标识和生命周期 - */ -export class Product { - private readonly _id: number; - private _name: string; - private _description: string; - private _price: number; - private _imageUrl: string; - private _stock: number; - private _categoryId: number; - - constructor( - id: number, - name: string, - description: string, - price: number, - imageUrl: string, - stock: number, - categoryId: number, - ) { - this._id = id; - this._name = name; - this._description = description; - this._price = price; - this._imageUrl = imageUrl; - this._stock = stock; - this._categoryId = categoryId; - } - - // 实体唯一标识 - get id(): number { - return this._id; - } - - public get name(): string { - return this._name; - } - public set name(value: string) { - this._name = value; - } - - public get description(): string { - return this._description; - } - public set description(value: string) { - this._description = value; - } - - - public get price(): number { - return this._price; - } - public set price(value: number) { - if (value < 0) { - throw new Error(ErrorText.PriceNotNegativein); - } - this._price = value; - } - - public get imageUrl(): string { - return this._imageUrl; - } - public set imageUrl(value: string) { - this._imageUrl = value; - } - - public get stock(): number { - return this._stock; - } - public set stock(value: number) { - if (value < 0) { - throw new Error(ErrorText.StockNotNegativein) - } - this._stock = value; - } - - public get categoryId(): number { - return this._categoryId; - } - public set categoryId(value: number) { - this._categoryId = value; - } - - /** - * 检查商品是否有库存 - */ - hasStock() { - return this._stock > 0 - } - - /** - * 减少商品库存 - * @param quantity 减少的数量 - */ - reduceStock(quantity: number): void { - if (quantity <= 0) { - throw new Error(ErrorText.StockIsGreaterThanZero) - } - - if (this._stock < quantity) { - throw new Error(ErrorText.LowStock) - } - - this._stock -= quantity; - } - - /** - * Class类转化为普通json数据 - */ - toDTO(): Record { - return { - id: this._id, - name: this._name, - description: this._description, - price: this._price, - imageUrl: this._imageUrl, - stock: this._stock, - categoryId: this._categoryId - } - } - - /** - * 从json数据创建Product类实例 - * 转化为领域实体 Product - */ - static fromDTO(data: Record): Product { - return new Product( - data.id, - data.name, - data.description, - data.price, - data.imageUrl, - data.stock, - data.categoryId, - ); - } -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/domain/enums/ErrorEnums.ts b/front-end/app-product/src/domains/product/domain/enums/ErrorEnums.ts deleted file mode 100644 index c766cc4..0000000 --- a/front-end/app-product/src/domains/product/domain/enums/ErrorEnums.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum ErrorText { - PriceNotNegativein = "商品 价格 不能为负数", - StockNotNegativein = "商品 库存 不能为负数", - StockIsGreaterThanZero = "减少的库存数量必须大于0", - LowStock = "库存不足", - IdNotNull = "商品Id不能为空" -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/domain/events/README.md b/front-end/app-product/src/domains/product/domain/events/README.md deleted file mode 100644 index abfa02f..0000000 --- a/front-end/app-product/src/domains/product/domain/events/README.md +++ /dev/null @@ -1 +0,0 @@ -# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/domain/type/ProductType.ts b/front-end/app-product/src/domains/product/domain/type/ProductType.ts deleted file mode 100644 index 8669e63..0000000 --- a/front-end/app-product/src/domains/product/domain/type/ProductType.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface ProductType { - id: number, - name: string, - description: string, - price: number, - imageUrl: string, - stock: number, - categoryId: number, -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/domain/value-objects/README.md b/front-end/app-product/src/domains/product/domain/value-objects/README.md deleted file mode 100644 index abfa02f..0000000 --- a/front-end/app-product/src/domains/product/domain/value-objects/README.md +++ /dev/null @@ -1 +0,0 @@ -# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/index.ts b/front-end/app-product/src/domains/product/index.ts deleted file mode 100644 index 6ac8f44..0000000 --- a/front-end/app-product/src/domains/product/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './application/services/ProductApplicationService'; -export * from './domain/entities/Product'; -export * from './domain/enums/ErrorEnums'; -export * from './domain/type/ProductType'; -export * from './infr/api/HttpProductRepository'; -export * from './ports/repositories/ProductRepository'; \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/infr/api/HttpProductRepository.ts b/front-end/app-product/src/domains/product/infr/api/HttpProductRepository.ts deleted file mode 100644 index c7ee5fc..0000000 --- a/front-end/app-product/src/domains/product/infr/api/HttpProductRepository.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Product } from "@domains/product/domain/entities/Product.ts"; -import { ProductType } from "@domains/product/domain/type/ProductType.ts"; -import { ProductRepository } from "@domains/product/ports/repositories/ProductRepository"; -import { http } from "shared/infra"; -import HttpConfig from "../config/HttpConfig"; - - -/** - * HTTP商品仓储实现 - 实现商品数据的HTTP访问 - * 这是基础设施层,负责具体的数据获取实现 - */ -export class HttpProductRepository implements ProductRepository { - - async findAll(): Promise> { - const result: Array = await http.get({ url: HttpConfig.apiUrlList.productsListUrl }); - const items = result.map((item) => Product.fromDTO(item)) - return items; - } - - async findById(id: number): Promise { - const result: ProductType = await http.get({ - url: HttpConfig.apiUrlList.productsInfoUrl, - data: { id } - }); - return Product.fromDTO(result); - } -} \ No newline at end of file diff --git a/front-end/app-product/src/domains/product/infr/config/HttpConfig.ts b/front-end/app-product/src/domains/product/infr/config/HttpConfig.ts deleted file mode 100644 index 3fd9fc2..0000000 --- a/front-end/app-product/src/domains/product/infr/config/HttpConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -interface apiUrlListType { - productsListUrl: string - productsInfoUrl: string -} - -export default class HttpConfig { - public static apiUrlList: apiUrlListType = { - productsListUrl: '/productsList', - productsInfoUrl: '/productsInfo', - } -} diff --git a/front-end/app-product/src/domains/product/ports/repositories/ProductRepository.ts b/front-end/app-product/src/domains/product/ports/repositories/ProductRepository.ts deleted file mode 100644 index ee49c7f..0000000 --- a/front-end/app-product/src/domains/product/ports/repositories/ProductRepository.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Product } from "../../domain/entities/Product" -import { ProductType } from "../../domain/type/ProductType" - -/** - * 商品仓储接口 - 定义商品数据访问的契约 - * 在DDD中,端口定义了领域层与外部世界的交互方式 - */ -export interface ProductRepository { - - /** - * 获取商品列表 - */ - findAll(): Promise>; - - /** - * 根据ID获取商品详情 - * @param id 商品ID - */ - findById(id: number): Promise -} \ No newline at end of file diff --git a/front-end/app-product/src/features/product/assets/README.md b/front-end/app-product/src/features/product/assets/README.md deleted file mode 100644 index abfa02f..0000000 --- a/front-end/app-product/src/features/product/assets/README.md +++ /dev/null @@ -1 +0,0 @@ -# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/front-end/app-product/src/features/product/components/README.md b/front-end/app-product/src/features/product/components/README.md deleted file mode 100644 index abfa02f..0000000 --- a/front-end/app-product/src/features/product/components/README.md +++ /dev/null @@ -1 +0,0 @@ -# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/front-end/app-product/src/features/product/composables/README.md b/front-end/app-product/src/features/product/composables/README.md deleted file mode 100644 index abfa02f..0000000 --- a/front-end/app-product/src/features/product/composables/README.md +++ /dev/null @@ -1 +0,0 @@ -# 临时占位,上传空文件夹,保存文件结构用,后面删除 \ No newline at end of file diff --git a/front-end/app-product/src/features/product/index.ts b/front-end/app-product/src/features/product/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/front-end/app-product/src/features/product/router/index.ts b/front-end/app-product/src/features/product/router/index.ts deleted file mode 100644 index fd1b654..0000000 --- a/front-end/app-product/src/features/product/router/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RouteRecordRaw } from 'vue-router'; - -export const productRouter: Array = [ - { - path: '/Products', - name: 'ProductList', - component: () => import('../views/ProductList/ProductList.jsx'), - meta: { title: '商品列表' } - }, - { - path: '/ProductDetail', - name: 'ProductDetail', - component: () => import('../views/ProductDetail/ProductDetail.jsx'), - meta: { title: '商品详情' } - }, -] \ No newline at end of file diff --git a/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.module.less b/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.module.less deleted file mode 100644 index 739d26d..0000000 --- a/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.module.less +++ /dev/null @@ -1,5 +0,0 @@ -.ProductDetail { - .title { - color: blue; - } -} \ No newline at end of file diff --git a/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.tsx b/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.tsx deleted file mode 100644 index 7fcbd63..0000000 --- a/front-end/app-product/src/features/product/views/ProductDetail/ProductDetail.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import styles from './ProductDetail.module.less' - -import { product } from '@domains/product/di/productModule'; -import { ProductType } from '@domains/product/domain/type/ProductType'; -import { defineComponent, onMounted, reactive } from 'vue' -import { useRoute } from 'vue-router' - -interface storeType { - item: ProductType - id: number -} - -export default defineComponent({ - setup() { - const route = useRoute(); - const productService = product.createProductApplicationService(); - - const state = reactive({ - item: {} as ProductType, - id: 0 - }) - - onMounted(() => { - if (!route.query.id) { - throw new Error("缺少id参数"); - } - - state.id = Number(route.query.id) - console.log("id: ", state.id); - loadProductDetail() - }) - - const loadProductDetail = async () => { - state.item = await productService.getProductInfo(state.id); - console.log("state.item: ", state.item); - } - - return () => ( -
-

商品领域模块 - 商品详情页面

-
-

商品id:{ state.id }

- { state.item.name } -
    - { - Object.entries(state.item).map(([ key, value ]) => -
  • { key }: { value }
  • - ) - } -
-
- ) - } -}) - -// export default defineComponent({ -// setup() { -// return () => ( -//
商品详情
-// ) -// } -// }) \ No newline at end of file diff --git a/front-end/app-product/src/features/product/views/ProductList/ProductList.module.less b/front-end/app-product/src/features/product/views/ProductList/ProductList.module.less deleted file mode 100644 index afc4c2a..0000000 --- a/front-end/app-product/src/features/product/views/ProductList/ProductList.module.less +++ /dev/null @@ -1,5 +0,0 @@ -.ProductList { - .test { - color: var(--theme-color); - } -} diff --git a/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx b/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx deleted file mode 100644 index 1c1c862..0000000 --- a/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import styles from './ProductList.module.less' - -import { defineComponent, onMounted, reactive } from 'vue' -import { useRouter } from 'vue-router'; -import { product } from '@domains/product/di/productModule'; -import { ProductType } from '@domains/product/domain/type/ProductType'; -import { ElCard, ElIcon, Icons } from "shared/element-plus"; - -interface storeType { - list: Array -} - -export default defineComponent({ - setup() { - - const router = useRouter(); - const productService = product.createProductApplicationService(); - - const state = reactive({ - list: [] - }) - - - onMounted(() => { - loadProductList() - }) - - const loadProductList = async () => { - state.list = await productService.getProductList(); - console.log("state.list: ", state.list); - } - - const ElCardHeader = () => { - return ( -
- - - - Card 头部 -
- ) - } - - const ElCardFooter = () => { - return
Footer 底部
- } - - return () => ( -
-

商品领域模块 - 商品列表页面3

-
-

从Deno 后端 加载的数据,点击进详情

- - { { - header: () => ElCardHeader(), - footer: () => ElCardFooter(), - default: () => - state.list.map(item => -

{ - router.push({ name: 'ProductDetail', query: { id: item.id } }); - } }> - { item.id } | { item.name } | ¥{ item.price } -

- ) - } } -
- -
- ) - } -}) - -// export default defineComponent({ -// setup() { -// return () => ( -//
商品列表
-// ) -// } -// }) \ No newline at end of file diff --git a/front-end/app-product/src/main.ts b/front-end/app-product/src/main.ts deleted file mode 100644 index dcbab81..0000000 --- a/front-end/app-product/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -// import './assets/main.css' - -import { createApp } from 'vue' -import { createPinia } from 'pinia' - -import App from './App' - - -createApp(App) - .use(createPinia()) - .mount('#app') - diff --git a/front-end/app-product/src/shared/types/env.d.ts b/front-end/app-product/src/shared/types/env.d.ts deleted file mode 100644 index c7077e0..0000000 --- a/front-end/app-product/src/shared/types/env.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.module.less' { - const classes: { [key: string]: string } - export default classes -} diff --git a/front-end/app-product/src/shared/types/shared.d.ts b/front-end/app-product/src/shared/types/shared.d.ts deleted file mode 100644 index 6a93e75..0000000 --- a/front-end/app-product/src/shared/types/shared.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// - -declare module 'shared/infra' { - export const http: any; -} - -declare module 'shared/utils' { - export const UrlCheckUtils: any; -} - -declare module 'shared/element-plus' { - import * as ElementPlus from 'element-plus'; - import * as ElementPlusIcons from '@element-plus/icons-vue'; - import type { App } from 'vue'; - export * from 'element-plus'; - export import Icons = ElementPlusIcons; - - export function init(app: App): void; - - const defaultExport: typeof ElementPlus & { - Icons: typeof ElementPlusIcons; - init: typeof init; - }; - export default defaultExport; -} - -declare module 'shared/normalize' { - export const init: () => void; -} \ No newline at end of file diff --git a/front-end/app-product/tsconfig.app.json b/front-end/app-product/tsconfig.app.json deleted file mode 100644 index 2102b28..0000000 --- a/front-end/app-product/tsconfig.app.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": [ - "src/**/*.ts", - "src/**/*.d.ts", - "src/**/*", - "src/**/*.tsx", - "src/**/*.vue" - ], - "exclude": [ - "src/**/__tests__/*" - ], - "compilerOptions": { - "verbatimModuleSyntax": false, - "jsx": "preserve", - "jsxImportSource": "vue", - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "paths": { - "@/*": [ - "./src/app/*" - ], - "@shared/*": [ - "./src/shared/*" - ], - "@features/*": [ - "./src/features/*" - ], - "@domains/*": [ - "./src/domains/*" - ] - } - } -} \ No newline at end of file diff --git a/front-end/app-product/tsconfig.json b/front-end/app-product/tsconfig.json deleted file mode 100644 index 66b5e57..0000000 --- a/front-end/app-product/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.node.json" - }, - { - "path": "./tsconfig.app.json" - } - ] -} diff --git a/front-end/app-product/tsconfig.node.json b/front-end/app-product/tsconfig.node.json deleted file mode 100644 index a83dfc9..0000000 --- a/front-end/app-product/tsconfig.node.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "@tsconfig/node22/tsconfig.json", - "include": [ - "vite.config.*", - "vitest.config.*", - "cypress.config.*", - "nightwatch.conf.*", - "playwright.config.*", - "eslint.config.*" - ], - "compilerOptions": { - "noEmit": true, - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - - "module": "ESNext", - "moduleResolution": "Bundler", - "types": ["node"] - } -} diff --git a/front-end/app-product/vite.config.ts b/front-end/app-product/vite.config.ts deleted file mode 100644 index 7e36fc4..0000000 --- a/front-end/app-product/vite.config.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { defineConfig, loadEnv } from 'vite'; -import vue from '@vitejs/plugin-vue'; -import vueJsx from '@vitejs/plugin-vue-jsx' -import vueDevTools from 'vite-plugin-vue-devtools' -import federation from 'vite-federation'; -import { fileURLToPath, URL } from "node:url"; -import { visualizer } from 'rollup-plugin-visualizer'; -import { reg, init } from 'vite-plugin-nacos'; - -export default defineConfig(async ({ mode }) => { - const env = loadEnv(mode, process.cwd()); - const { VITE_MODEL_SHARED_URL } = await init(env) - - // console.log("VITE_MODEL_SHARED_URL: ", VITE_MODEL_SHARED_URL); - - return { - plugins: [ - reg(), - - federation({ - name: env.VITE_SERVER_NAME, - remotes: { - shared: `${VITE_MODEL_SHARED_URL}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js` - }, - exposes: { // 暴露给主应用的资源(按功能分组) - './ProductDomain': './src/domains/product/index.ts', // 领域层(实体、服务、接口等) - './ProductRouter': './src/features/product/router/index.ts' // 商品路由 - }, - shared: { - vue: { import: false, generate: false, requiredVersion: '^3.5.18' }, - 'vue-router': { import: false, generate: false, requiredVersion: '^4.0.6' }, - pinia: { import: false, generate: false, requiredVersion: '^3.0.3' }, - } - }), - vue(), - vueJsx(), - vueDevTools(), - visualizer({ - title: "商品模块依赖分析", - filename: 'dist/stats.html', // 分析文件输出路径 - open: false, // 构建完成后自动打开 - gzipSize: true, // 显示gzip后的大小 - brotliSize: true, // 显示brotli后的大小 - }) - ], - build: { - target: 'esnext', - cssCodeSplit: true, - rollupOptions: { - // 关键配置:告诉 Rollup 不要处理模块联邦的导入 - external: ['shared/normalize'], - output: { - minifyInternalExports: false - } - } - }, - resolve: { - alias: { - '@': fileURLToPath(new URL('./src/', import.meta.url)), - '@shared': fileURLToPath(new URL('./src/shared/', import.meta.url)), - '@features': fileURLToPath(new URL('./src/features/', import.meta.url)), - '@domains': fileURLToPath(new URL('./src/domains/', import.meta.url)) - }, - }, - } -}); \ No newline at end of file