From cea86e48a7378937d5aaf26672c7e46160253c38 Mon Sep 17 00:00:00 2001 From: Agent Date: Fri, 20 Mar 2026 04:59:05 +0000 Subject: [PATCH] Initial commit: deploy code --- docker-compose.yml | 74 ++++++++++++++++++++ frontend/Dockerfile | 24 +++++++ init.sql | 163 ++++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 74 ++++++++++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 docker-compose.yml create mode 100644 frontend/Dockerfile create mode 100644 init.sql create mode 100644 nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..aa9e8bf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,74 @@ +version: '3.8' + +services: + # PostgreSQL数据库 + postgres: + image: postgres:15-alpine + container_name: building-postgres + environment: + POSTGRES_DB: building_materials + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - postgres-data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis缓存 + redis: + image: redis:7-alpine + container_name: building-redis + ports: + - "6379:6379" + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # 后端服务 + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: building-backend + ports: + - "8080:8080" + environment: + SPRING_PROFILES_ACTIVE: production + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/building_materials + SPRING_DATASOURCE_USERNAME: postgres + SPRING_DATASOURCE_PASSWORD: postgres + SPRING_REDIS_HOST: redis + SPRING_REDIS_PORT: 6379 + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + restart: unless-stopped + + # 前端Nginx + frontend: + build: + context: ./frontend + dockerfile: ../deploy/frontend/Dockerfile + container_name: building-frontend + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - backend + restart: unless-stopped + +volumes: + postgres-data: + redis-data: diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..e305388 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,24 @@ +FROM node:18-alpine AS builder + +WORKDIR /app + +# 安装uni-app CLI +RUN npm install -g @vue/cli @dcloudio/uni-cli-common + +COPY frontend/package.json ./ +RUN npm install + +COPY frontend/ ./ +RUN npm run build:h5 + +FROM nginx:alpine + +# 复制构建产物 +COPY --from=builder /app/dist/build/h5 /usr/share/nginx/html + +# 复制nginx配置 +COPY deploy/nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..7360b90 --- /dev/null +++ b/init.sql @@ -0,0 +1,163 @@ +-- 建材销售管家数据库初始化脚本 +-- PostgreSQL + +-- 1. 用户表 +CREATE TABLE "users" ( + "user_id" UUID PRIMARY KEY, + "username" VARCHAR(50) NOT NULL, + "phone" VARCHAR(20) DEFAULT NULL, + "password" VARCHAR(255) DEFAULT NULL, + "wechat_openid" VARCHAR(64) DEFAULT NULL, + "wechat_unionid" VARCHAR(64) DEFAULT NULL, + "alipay_openid" VARCHAR(64) DEFAULT NULL, + "role" VARCHAR(20) DEFAULT 'sales', + "status" INTEGER DEFAULT 1, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE ("phone"), + UNIQUE ("wechat_openid"), + UNIQUE ("alipay_openid") +); + +-- 2. 商品分类表 +CREATE TABLE "categories" ( + "category_id" UUID PRIMARY KEY, + "name" VARCHAR(50) NOT NULL, + "parent_id" VARCHAR(32) DEFAULT '0', + "sort_order" INTEGER DEFAULT 0, + "icon" VARCHAR(255) DEFAULT NULL, + "status" INTEGER DEFAULT 1, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 默认分类数据 +INSERT INTO "categories" ("category_id", "name", "parent_id", "sort_order") VALUES +('CAT001', '五金建材', '0', 1), +('CAT002', '板材', '0', 2), +('CAT003', '木门', '0', 3), +('CAT004', '地板', '0', 4); + +-- 3. 商品表 +CREATE TABLE "products" ( + "product_id" UUID PRIMARY KEY, + "category_id" VARCHAR(32) NOT NULL, + "name" VARCHAR(100) NOT NULL, + "spec" VARCHAR(100) DEFAULT NULL, + "unit" VARCHAR(20) DEFAULT '个', + "price" DECIMAL(10,2) NOT NULL DEFAULT 0, + "cost_price" DECIMAL(10,2) DEFAULT 0, + "image_url" VARCHAR(500) DEFAULT NULL, + "barcode" VARCHAR(50) DEFAULT NULL, + "stock_alert" INTEGER DEFAULT 10, + "description" TEXT DEFAULT NULL, + "status" INTEGER DEFAULT 1, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 4. 客户表 +CREATE TABLE "customers" ( + "customer_id" UUID PRIMARY KEY, + "name" VARCHAR(100) NOT NULL, + "phone" VARCHAR(20) DEFAULT NULL, + "wechat_openid" VARCHAR(64) DEFAULT NULL, + "address" VARCHAR(255) DEFAULT NULL, + "remark" TEXT DEFAULT NULL, + "total_amount" DECIMAL(12,2) DEFAULT 0, + "created_by" VARCHAR(32) DEFAULT NULL, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 5. 订单表 +CREATE TABLE "orders" ( + "order_id" UUID PRIMARY KEY, + "order_no" VARCHAR(32) NOT NULL, + "customer_id" VARCHAR(32) DEFAULT NULL, + "customer_name" VARCHAR(100) DEFAULT NULL, + "customer_phone" VARCHAR(20) DEFAULT NULL, + "customer_wechat" VARCHAR(64) DEFAULT NULL, + "total_amount" DECIMAL(12,2) NOT NULL DEFAULT 0, + "discount_amount" DECIMAL(12,2) DEFAULT 0, + "actual_amount" DECIMAL(12,2) NOT NULL DEFAULT 0, + "discount_rate" DECIMAL(5,2) DEFAULT 100, + "status" INTEGER DEFAULT 1, + "payment_method" VARCHAR(20) DEFAULT NULL, + "remark" TEXT DEFAULT NULL, + "operator_id" VARCHAR(32) NOT NULL, + "operator_name" VARCHAR(50) DEFAULT NULL, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE ("order_no") +); + +-- 6. 订单明细表 +CREATE TABLE "order_items" ( + "item_id" UUID PRIMARY KEY, + "order_id" VARCHAR(32) NOT NULL, + "product_id" VARCHAR(32) NOT NULL, + "product_name" VARCHAR(100) NOT NULL, + "product_spec" VARCHAR(100) DEFAULT NULL, + "unit" VARCHAR(20) DEFAULT NULL, + "price" DECIMAL(10,2) NOT NULL, + "quantity" INTEGER NOT NULL DEFAULT 1, + "subtotal" DECIMAL(12,2) NOT NULL, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 7. 库存表 +CREATE TABLE "stock" ( + "stock_id" UUID PRIMARY KEY, + "product_id" VARCHAR(32) NOT NULL, + "warehouse_id" VARCHAR(32) DEFAULT 'WH001', + "quantity" INTEGER NOT NULL DEFAULT 0, + "locked_quantity" INTEGER DEFAULT 0, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE ("product_id", "warehouse_id") +); + +-- 8. 库存流水表 +CREATE TABLE "stock_flow" ( + "flow_id" UUID PRIMARY KEY, + "product_id" VARCHAR(32) NOT NULL, + "type" INTEGER NOT NULL, + "quantity" INTEGER NOT NULL, + "before_quantity" INTEGER NOT NULL, + "after_quantity" INTEGER NOT NULL, + "related_id" VARCHAR(32) DEFAULT NULL, + "related_type" VARCHAR(20) DEFAULT NULL, + "operator_id" VARCHAR(32) NOT NULL, + "remark" VARCHAR(255) DEFAULT NULL, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 9. 仓库表 +CREATE TABLE "warehouses" ( + "warehouse_id" UUID PRIMARY KEY, + "name" VARCHAR(50) NOT NULL, + "address" VARCHAR(255) DEFAULT NULL, + "remark" VARCHAR(255) DEFAULT NULL, + "status" INTEGER DEFAULT 1, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 默认仓库 +INSERT INTO "warehouses" ("warehouse_id", "name", "address") VALUES +('WH001', '主仓库', '默认仓库'); + +-- 创建索引 +CREATE INDEX idx_products_category ON "products"("category_id"); +CREATE INDEX idx_products_status ON "products"("status"); +CREATE INDEX idx_orders_customer ON "orders"("customer_id"); +CREATE INDEX idx_orders_status ON "orders"("status"); +CREATE INDEX idx_orders_created ON "orders"("created_at"); +CREATE INDEX idx_order_items_order ON "order_items"("order_id"); +CREATE INDEX idx_stock_product ON "stock"("product_id"); +CREATE INDEX idx_stock_flow_product ON "stock_flow"("product_id"); +CREATE INDEX idx_stock_flow_created ON "stock_flow"("created_at"); + +-- 创建测试用户 (密码: admin123) +INSERT INTO "users" ("user_id", "username", "phone", "password", "role") +VALUES ('USER001', '管理员', '13800138000', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 'admin'); diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9f816e3 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,74 @@ +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml; + + # 上游后端服务 + upstream backend { + server backend:8080; + } + + server { + listen 80; + server_name localhost; + + # 前端静态资源 + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } + + # API代理 + location /api/ { + proxy_pass http://backend/; + proxy_set_header Host $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; + + # 超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 30d; + add_header Cache-Control "public, immutable"; + } + + # 健康检查 + location /health { + access_log off; + return 200 "OK"; + } + } +}