--- slug: tech-v2-0-full-architecture --- # 「伴享」技术方案 V2.0 — 完整架构 **版本**: V2.0 **对应PRD**: PRD-V2.0-完整产品 **更新日期**: 2026-02-16 --- ## 1. 架构演进 V2.0标志着产品从MVP走向完整商业化。核心变更:**商家后台Web端**、**完整9大服务板块**、**积分/优惠券/拼团**、**Elasticsearch搜索**。 ### 1.1 完整架构图 ``` ┌───────────────────────────────────────────────────────────┐ │ 客户端层 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │ │Flutter │ │子女端 │ │商家后台 │ │管理后台 │ │ │ │APP │ │小程序 │ │Web │ │Web(内部) │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ └──────┬──────┘ │ └───────┼────────────┼────────────┼───────────────┼─────────┘ │ │ │ │ ┌───────▼────────────▼────────────▼───────────────▼─────────┐ │ Nginx API网关 │ │ /api/v1/* → APP /api/merchant/* → 商家 │ │ /api/child/* → 子女 /api/admin/* → 管理 │ └────────────────────────┬──────────────────────────────────┘ │ ┌────────────────────────▼──────────────────────────────────┐ │ 业务服务层 (Node.js) │ │ 用户│活动│AI管家│医疗│支付│通知│队长│会员│健康│子女│社区 │ │ 商家│积分│优惠券│拼团│家政│配送│候鸟│文教│法律│设备│课程 │ └────────────────────────┬──────────────────────────────────┘ │ ┌────────────────────────▼──────────────────────────────────┐ │ Worker层 │ │ 健康同步│预警检测│队长等级│结算│积分过期│排行榜│会员到期 │ └────────────────────────┬──────────────────────────────────┘ │ ┌────────────────────────▼──────────────────────────────────┐ │ 数据层 │ │ PostgreSQL │ Redis │ OSS │ Elasticsearch │ └───────────────────────────────────────────────────────────┘ ``` ### 1.2 新增技术组件 | 组件 | 用途 | 版本 | |------|------|------| | Elasticsearch | 全文搜索+日志 | 8.x | | React + Ant Design Pro | 商家后台 | React 18 | | Bull | 任务队列 | 4.x | --- ## 2. 商家后台架构 ### 2.1 技术选型 - **前端**: React 18 + Ant Design Pro + ECharts + Vite - **状态管理**: Zustand - **部署**: Nginx静态托管 + CDN ### 2.2 项目结构 ``` banxiang-merchant-web/ ├── src/ │ ├── pages/ │ │ ├── login/ # 登录 │ │ ├── register/ # 注册+资质 │ │ ├── dashboard/ # 数据看板 │ │ ├── services/ # 服务管理 │ │ ├── orders/ # 订单管理 │ │ ├── finance/ # 财务结算 │ │ ├── reviews/ # 评价管理 │ │ └── settings/ # 店铺设置 │ ├── components/ │ ├── services/api.ts │ └── stores/ └── package.json ``` ### 2.3 增量DDL ```sql -- =========================== -- V2.0 增量DDL -- =========================== -- 1. 商家 CREATE TABLE merchants ( id BIGSERIAL PRIMARY KEY, company_name VARCHAR(200) NOT NULL, credit_code VARCHAR(18) UNIQUE, category VARCHAR(50) NOT NULL, store_name VARCHAR(100), store_description TEXT, store_logo_url TEXT, contact_name VARCHAR(50), contact_phone VARCHAR(11), contact_email VARCHAR(100), business_license_url TEXT, industry_license_url TEXT, other_licenses TEXT[], address TEXT, lat DECIMAL(10,7), lng DECIMAL(10,7), service_area TEXT[], business_hours JSONB, annual_fee DECIMAL(10,2) DEFAULT 0, commission_rate DECIMAL(4,2), level VARCHAR(20) DEFAULT 'normal' CHECK (level IN ('normal', 'certified', 'strategic')), rating DECIMAL(2,1) DEFAULT 5.0, total_orders INTEGER DEFAULT 0, total_revenue DECIMAL(12,2) DEFAULT 0, status VARCHAR(20) DEFAULT 'pending', approved_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE merchant_accounts ( id BIGSERIAL PRIMARY KEY, merchant_id BIGINT REFERENCES merchants(id), phone VARCHAR(11) UNIQUE NOT NULL, password_hash VARCHAR(100), role VARCHAR(20) DEFAULT 'owner', last_login_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE merchant_services ( id BIGSERIAL PRIMARY KEY, merchant_id BIGINT REFERENCES merchants(id), title VARCHAR(200) NOT NULL, category VARCHAR(50), subcategory VARCHAR(50), description TEXT, images TEXT[], price DECIMAL(10,2), original_price DECIMAL(10,2), price_unit VARCHAR(20), stock INTEGER, service_duration VARCHAR(50), is_online BOOLEAN DEFAULT TRUE, sales_count INTEGER DEFAULT 0, rating DECIMAL(2,1) DEFAULT 5.0, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE merchant_settlements ( id BIGSERIAL PRIMARY KEY, merchant_id BIGINT REFERENCES merchants(id), period_start DATE, period_end DATE, total_orders INTEGER, gross_amount DECIMAL(12,2), commission_amount DECIMAL(12,2), net_amount DECIMAL(12,2), bank_account_encrypted JSONB, status VARCHAR(20) DEFAULT 'pending', paid_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE merchant_daily_stats ( merchant_id BIGINT REFERENCES merchants(id), stat_date DATE, page_views INTEGER DEFAULT 0, order_count INTEGER DEFAULT 0, revenue DECIMAL(10,2) DEFAULT 0, avg_rating DECIMAL(2,1), PRIMARY KEY(merchant_id, stat_date) ); -- 2. 积分 CREATE TABLE point_accounts ( user_id BIGINT PRIMARY KEY REFERENCES users(id), total_points INTEGER DEFAULT 0, available_points INTEGER DEFAULT 0, used_points INTEGER DEFAULT 0, expired_points INTEGER DEFAULT 0, updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE point_transactions ( id BIGSERIAL PRIMARY KEY, user_id BIGINT REFERENCES users(id), type VARCHAR(20), action VARCHAR(50), points INTEGER, balance INTEGER, reference_id VARCHAR(50), description TEXT, expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE point_products ( id BIGSERIAL PRIMARY KEY, title VARCHAR(100), description TEXT, image_url TEXT, points_required INTEGER, stock INTEGER, category VARCHAR(20), status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW() ); -- 3. 优惠券 CREATE TABLE coupon_templates ( id BIGSERIAL PRIMARY KEY, title VARCHAR(100), type VARCHAR(20), value DECIMAL(10,2), min_amount DECIMAL(10,2), applicable_categories TEXT[], merchant_id BIGINT REFERENCES merchants(id), total_count INTEGER, issued_count INTEGER DEFAULT 0, valid_days INTEGER, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE user_coupons ( id BIGSERIAL PRIMARY KEY, user_id BIGINT REFERENCES users(id), template_id BIGINT REFERENCES coupon_templates(id), status VARCHAR(20) DEFAULT 'available', used_order_no VARCHAR(20), expires_at TIMESTAMP, used_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); -- 4. 拼团 CREATE TABLE group_buy_campaigns ( id BIGSERIAL PRIMARY KEY, service_id BIGINT REFERENCES merchant_services(id), original_price DECIMAL(10,2), group_price DECIMAL(10,2), min_members INTEGER DEFAULT 3, valid_hours INTEGER DEFAULT 24, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE group_buy_orders ( id BIGSERIAL PRIMARY KEY, campaign_id BIGINT REFERENCES group_buy_campaigns(id), initiator_id BIGINT REFERENCES users(id), current_members INTEGER DEFAULT 1, status VARCHAR(20) DEFAULT 'pending', expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE group_buy_members ( group_order_id BIGINT REFERENCES group_buy_orders(id), user_id BIGINT REFERENCES users(id), order_no VARCHAR(20), joined_at TIMESTAMP DEFAULT NOW(), PRIMARY KEY(group_order_id, user_id) ); -- 5. 课程 CREATE TABLE courses ( id BIGSERIAL PRIMARY KEY, title VARCHAR(200), category VARCHAR(50), instructor_name VARCHAR(50), description TEXT, cover_image TEXT, type VARCHAR(20), video_url TEXT, live_room_id VARCHAR(50), scheduled_at TIMESTAMP, duration_minutes INTEGER, price DECIMAL(10,2) DEFAULT 0, enrollment_count INTEGER DEFAULT 0, rating DECIMAL(2,1) DEFAULT 5.0, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE course_enrollments ( course_id BIGINT REFERENCES courses(id), user_id BIGINT REFERENCES users(id), progress DECIMAL(5,2) DEFAULT 0, completed BOOLEAN DEFAULT FALSE, enrolled_at TIMESTAMP DEFAULT NOW(), PRIMARY KEY(course_id, user_id) ); ``` --- ## 3. 商家API端点 | 方法 | 路径 | 描述 | |------|------|------| | POST | /merchant/register | 商家注册 | | POST | /merchant/auth/login | 登录 | | GET | /merchant/dashboard | 看板 | | GET/POST/PUT/DELETE | /merchant/services/* | 服务CRUD | | GET/POST | /merchant/orders/* | 订单管理 | | GET/POST | /merchant/settlements/* | 结算管理 | | GET | /merchant/stats/* | 数据统计 | | GET/POST | /merchant/reviews/* | 评价管理 | | PUT | /merchant/settings | 店铺设置 | | POST | /merchant/coupons | 创建优惠券 | --- ## 4. 结算系统 ```javascript // workers/settlement.js - 每月1日凌晨生成结算单 cron.schedule('0 2 1 * *', async () => { const merchants = await db.query('SELECT * FROM merchants WHERE status = $1', ['active']); const lastMonth = moment().subtract(1, 'month'); for (const m of merchants.rows) { const stats = await db.query(` SELECT COUNT(*) as cnt, COALESCE(SUM(amount), 0) as gross FROM orders o JOIN merchant_services ms ON o.detail->>'serviceId' = ms.id::text WHERE ms.merchant_id = $1 AND o.status = 'completed' AND o.completed_at BETWEEN $2 AND $3 `, [m.id, lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]); const gross = parseFloat(stats.rows[0].gross); const commission = gross * m.commission_rate / 100; await db.query(` INSERT INTO merchant_settlements (merchant_id, period_start, period_end, total_orders, gross_amount, commission_amount, net_amount) VALUES ($1, $2, $3, $4, $5, $6, $7) `, [m.id, lastMonth.startOf('month').format('YYYY-MM-DD'), lastMonth.endOf('month').format('YYYY-MM-DD'), stats.rows[0].cnt, gross, commission, gross - commission]); } }); ``` --- ## 5. Elasticsearch搜索 ```json // 商家/服务索引,使用ik_max_word分词器 // 支持:全文搜索 + 地理位置排序 + 分类筛选 + 评分排序 ``` ```javascript // GET /api/v1/search?q=保洁&category=housekeeping&lat=30.57&lng=104.07 router.get('/search', async (req, res) => { const { q, category, lat, lng, page = 1, size = 20 } = req.query; const result = await esClient.search({ index: 'banxiang_services', body: { query: { bool: { must: [{ multi_match: { query: q, fields: ['title^2', 'description'] } }], filter: category ? [{ term: { category } }] : [] } }, sort: lat ? [{ _geo_distance: { location: { lat, lon: lng }, order: 'asc' } }] : [{ salesCount: 'desc' }], from: (page - 1) * size, size } }); res.json({ success: true, data: result.hits }); }); ``` --- ## 6. 完整部署方案 ### 6.1 服务器配置 | 服务 | 配置 | 月费 | |------|------|------| | 应用服务器 | ECS 4核8G × 2 | ¥800 | | 数据库 | RDS PG 4核8G(主从) | ¥600 | | Redis | 2G | ¥150 | | Elasticsearch | 2核4G | ¥300 | | OSS + CDN | 按量 | ¥200 | | **月合计** | | **¥2050** | ### 6.2 Docker Compose ```yaml version: '3.8' services: app: build: . ports: ["3000:3000"] deploy: { replicas: 2 } depends_on: [db, redis, es] restart: always worker: build: . command: node workers/index.js depends_on: [db, redis] restart: always db: image: postgres:15 volumes: [pgdata:/var/lib/postgresql/data] restart: always redis: image: redis:7-alpine volumes: [redisdata:/data] restart: always es: image: elasticsearch:8.12.0 environment: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" volumes: [esdata:/usr/share/elasticsearch/data] restart: always nginx: image: nginx:alpine ports: ["80:80", "443:443"] volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./merchant-web/dist:/var/www/merchant depends_on: [app] restart: always volumes: pgdata: redisdata: esdata: ``` ### 6.3 Nginx关键配置 ```nginx # APP API + SSE支持 server { listen 443 ssl; server_name api.banxiang.com; location /api/ { proxy_pass http://app:3000; } location /api/v1/ai/chat { proxy_pass http://app:3000; proxy_buffering off; proxy_http_version 1.1; } } # 商家后台SPA server { listen 443 ssl; server_name merchant.banxiang.com; root /var/www/merchant; location / { try_files $uri /index.html; } location /api/merchant/ { proxy_pass http://app:3000; } } ``` --- ## 7. 数据库表总览(全版本49张表) | 版本 | 表数 | 核心表 | |------|------|--------| | V1.0 | 17张 | users, activities, ai_conversations, orders, hospitals等 | | V1.1 | 9张 | captains, memberships, service_providers, products等 | | V1.2 | 11张 | health_records, child_users, posts, service_products等 | | V2.0 | 12张 | merchants, point_accounts, coupon_templates, courses等 | | **合计** | **49张** | | --- ## 8. 向微服务演进路线 ``` V2.0 (当前): 模块化单体 → 代码按服务边界分离,共享数据库 V3.0 (计划): 拆分核心服务 → 用户服务(独立DB) → 活动服务(独立DB) → AI管家服务(独立,GPU资源) → 其余保持单体 V4.0 (远期): 完整微服务 → 所有服务独立 → API Gateway统一入口 → 服务间gRPC通信 → K8s编排 ``` --- ## 9. 安全合规清单 | 项目 | 状态 | 说明 | |------|------|------| | 《个人信息保护法》合规 | ✅ | 最小收集、明确告知、分项授权 | | 数据加密 | ✅ | TLS 1.3 + AES-256 | | 健康数据隔离 | ✅ | 单独加密存储 | | 医疗免责声明 | ✅ | 所有页面标注 | | 金融合规 | ✅ | 仅信息展示,不销售 | | 老年人权益 | ✅ | 无自动续费、大额二次确认 | | RBAC权限 | ✅ | 用户/商家/管理员角色分离 | | 审计日志 | ✅ | 全操作记录 | | 数据备份 | ✅ | 每日自动+跨区域 | | 安全渗透测试 | 📋 | V2.0上线前完成 | --- **文档结束**