commit 88ad71041bcec59715274c9ca568b0ca434408a1 Author: MSuMshk <2039814060@qq.com> Date: Thu Jan 29 01:58:15 2026 +0000 docs: 初始化 Docs 仓库 diff --git a/ABOUT.md b/ABOUT.md new file mode 100644 index 0000000..fad432e --- /dev/null +++ b/ABOUT.md @@ -0,0 +1,9 @@ +# TakeoutSaaS.Docs + +该仓库用于承载 TakeoutSaaS 的全部文档与运维资料(含脚本)。 + +## 内容范围 +- `Document/`:项目设计/开发/运维文档、Swagger、xmind 等。 +- `deploy/`:部署与基础设施配置(PostgreSQL/Redis/Prometheus/dbhub 等)。 +- `scripts/`:本地开发/构建/调试脚本(ps1/sh)。 +- 其它根目录 `.md`:模块设计、接口设计等。 diff --git a/Document/01_项目概述.md b/Document/01_项目概述.md new file mode 100644 index 0000000..3d8920c --- /dev/null +++ b/Document/01_项目概述.md @@ -0,0 +1,188 @@ +# 外卖SaaS系统 - 项目概述 + +## 1. 项目简介 + +### 1.1 项目背景 +外卖SaaS系统是一个面向餐饮企业的多租户外卖管理平台,旨在为中小型餐饮企业提供完整的外卖业务解决方案。系统支持商家入驻、菜品管理、订单处理、配送管理等核心功能。 + +### 1.2 项目目标 +- 提供稳定、高效的外卖业务管理平台 +- 支持多租户架构,实现数据隔离和资源共享 +- 提供完善的商家管理和运营工具 +- 支持灵活的配送模式(自配送、第三方配送) +- 提供实时数据分析和报表功能 + +### 1.3 核心价值 +- **降低成本**:SaaS模式降低企业IT投入成本 +- **快速上线**:开箱即用,快速开展外卖业务 +- **灵活扩展**:支持业务增长和功能定制 +- **数据驱动**:提供数据分析,辅助经营决策 + +## 2. 业务模块 + +### 2.1 租户管理模块 +- 租户注册与认证 +- 租户信息管理 +- 套餐订阅管理 +- 权限与配额管理 + +### 2.2 商家管理模块 +- 商家入驻审核 +- 商家信息管理 +- 门店管理(支持多门店) +- 营业时间设置 +- 配送范围设置 + +### 2.3 菜品管理模块 +- 菜品分类管理 +- 菜品信息管理(名称、价格、图片、描述) +- 菜品规格管理(大份、小份等) +- 菜品库存管理 +- 菜品上下架管理 + +### 2.4 订单管理模块 +- 订单创建与支付 +- 订单状态流转(待支付、待接单、制作中、配送中、已完成、已取消) +- 订单查询与筛选 +- 订单退款处理 +- 订单统计分析 + +### 2.5 配送管理模块 +- 配送员管理 +- 配送任务分配 +- 配送路线规划 +- 配送状态跟踪 +- 配送费用计算 + +### 2.6 用户管理模块 +- 用户注册与登录 +- 用户信息管理 +- 收货地址管理 +- 用户订单历史 +- 用户评价管理 + +### 2.7 支付管理模块 +- 多支付方式支持(微信、支付宝、余额) +- 支付回调处理 +- 退款处理 +- 账单管理 + +### 2.8 营销管理模块 +- 优惠券管理 +- 满减活动 +- 会员积分 +- 推广活动 + +### 2.9 数据分析模块 +- 销售数据统计 +- 订单趋势分析 +- 用户行为分析 +- 商家经营报表 +- 平台运营大盘 + +### 2.10 系统管理模块 +- 系统配置管理 +- 日志管理 +- 权限管理 +- 消息通知管理 + +## 3. 用户角色 + +### 3.1 平台管理员(Web管理端) +- 管理所有租户和商家 +- 系统配置和维护 +- 数据监控和分析 +- 审核商家入驻 +- 平台运营管理 + +### 3.2 租户管理员(Web管理端) +- 管理租户下的所有商家 +- 查看租户数据报表 +- 管理租户套餐和权限 +- 租户配置管理 + +### 3.3 商家管理员(Web管理端) +- 管理门店信息 +- 管理菜品和订单 +- 查看经营数据 +- 管理配送(自配送或第三方配送对接) +- 营销活动管理 + +### 3.4 商家员工(Web管理端) +- 处理订单(接单/出餐/发货) +- 更新菜品状态 +- 订单打印与出餐看板 + +### 3.5 普通用户/消费者(小程序端 + Web用户端) +- 浏览商家和菜品 +- 下单和支付 +- 查看订单状态 +- 评价和反馈 +- 收货地址管理 +- 优惠券领取和使用 + +## 4. 系统特性 + +### 4.1 多租户架构 +- 数据隔离:每个租户数据完全隔离 +- 资源共享:共享基础设施,降低成本 +- 灵活配置:支持租户级别的个性化配置 + +### 4.2 高可用性 +- 服务高可用:支持集群部署 +- 数据高可用:数据库主从复制 +- 故障自动恢复 + +### 4.3 高性能 +- 缓存策略:Redis缓存热点数据 +- 数据库优化:索引优化、查询优化 +- 异步处理:消息队列处理耗时任务 + +### 4.4 安全性 +- 身份认证:JWT Token认证 +- 权限控制:基于角色的访问控制(RBAC) +- 数据加密:敏感数据加密存储 +- 接口防护:限流、防重放攻击 + +### 4.5 可扩展性 +- 微服务架构:支持服务独立扩展 +- 插件化设计:支持功能模块插拔 +- API开放:提供开放API接口 + +## 5. 技术选型 + +- **后端框架**:.NET 10 +- **ORM框架**:Entity Framework Core 10 + Dapper +- **数据库**:PostgreSQL 16+ +- **缓存**:Redis 7.0+ +- **消息队列**:RabbitMQ 3.12+ +- **API文档**:Swagger/OpenAPI +- **日志**:Serilog +- **认证授权**:JWT + OAuth2.0 + +## 6. 项目里程碑 + +### Phase 1:基础功能(1-2个月) +- 租户管理 +- 商家管理 +- 菜品管理 +- 订单管理(基础流程) + +### Phase 2:核心功能(2-3个月) +- 配送管理 +- 支付集成 +- 用户管理 +- 基础营销功能 + +### Phase 3:高级功能(3-4个月) +- 数据分析 +- 高级营销 +- 系统优化 +- 性能调优 + +### Phase 4:完善与上线(1个月) +- 测试与修复 +- 文档完善 +- 部署上线 +- 运维监控 + diff --git a/Document/02_技术架构.md b/Document/02_技术架构.md new file mode 100644 index 0000000..e7d1861 --- /dev/null +++ b/Document/02_技术架构.md @@ -0,0 +1,253 @@ +# 外卖SaaS系统 - 技术架构 + +## 1. 技术栈 + +### 1.1 后端技术栈 +- **.NET 10**:最新的.NET平台,提供高性能和现代化开发体验 +- **ASP.NET Core Web API**:构建RESTful API服务 +- **Entity Framework Core 10**:最新ORM框架,用于复杂查询和实体管理 +- **Dapper 2.1+**:轻量级ORM,用于高性能查询和批量操作 +- **PostgreSQL 16+**:主数据库,支持JSON、全文搜索等高级特性 +- **Redis 7.0+**:缓存和会话存储 +- **RabbitMQ 3.12+**:消息队列,处理异步任务 + +### 1.2 开发工具和框架 +- **AutoMapper**:对象映射 +- **FluentValidation**:数据验证 +- **Serilog**:结构化日志 +- **MediatR**:CQRS和中介者模式实现 +- **Hangfire**:后台任务调度 +- **Polly**:弹性和瞬态故障处理 +- **Swagger/Swashbuckle**:API文档生成 + +### 1.3 认证授权 +- **JWT (JSON Web Token)**:无状态身份认证 +- **IdentityServer/Duende IdentityServer**:OAuth2.0和OpenID Connect +- **ASP.NET Core Identity**:用户身份管理 + +### 1.4 测试框架 +- **xUnit**:单元测试框架 +- **Moq**:Mock框架 +- **FluentAssertions**:断言库 +- **Testcontainers**:集成测试容器化 + +### 1.5 DevOps工具 +- **Docker**:容器化部署 +- **Docker Compose**:本地开发环境 +- **GitHub Actions/GitLab CI**:CI/CD流水线 +- **Nginx**:反向代理和负载均衡 + +## 2. 系统架构 + +### 2.1 整体架构 +``` +┌─────────────────────────────────────────────────────────────┐ +│ 客户端层 │ +│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ +│ │ Web管理端 │ │ Web用户端 │ │ 小程序端(用户) │ │ +│ └──────────┘ └──────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ API网关层 │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Nginx / API Gateway (路由、限流、认证、日志) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 应用服务层 │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │租户服务 │ │商家服务 │ │订单服务 │ │配送服务 │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │用户服务 │ │支付服务 │ │营销服务 │ │通知服务 │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 基础设施层 │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │PostgreSQL │ │ Redis │ │ RabbitMQ │ │ MinIO │ │ +│ │ (主库) │ │ (缓存) │ │ (消息队列)│ │(对象存储) │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.2 分层架构 + +#### 2.2.1 表现层 (Presentation Layer) +- **TakeoutSaaS.AdminApi**:管理后台 Web API 项目(/api/admin/v1) + - Controllers:后台管理API控制器 + - Filters:过滤器(异常处理、日志、验证) + - Middleware:中间件(认证、租户识别、RBAC) + - Models:请求/响应DTO +- **TakeoutSaaS.MiniApi**:小程序/用户端 Web API 项目(/api/mini/v1) + - Controllers:用户端API控制器 + - Filters:过滤器(异常处理、限流、签名校验) + - Middleware:中间件(小程序登录态、租户识别、CORS) + - Models:请求/响应DTO + +#### 2.2.2 应用层 (Application Layer) +- **TakeoutSaaS.Application**:应用逻辑 + - Services:应用服务 + - DTOs:数据传输对象 + - Interfaces:服务接口 + - Validators:FluentValidation验证器 + - Mappings:AutoMapper配置 + - Commands/Queries:CQRS命令和查询 + +#### 2.2.3 领域层 (Domain Layer) +- **TakeoutSaaS.Domain**:领域模型 + - Entities:实体类 + - ValueObjects:值对象 + - Enums:枚举 + - Events:领域事件 + - Interfaces:仓储接口 + - Specifications:规约模式 + +#### 2.2.4 基础设施层 (Infrastructure Layer) +- **TakeoutSaaS.Infrastructure**:基础设施实现 + - Data:数据访问 + - EFCore:EF Core DbContext和配置 + - Dapper:Dapper查询实现 + - Repositories:仓储实现 + - Migrations:数据库迁移 + - Cache:Redis缓存实现 + - MessageQueue:RabbitMQ实现 + - ExternalServices:第三方服务集成 + +#### 2.2.5 共享层 (Shared Layer) +- **TakeoutSaaS.Shared**:共享组件 + - Constants:常量定义 + - Exceptions:自定义异常 + - Extensions:扩展方法 + - Helpers:辅助类 + - Results:统一返回结果 + +## 3. 核心设计模式 + +### 3.1 多租户模式 +- **数据隔离策略**:每个租户独立Schema +- **租户识别**:通过HTTP Header或JWT Token识别租户 +- **动态切换**:运行时动态切换数据库连接 + +### 3.2 CQRS模式 +- **命令(Command)**:处理写操作,修改数据 +- **查询(Query)**:处理读操作,不修改数据 +- **分离优势**:读写分离,优化性能 + +### 3.3 仓储模式 +- **抽象数据访问**:统一数据访问接口 +- **EF Core仓储**:复杂查询和事务处理 +- **Dapper仓储**:高性能查询和批量操作 + +### 3.4 工作单元模式 +- **事务管理**:统一管理数据库事务 +- **批量提交**:减少数据库往返次数 + +### 3.5 领域驱动设计(DDD) +- **聚合根**:定义实体边界 +- **值对象**:不可变对象 +- **领域事件**:解耦业务逻辑 + +## 4. 数据访问策略 + +### 4.1 EF Core使用场景 +- 复杂的实体关系查询 +- 需要变更跟踪的操作 +- 事务性操作 +- 数据库迁移管理 + +### 4.2 Dapper使用场景 +- 高性能查询(大数据量) +- 复杂SQL查询 +- 批量插入/更新 +- 报表统计查询 +- 存储过程调用 + +### 4.3 混合使用策略 +```csharp +// EF Core - 复杂查询和实体管理 +public async Task GetOrderWithDetailsAsync(Guid orderId) +{ + return await _dbContext.Orders + .Include(o => o.OrderItems) + .Include(o => o.Customer) + .FirstOrDefaultAsync(o => o.Id == orderId); +} + +// Dapper - 高性能统计查询 +public async Task GetOrderStatisticsAsync(DateTime startDate, DateTime endDate) +{ + var sql = @" + SELECT + COUNT(*) as TotalOrders, + SUM(total_amount) as TotalAmount, + AVG(total_amount) as AvgAmount + FROM orders + WHERE created_at BETWEEN @StartDate AND @EndDate"; + + return await _connection.QueryFirstOrDefaultAsync(sql, + new { StartDate = startDate, EndDate = endDate }); +} +``` + +## 5. 缓存策略 + +### 5.1 缓存层次 +- **L1缓存**:内存缓存(IMemoryCache)- 进程内缓存 +- **L2缓存**:Redis缓存 - 分布式缓存 + +### 5.2 缓存场景 +- 商家信息缓存(30分钟) +- 菜品信息缓存(15分钟) +- 用户会话缓存(2小时) +- 配置信息缓存(1小时) +- 热点数据缓存(动态过期) + +### 5.3 缓存更新策略 +- **Cache-Aside**:旁路缓存,先查缓存,未命中查数据库 +- **Write-Through**:写入时同步更新缓存 +- **Write-Behind**:异步更新缓存 + +## 6. 消息队列应用 + +### 6.1 异步任务 +- 订单状态变更通知 +- 短信/邮件发送 +- 数据统计计算 +- 日志持久化 + +### 6.2 事件驱动 +- 订单创建事件 +- 支付成功事件 +- 配送状态变更事件 + +## 7. 安全设计 + +### 7.1 认证机制 +- JWT Token认证 +- Refresh Token刷新 +- Token过期管理 + +### 7.2 授权机制 +- 基于角色的访问控制(RBAC) +- 基于策略的授权 +- 资源级权限控制 + +### 7.3 数据安全 +- 敏感数据加密(密码、支付信息) +- HTTPS传输加密 +- SQL注入防护 +- XSS防护 + +### 7.4 接口安全 +- 请求签名验证 +- 接口限流(Rate Limiting) +- 防重放攻击 +- CORS跨域配置 + diff --git a/Document/03_数据库设计.md b/Document/03_数据库设计.md new file mode 100644 index 0000000..93074df --- /dev/null +++ b/Document/03_数据库设计.md @@ -0,0 +1,641 @@ +# 外卖SaaS系统 - 数据库设计 + +## 1. 数据库设计原则 + +### 1.1 命名规范 +- **表名**:小写字母,下划线分隔,复数形式(如:`orders`, `order_items`) +- **字段名**:小写字母,下划线分隔(如:`created_at`, `total_amount`) +- **主键**:统一使用 `id`,类型为 UUID +- **外键**:`表名_id`(如:`order_id`, `merchant_id`) +- **索引**:`idx_表名_字段名`(如:`idx_orders_merchant_id`) + +### 1.2 通用字段 +所有表都包含以下字段: +- `id`:UUID,主键 +- `created_at`:TIMESTAMP,创建时间 +- `updated_at`:TIMESTAMP,更新时间 +- `deleted_at`:TIMESTAMP,软删除时间(可选) +- `tenant_id`:UUID,租户ID(多租户隔离) + +### 1.3 数据类型规范 +- **金额**:DECIMAL(18,2) +- **时间**:TIMESTAMP WITH TIME ZONE +- **布尔**:BOOLEAN +- **枚举**:VARCHAR 或 INTEGER +- **JSON数据**:JSONB + +## 2. 核心表结构 + +### 2.1 租户管理 + +#### tenants(租户表) +```sql +CREATE TABLE tenants ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(100) NOT NULL, + code VARCHAR(50) UNIQUE NOT NULL, + contact_name VARCHAR(50), + contact_phone VARCHAR(20), + contact_email VARCHAR(100), + status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:冻结 3:过期 + subscription_plan VARCHAR(50), -- 订阅套餐 + subscription_start_date TIMESTAMP WITH TIME ZONE, + subscription_end_date TIMESTAMP WITH TIME ZONE, + max_merchants INTEGER DEFAULT 10, -- 最大商家数 + max_orders_per_day INTEGER DEFAULT 1000, -- 每日订单限额 + settings JSONB, -- 租户配置 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_tenants_code ON tenants(code); +CREATE INDEX idx_tenants_status ON tenants(status); +``` + +### 2.2 商家管理 + +#### merchants(商家表) +```sql +CREATE TABLE merchants ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + name VARCHAR(100) NOT NULL, + logo_url VARCHAR(500), + description TEXT, + contact_phone VARCHAR(20), + contact_person VARCHAR(50), + business_license VARCHAR(100), -- 营业执照号 + status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:休息 3:停业 + rating DECIMAL(3,2) DEFAULT 0, -- 评分 + total_sales INTEGER DEFAULT 0, -- 总销量 + settings JSONB, -- 商家配置 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_merchants_tenant_id ON merchants(tenant_id); +CREATE INDEX idx_merchants_status ON merchants(status); +``` + +#### merchant_stores(门店表) +```sql +CREATE TABLE merchant_stores ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + merchant_id UUID NOT NULL REFERENCES merchants(id), + name VARCHAR(100) NOT NULL, + address VARCHAR(500) NOT NULL, + latitude DECIMAL(10,7), -- 纬度 + longitude DECIMAL(10,7), -- 经度 + phone VARCHAR(20), + business_hours JSONB, -- 营业时间 {"monday": {"open": "09:00", "close": "22:00"}} + delivery_range INTEGER DEFAULT 3000, -- 配送范围(米) + min_order_amount DECIMAL(18,2) DEFAULT 0, -- 起送价 + delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费 + status INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_merchant_stores_merchant_id ON merchant_stores(merchant_id); +CREATE INDEX idx_merchant_stores_location ON merchant_stores USING GIST(point(longitude, latitude)); +``` + +### 2.3 菜品管理 + +#### categories(菜品分类表) +```sql +CREATE TABLE categories ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + merchant_id UUID NOT NULL REFERENCES merchants(id), + name VARCHAR(50) NOT NULL, + sort_order INTEGER DEFAULT 0, + status INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_categories_merchant_id ON categories(merchant_id); +``` + +#### dishes(菜品表) +```sql +CREATE TABLE dishes ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + merchant_id UUID NOT NULL REFERENCES merchants(id), + category_id UUID REFERENCES categories(id), + name VARCHAR(100) NOT NULL, + description TEXT, + image_url VARCHAR(500), + price DECIMAL(18,2) NOT NULL, + original_price DECIMAL(18,2), -- 原价 + unit VARCHAR(20) DEFAULT '份', -- 单位 + stock INTEGER, -- 库存(NULL表示不限) + sales_count INTEGER DEFAULT 0, -- 销量 + rating DECIMAL(3,2) DEFAULT 0, -- 评分 + sort_order INTEGER DEFAULT 0, + status INTEGER NOT NULL DEFAULT 1, -- 1:上架 2:下架 + tags JSONB, -- 标签 ["热销", "新品"] + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_dishes_merchant_id ON dishes(merchant_id); +CREATE INDEX idx_dishes_category_id ON dishes(category_id); +CREATE INDEX idx_dishes_status ON dishes(status); +``` + +#### dish_specs(菜品规格表) +```sql +CREATE TABLE dish_specs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + dish_id UUID NOT NULL REFERENCES dishes(id), + name VARCHAR(50) NOT NULL, -- 规格名称(如:大份、小份) + price DECIMAL(18,2) NOT NULL, + stock INTEGER, + status INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_dish_specs_dish_id ON dish_specs(dish_id); +``` + +### 2.4 用户管理 + +#### users(用户表) +```sql +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + phone VARCHAR(20) UNIQUE NOT NULL, + nickname VARCHAR(50), + avatar_url VARCHAR(500), + gender INTEGER, -- 0:未知 1:男 2:女 + birthday DATE, + balance DECIMAL(18,2) DEFAULT 0, -- 余额 + points INTEGER DEFAULT 0, -- 积分 + status INTEGER NOT NULL DEFAULT 1, + last_login_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_users_phone ON users(phone); +``` + +#### user_addresses(用户地址表) +```sql +CREATE TABLE user_addresses ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + contact_name VARCHAR(50) NOT NULL, + contact_phone VARCHAR(20) NOT NULL, + province VARCHAR(50), + city VARCHAR(50), + district VARCHAR(50), + address VARCHAR(500) NOT NULL, + house_number VARCHAR(50), -- 门牌号 + latitude DECIMAL(10,7), + longitude DECIMAL(10,7), + is_default BOOLEAN DEFAULT FALSE, + label VARCHAR(20), -- 标签:家、公司等 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_user_addresses_user_id ON user_addresses(user_id); +``` + +### 2.5 订单管理 + +#### orders(订单表) +```sql +CREATE TABLE orders ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_no VARCHAR(50) UNIQUE NOT NULL, -- 订单号 + merchant_id UUID NOT NULL REFERENCES merchants(id), + store_id UUID NOT NULL REFERENCES merchant_stores(id), + user_id UUID NOT NULL REFERENCES users(id), + + -- 收货信息 + delivery_address VARCHAR(500) NOT NULL, + delivery_latitude DECIMAL(10,7), + delivery_longitude DECIMAL(10,7), + contact_name VARCHAR(50) NOT NULL, + contact_phone VARCHAR(20) NOT NULL, + + -- 金额信息 + dish_amount DECIMAL(18,2) NOT NULL, -- 菜品金额 + delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费 + package_fee DECIMAL(18,2) DEFAULT 0, -- 打包费 + discount_amount DECIMAL(18,2) DEFAULT 0, -- 优惠金额 + total_amount DECIMAL(18,2) NOT NULL, -- 总金额 + actual_amount DECIMAL(18,2) NOT NULL, -- 实付金额 + + -- 订单状态 + status INTEGER NOT NULL DEFAULT 1, -- 1:待支付 2:待接单 3:制作中 4:待配送 5:配送中 6:已完成 7:已取消 + payment_status INTEGER DEFAULT 0, -- 0:未支付 1:已支付 2:已退款 + payment_method VARCHAR(20), -- 支付方式 + payment_time TIMESTAMP WITH TIME ZONE, + + -- 时间信息 + estimated_delivery_time TIMESTAMP WITH TIME ZONE, -- 预计送达时间 + accepted_at TIMESTAMP WITH TIME ZONE, -- 接单时间 + cooking_at TIMESTAMP WITH TIME ZONE, -- 开始制作时间 + delivered_at TIMESTAMP WITH TIME ZONE, -- 送达时间 + completed_at TIMESTAMP WITH TIME ZONE, -- 完成时间 + cancelled_at TIMESTAMP WITH TIME ZONE, -- 取消时间 + + remark TEXT, -- 备注 + cancel_reason TEXT, -- 取消原因 + + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_orders_tenant_id ON orders(tenant_id); +CREATE INDEX idx_orders_order_no ON orders(order_no); +CREATE INDEX idx_orders_merchant_id ON orders(merchant_id); +CREATE INDEX idx_orders_user_id ON orders(user_id); +CREATE INDEX idx_orders_status ON orders(status); +CREATE INDEX idx_orders_created_at ON orders(created_at); +``` + +#### order_items(订单明细表) +```sql +CREATE TABLE order_items ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_id UUID NOT NULL REFERENCES orders(id), + dish_id UUID NOT NULL REFERENCES dishes(id), + dish_name VARCHAR(100) NOT NULL, -- 冗余字段,防止菜品被删除 + dish_image_url VARCHAR(500), + spec_id UUID REFERENCES dish_specs(id), + spec_name VARCHAR(50), + price DECIMAL(18,2) NOT NULL, -- 单价 + quantity INTEGER NOT NULL, -- 数量 + amount DECIMAL(18,2) NOT NULL, -- 小计 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_order_items_order_id ON order_items(order_id); +CREATE INDEX idx_order_items_dish_id ON order_items(dish_id); +``` + +### 2.6 配送管理 + +#### delivery_drivers(配送员表) +```sql +CREATE TABLE delivery_drivers ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + merchant_id UUID REFERENCES merchants(id), -- NULL表示平台配送员 + name VARCHAR(50) NOT NULL, + phone VARCHAR(20) UNIQUE NOT NULL, + id_card VARCHAR(18), -- 身份证号 + vehicle_type VARCHAR(20), -- 车辆类型:电动车、摩托车 + vehicle_number VARCHAR(20), -- 车牌号 + status INTEGER NOT NULL DEFAULT 1, -- 1:空闲 2:配送中 3:休息 4:离线 + current_latitude DECIMAL(10,7), -- 当前位置 + current_longitude DECIMAL(10,7), + rating DECIMAL(3,2) DEFAULT 0, + total_deliveries INTEGER DEFAULT 0, -- 总配送单数 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_delivery_drivers_merchant_id ON delivery_drivers(merchant_id); +CREATE INDEX idx_delivery_drivers_status ON delivery_drivers(status); +``` + +#### delivery_tasks(配送任务表) +```sql +CREATE TABLE delivery_tasks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_id UUID NOT NULL REFERENCES orders(id), + driver_id UUID REFERENCES delivery_drivers(id), + pickup_address VARCHAR(500) NOT NULL, -- 取餐地址 + pickup_latitude DECIMAL(10,7), + pickup_longitude DECIMAL(10,7), + delivery_address VARCHAR(500) NOT NULL, -- 送餐地址 + delivery_latitude DECIMAL(10,7), + delivery_longitude DECIMAL(10,7), + distance INTEGER, -- 配送距离(米) + estimated_time INTEGER, -- 预计时长(分钟) + status INTEGER NOT NULL DEFAULT 1, -- 1:待分配 2:待取餐 3:配送中 4:已送达 5:异常 + assigned_at TIMESTAMP WITH TIME ZONE, -- 分配时间 + picked_at TIMESTAMP WITH TIME ZONE, -- 取餐时间 + delivered_at TIMESTAMP WITH TIME ZONE, -- 送达时间 + delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费 + remark TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_delivery_tasks_order_id ON delivery_tasks(order_id); +CREATE INDEX idx_delivery_tasks_driver_id ON delivery_tasks(driver_id); +CREATE INDEX idx_delivery_tasks_status ON delivery_tasks(status); +``` + +### 2.7 支付管理 + +#### payments(支付记录表) +```sql +CREATE TABLE payments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_id UUID NOT NULL REFERENCES orders(id), + user_id UUID NOT NULL REFERENCES users(id), + payment_no VARCHAR(50) UNIQUE NOT NULL, -- 支付单号 + payment_method VARCHAR(20) NOT NULL, -- 支付方式:wechat、alipay、balance + amount DECIMAL(18,2) NOT NULL, + status INTEGER NOT NULL DEFAULT 0, -- 0:待支付 1:支付中 2:成功 3:失败 4:已退款 + third_party_no VARCHAR(100), -- 第三方支付单号 + paid_at TIMESTAMP WITH TIME ZONE, + callback_data JSONB, -- 回调数据 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_payments_order_id ON payments(order_id); +CREATE INDEX idx_payments_payment_no ON payments(payment_no); +CREATE INDEX idx_payments_user_id ON payments(user_id); +``` + +#### refunds(退款记录表) +```sql +CREATE TABLE refunds ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_id UUID NOT NULL REFERENCES orders(id), + payment_id UUID NOT NULL REFERENCES payments(id), + refund_no VARCHAR(50) UNIQUE NOT NULL, + amount DECIMAL(18,2) NOT NULL, + reason TEXT, + status INTEGER NOT NULL DEFAULT 0, -- 0:待审核 1:退款中 2:成功 3:失败 + third_party_no VARCHAR(100), + refunded_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_refunds_order_id ON refunds(order_id); +CREATE INDEX idx_refunds_payment_id ON refunds(payment_id); +``` + +### 2.8 营销管理 + +#### coupons(优惠券表) +```sql +CREATE TABLE coupons ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + merchant_id UUID REFERENCES merchants(id), -- NULL表示平台券 + name VARCHAR(100) NOT NULL, + type INTEGER NOT NULL, -- 1:满减券 2:折扣券 3:代金券 + discount_type INTEGER NOT NULL, -- 1:固定金额 2:百分比 + discount_value DECIMAL(18,2) NOT NULL, -- 优惠值 + min_order_amount DECIMAL(18,2) DEFAULT 0, -- 最低消费 + max_discount_amount DECIMAL(18,2), -- 最大优惠金额(折扣券用) + total_quantity INTEGER NOT NULL, -- 总数量 + received_quantity INTEGER DEFAULT 0, -- 已领取数量 + used_quantity INTEGER DEFAULT 0, -- 已使用数量 + valid_start_time TIMESTAMP WITH TIME ZONE NOT NULL, + valid_end_time TIMESTAMP WITH TIME ZONE NOT NULL, + status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:停用 + description TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_coupons_merchant_id ON coupons(merchant_id); +CREATE INDEX idx_coupons_status ON coupons(status); +``` + +#### user_coupons(用户优惠券表) +```sql +CREATE TABLE user_coupons ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + coupon_id UUID NOT NULL REFERENCES coupons(id), + status INTEGER NOT NULL DEFAULT 1, -- 1:未使用 2:已使用 3:已过期 + used_order_id UUID REFERENCES orders(id), + received_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + used_at TIMESTAMP WITH TIME ZONE, + expired_at TIMESTAMP WITH TIME ZONE NOT NULL +); + +CREATE INDEX idx_user_coupons_user_id ON user_coupons(user_id); +CREATE INDEX idx_user_coupons_coupon_id ON user_coupons(coupon_id); +CREATE INDEX idx_user_coupons_status ON user_coupons(status); +``` + +### 2.9 评价管理 + +#### reviews(评价表) +```sql +CREATE TABLE reviews ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL REFERENCES tenants(id), + order_id UUID NOT NULL REFERENCES orders(id), + user_id UUID NOT NULL REFERENCES users(id), + merchant_id UUID NOT NULL REFERENCES merchants(id), + rating INTEGER NOT NULL, -- 评分 1-5 + taste_rating INTEGER, -- 口味评分 + package_rating INTEGER, -- 包装评分 + delivery_rating INTEGER, -- 配送评分 + content TEXT, + images JSONB, -- 评价图片 + is_anonymous BOOLEAN DEFAULT FALSE, + reply_content TEXT, -- 商家回复 + reply_at TIMESTAMP WITH TIME ZONE, + status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:隐藏 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_reviews_order_id ON reviews(order_id); +CREATE INDEX idx_reviews_user_id ON reviews(user_id); +CREATE INDEX idx_reviews_merchant_id ON reviews(merchant_id); +``` + +### 2.10 系统管理 + +#### system_users(系统用户表) +```sql +CREATE TABLE system_users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID REFERENCES tenants(id), -- NULL表示平台管理员 + merchant_id UUID REFERENCES merchants(id), -- NULL表示租户管理员 + username VARCHAR(50) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + real_name VARCHAR(50), + phone VARCHAR(20), + email VARCHAR(100), + role_id UUID REFERENCES roles(id), + status INTEGER NOT NULL DEFAULT 1, + last_login_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_system_users_username ON system_users(username); +CREATE INDEX idx_system_users_tenant_id ON system_users(tenant_id); +``` + +#### roles(角色表) +```sql +CREATE TABLE roles ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID REFERENCES tenants(id), + name VARCHAR(50) NOT NULL, + code VARCHAR(50) NOT NULL, + description TEXT, + permissions JSONB, -- 权限列表 + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_roles_tenant_id ON roles(tenant_id); +``` + +#### operation_logs(操作日志表) +```sql +CREATE TABLE operation_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID REFERENCES tenants(id), + user_id UUID, + user_type VARCHAR(20), -- system_user, merchant_user, customer + module VARCHAR(50), -- 模块 + action VARCHAR(50), -- 操作 + description TEXT, + ip_address VARCHAR(50), + user_agent TEXT, + request_data JSONB, + response_data JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_operation_logs_tenant_id ON operation_logs(tenant_id); +CREATE INDEX idx_operation_logs_user_id ON operation_logs(user_id); +CREATE INDEX idx_operation_logs_created_at ON operation_logs(created_at); +``` + +## 3. 数据库索引策略 + +### 3.1 主键索引 +- 所有表使用UUID作为主键,自动创建主键索引 + +### 3.2 外键索引 +- 所有外键字段创建索引,提升关联查询性能 + +### 3.3 业务索引 +- 订单号、支付单号等唯一业务字段创建唯一索引 +- 状态字段创建普通索引 +- 时间字段(created_at)创建索引,支持时间范围查询 + +### 3.4 复合索引 +```sql +-- 订单查询常用复合索引 +CREATE INDEX idx_orders_merchant_status_created ON orders(merchant_id, status, created_at DESC); + +-- 用户订单查询 +CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at DESC); +``` + +### 3.5 地理位置索引 +```sql +-- 使用PostGIS扩展支持地理位置查询 +CREATE EXTENSION IF NOT EXISTS postgis; + +-- 门店位置索引 +CREATE INDEX idx_merchant_stores_location ON merchant_stores + USING GIST(ST_MakePoint(longitude, latitude)); +``` + +## 4. 数据库优化 + +### 4.1 分区策略 +```sql +-- 订单表按月分区 +CREATE TABLE orders_2024_01 PARTITION OF orders + FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'); + +CREATE TABLE orders_2024_02 PARTITION OF orders + FOR VALUES FROM ('2024-02-01') TO ('2024-03-01'); +``` + +### 4.2 物化视图 +```sql +-- 商家统计物化视图 +CREATE MATERIALIZED VIEW merchant_statistics AS +SELECT + m.id as merchant_id, + m.name, + COUNT(DISTINCT o.id) as total_orders, + SUM(o.actual_amount) as total_revenue, + AVG(r.rating) as avg_rating +FROM merchants m +LEFT JOIN orders o ON m.id = o.merchant_id AND o.status = 6 +LEFT JOIN reviews r ON m.id = r.merchant_id +GROUP BY m.id, m.name; + +CREATE UNIQUE INDEX ON merchant_statistics(merchant_id); +``` + +### 4.3 查询优化建议 +- 避免SELECT *,只查询需要的字段 +- 使用EXPLAIN分析查询计划 +- 合理使用JOIN,避免过多关联 +- 大数据量查询使用分页 +- 使用prepared statement防止SQL注入 + +## 5. 数据备份策略 + +### 5.1 备份方案 +- **全量备份**:每天凌晨2点执行 +- **增量备份**:每4小时执行一次 +- **WAL归档**:实时归档,支持PITR + +### 5.2 备份脚本示例 +```bash +#!/bin/bash +# 全量备份 +pg_dump -h localhost -U postgres -d takeout_saas -F c -f /backup/full_$(date +%Y%m%d).dump + +# 保留最近30天的备份 +find /backup -name "full_*.dump" -mtime +30 -delete +``` + +## 6. 数据迁移 + +### 6.1 EF Core Migrations +```bash +# 添加迁移 +dotnet ef migrations add InitialCreate --project TakeoutSaaS.Infrastructure + +# 更新数据库 +dotnet ef database update --project TakeoutSaaS.Infrastructure +``` + +### 6.2 版本控制 +- 所有数据库变更通过Migration管理 +- Migration文件纳入版本控制 +- 生产环境变更需要审核 + diff --git a/Document/04_API接口设计.md b/Document/04_API接口设计.md new file mode 100644 index 0000000..1f3735d --- /dev/null +++ b/Document/04_API接口设计.md @@ -0,0 +1,885 @@ +# 外卖SaaS系统 - API接口设计 + +## 1. API设计规范 + +### 1.1 RESTful规范 +- 使用标准HTTP方法:GET、POST、PUT、DELETE、PATCH +- URL使用名词复数形式,如:`/api/orders` +- 使用HTTP状态码表示请求结果 +- 版本控制:`/api/v1/orders` + +### 1.2 请求规范 +- **Content-Type**:`application/json` +- **认证方式**:Bearer Token (JWT) +- **租户识别**:通过Header `X-Tenant-Id` 或从Token中解析 + +### 1.3 响应规范 +```json +{ + "success": true, + "code": 200, + "message": "操作成功", + "data": {}, + "timestamp": "2024-01-01T12:00:00Z" +} +``` + +### 1.4 错误响应 +```json +{ + "success": false, + "code": 400, + "message": "参数错误", + "errors": [ + { + "field": "phone", + "message": "手机号格式不正确" + } + ], + "timestamp": "2024-01-01T12:00:00Z" +} +``` + +### 1.5 HTTP状态码 +- **200 OK**:请求成功 +- **201 Created**:创建成功 +- **204 No Content**:删除成功 +- **400 Bad Request**:参数错误 +- **401 Unauthorized**:未认证 +- **403 Forbidden**:无权限 +- **404 Not Found**:资源不存在 +- **409 Conflict**:资源冲突 +- **422 Unprocessable Entity**:业务逻辑错误 +- **500 Internal Server Error**:服务器错误 + +### 1.6 分页规范 +```json +// 请求参数 +{ + "pageIndex": 1, + "pageSize": 20, + "sortBy": "createdAt", + "sortOrder": "desc" +} + +// 响应格式 +{ + "success": true, + "data": { + "items": [], + "totalCount": 100, + "pageIndex": 1, + "pageSize": 20, + "totalPages": 5 + } +} +``` + +## 2. 认证授权接口 + +### 2.1 用户登录 +```http +POST /api/v1/auth/login +Content-Type: application/json + +{ + "phone": "13800138000", + "password": "password123", + "loginType": "customer" // customer, merchant, system +} + +Response: +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIs...", + "refreshToken": "eyJhbGciOiJIUzI1NiIs...", + "expiresIn": 7200, + "tokenType": "Bearer", + "userInfo": { + "id": "uuid", + "phone": "13800138000", + "nickname": "张三", + "avatar": "https://..." + } + } +} +``` + +### 2.2 刷新Token +```http +POST /api/v1/auth/refresh +Content-Type: application/json + +{ + "refreshToken": "eyJhbGciOiJIUzI1NiIs..." +} + +Response: +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIs...", + "expiresIn": 7200 + } +} +``` + +### 2.3 用户注册 +```http +POST /api/v1/auth/register +Content-Type: application/json + +{ + "phone": "13800138000", + "password": "password123", + "verificationCode": "123456", + "nickname": "张三" +} +``` + +### 2.4 发送验证码 +```http +POST /api/v1/auth/send-code +Content-Type: application/json + +{ + "phone": "13800138000", + "type": "register" // register, login, reset_password +} +``` + +## 3. 商家管理接口 + +### 3.1 获取商家列表 +```http +GET /api/v1/merchants?pageIndex=1&pageSize=20&keyword=&status=1 +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "items": [ + { + "id": "uuid", + "name": "美味餐厅", + "logo": "https://...", + "rating": 4.5, + "totalSales": 1000, + "status": 1, + "createdAt": "2024-01-01T12:00:00Z" + } + ], + "totalCount": 50, + "pageIndex": 1, + "pageSize": 20 + } +} +``` + +### 3.2 获取商家详情 +```http +GET /api/v1/merchants/{id} +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "id": "uuid", + "name": "美味餐厅", + "logo": "https://...", + "description": "专注美食20年", + "contactPhone": "400-123-4567", + "rating": 4.5, + "totalSales": 1000, + "status": 1, + "stores": [ + { + "id": "uuid", + "name": "总店", + "address": "北京市朝阳区...", + "phone": "010-12345678" + } + ] + } +} +``` + +### 3.3 创建商家 +```http +POST /api/v1/merchants +Authorization: Bearer {token} +Content-Type: application/json + +{ + "name": "美味餐厅", + "logo": "https://...", + "description": "专注美食20年", + "contactPhone": "400-123-4567", + "contactPerson": "张三", + "businessLicense": "91110000..." +} +``` + +### 3.4 更新商家信息 +```http +PUT /api/v1/merchants/{id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "name": "美味餐厅", + "logo": "https://...", + "description": "专注美食20年" +} +``` + +### 3.5 删除商家 +```http +DELETE /api/v1/merchants/{id} +Authorization: Bearer {token} +``` + +## 4. 菜品管理接口 + +### 4.1 获取菜品列表 +```http +GET /api/v1/dishes?merchantId={merchantId}&categoryId={categoryId}&keyword=&status=1&pageIndex=1&pageSize=20 +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "items": [ + { + "id": "uuid", + "name": "宫保鸡丁", + "description": "经典川菜", + "image": "https://...", + "price": 38.00, + "originalPrice": 48.00, + "salesCount": 500, + "rating": 4.8, + "status": 1, + "tags": ["热销", "招牌菜"] + } + ], + "totalCount": 100 + } +} +``` + +### 4.2 获取菜品详情 +```http +GET /api/v1/dishes/{id} +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "id": "uuid", + "name": "宫保鸡丁", + "description": "经典川菜,选用优质鸡肉...", + "image": "https://...", + "price": 38.00, + "originalPrice": 48.00, + "unit": "份", + "stock": 100, + "salesCount": 500, + "rating": 4.8, + "status": 1, + "tags": ["热销", "招牌菜"], + "specs": [ + { + "id": "uuid", + "name": "大份", + "price": 48.00, + "stock": 50 + }, + { + "id": "uuid", + "name": "小份", + "price": 28.00, + "stock": 50 + } + ] + } +} +``` + +### 4.3 创建菜品 +```http +POST /api/v1/dishes +Authorization: Bearer {token} +Content-Type: application/json + +{ + "merchantId": "uuid", + "categoryId": "uuid", + "name": "宫保鸡丁", + "description": "经典川菜", + "image": "https://...", + "price": 38.00, + "originalPrice": 48.00, + "unit": "份", + "stock": 100, + "tags": ["热销", "招牌菜"], + "specs": [ + { + "name": "大份", + "price": 48.00, + "stock": 50 + } + ] +} +``` + +### 4.4 更新菜品 +```http +PUT /api/v1/dishes/{id} +Authorization: Bearer {token} +Content-Type: application/json + +{ + "name": "宫保鸡丁", + "price": 38.00, + "stock": 100, + "status": 1 +} +``` + +### 4.5 批量上下架 +```http +PATCH /api/v1/dishes/batch-status +Authorization: Bearer {token} +Content-Type: application/json + +{ + "dishIds": ["uuid1", "uuid2"], + "status": 1 // 1:上架 2:下架 +} +``` + +## 5. 订单管理接口 + +### 5.1 创建订单 +```http +POST /api/v1/orders +Authorization: Bearer {token} +Content-Type: application/json + +{ + "merchantId": "uuid", + "storeId": "uuid", + "items": [ + { + "dishId": "uuid", + "specId": "uuid", + "quantity": 2, + "price": 38.00 + } + ], + "deliveryAddress": { + "contactName": "张三", + "contactPhone": "13800138000", + "address": "北京市朝阳区...", + "latitude": 39.9042, + "longitude": 116.4074 + }, + "remark": "少辣", + "couponId": "uuid" +} + +Response: +{ + "success": true, + "data": { + "orderId": "uuid", + "orderNo": "202401010001", + "totalAmount": 76.00, + "deliveryFee": 5.00, + "discountAmount": 10.00, + "actualAmount": 71.00, + "paymentInfo": { + "paymentNo": "PAY202401010001", + "qrCode": "https://..." // 支付二维码 + } + } +} +``` + +### 5.2 获取订单列表 +```http +GET /api/v1/orders?status=&startDate=&endDate=&pageIndex=1&pageSize=20 +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "items": [ + { + "id": "uuid", + "orderNo": "202401010001", + "merchantName": "美味餐厅", + "totalAmount": 76.00, + "actualAmount": 71.00, + "status": 2, + "statusText": "待接单", + "createdAt": "2024-01-01T12:00:00Z", + "items": [ + { + "dishName": "宫保鸡丁", + "specName": "大份", + "quantity": 2, + "price": 38.00 + } + ] + } + ], + "totalCount": 50 + } +} +``` + +### 5.3 获取订单详情 +```http +GET /api/v1/orders/{id} +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "id": "uuid", + "orderNo": "202401010001", + "merchant": { + "id": "uuid", + "name": "美味餐厅", + "phone": "400-123-4567" + }, + "items": [ + { + "dishName": "宫保鸡丁", + "dishImage": "https://...", + "specName": "大份", + "quantity": 2, + "price": 38.00, + "amount": 76.00 + } + ], + "deliveryAddress": { + "contactName": "张三", + "contactPhone": "13800138000", + "address": "北京市朝阳区..." + }, + "dishAmount": 76.00, + "deliveryFee": 5.00, + "packageFee": 2.00, + "discountAmount": 10.00, + "totalAmount": 83.00, + "actualAmount": 73.00, + "status": 2, + "statusText": "待接单", + "paymentStatus": 1, + "paymentMethod": "wechat", + "estimatedDeliveryTime": "2024-01-01T13:00:00Z", + "createdAt": "2024-01-01T12:00:00Z", + "paidAt": "2024-01-01T12:05:00Z", + "remark": "少辣", + "timeline": [ + { + "status": "created", + "statusText": "订单创建", + "time": "2024-01-01T12:00:00Z" + }, + { + "status": "paid", + "statusText": "支付成功", + "time": "2024-01-01T12:05:00Z" + } + ] + } +} +``` + +### 5.4 商家接单 +```http +POST /api/v1/orders/{id}/accept +Authorization: Bearer {token} +Content-Type: application/json + +{ + "estimatedTime": 30 // 预计制作时长(分钟) +} +``` + +### 5.5 开始制作 +```http +POST /api/v1/orders/{id}/cooking +Authorization: Bearer {token} +``` + +### 5.6 订单完成 +```http +POST /api/v1/orders/{id}/complete +Authorization: Bearer {token} +``` + +### 5.7 取消订单 +```http +POST /api/v1/orders/{id}/cancel +Authorization: Bearer {token} +Content-Type: application/json + +{ + "reason": "用户取消", + "cancelBy": "customer" // customer, merchant, system +} +``` + +## 6. 支付接口 + +### 6.1 创建支付 +```http +POST /api/v1/payments +Authorization: Bearer {token} +Content-Type: application/json + +{ + "orderId": "uuid", + "paymentMethod": "wechat", // wechat, alipay, balance + "amount": 71.00 +} + +Response: +{ + "success": true, + "data": { + "paymentNo": "PAY202401010001", + "qrCode": "https://...", // 支付二维码 + "deepLink": "weixin://..." // 唤起支付的深度链接 + } +} +``` + +### 6.2 查询支付状态 +```http +GET /api/v1/payments/{paymentNo} +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "paymentNo": "PAY202401010001", + "status": 2, // 0:待支付 1:支付中 2:成功 3:失败 + "amount": 71.00, + "paidAt": "2024-01-01T12:05:00Z" + } +} +``` + +### 6.3 支付回调(第三方调用) +```http +POST /api/v1/payments/callback/wechat +Content-Type: application/json + +{ + "out_trade_no": "PAY202401010001", + "transaction_id": "4200001234567890", + "total_fee": 7100, + "result_code": "SUCCESS" +} +``` + +### 6.4 申请退款 +```http +POST /api/v1/refunds +Authorization: Bearer {token} +Content-Type: application/json + +{ + "orderId": "uuid", + "amount": 71.00, + "reason": "不想要了" +} +``` + +## 7. 配送管理接口 + +### 7.1 获取配送任务列表 +```http +GET /api/v1/delivery-tasks?status=&driverId=&pageIndex=1&pageSize=20 +Authorization: Bearer {token} +``` + +### 7.2 分配配送员 +```http +POST /api/v1/delivery-tasks/{id}/assign +Authorization: Bearer {token} +Content-Type: application/json + +{ + "driverId": "uuid" +} +``` + +### 7.3 配送员接单 +```http +POST /api/v1/delivery-tasks/{id}/accept +Authorization: Bearer {token} +``` + +### 7.4 确认取餐 +```http +POST /api/v1/delivery-tasks/{id}/pickup +Authorization: Bearer {token} +``` + +### 7.5 确认送达 +```http +POST /api/v1/delivery-tasks/{id}/deliver +Authorization: Bearer {token} +Content-Type: application/json + +{ + "deliveryCode": "123456" // 取餐码 +} +``` + +### 7.6 更新配送员位置 +```http +POST /api/v1/delivery-drivers/location +Authorization: Bearer {token} +Content-Type: application/json + +{ + "latitude": 39.9042, + "longitude": 116.4074 +} +``` + +## 8. 营销管理接口 + +### 8.1 获取优惠券列表 +```http +GET /api/v1/coupons?merchantId=&status=1&pageIndex=1&pageSize=20 +Authorization: Bearer {token} +``` + +### 8.2 领取优惠券 +```http +POST /api/v1/coupons/{id}/receive +Authorization: Bearer {token} +``` + +### 8.3 获取用户优惠券 +```http +GET /api/v1/user-coupons?status=1&pageIndex=1&pageSize=20 +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "items": [ + { + "id": "uuid", + "couponName": "满50减10", + "discountValue": 10.00, + "minOrderAmount": 50.00, + "status": 1, + "expiredAt": "2024-12-31T23:59:59Z" + } + ] + } +} +``` + +### 8.4 获取可用优惠券 +```http +GET /api/v1/user-coupons/available?merchantId={merchantId}&amount={amount} +Authorization: Bearer {token} +``` + +## 9. 评价管理接口 + +### 9.1 创建评价 +```http +POST /api/v1/reviews +Authorization: Bearer {token} +Content-Type: application/json + +{ + "orderId": "uuid", + "rating": 5, + "tasteRating": 5, + "packageRating": 5, + "deliveryRating": 5, + "content": "非常好吃", + "images": ["https://...", "https://..."], + "isAnonymous": false +} +``` + +### 9.2 获取商家评价列表 +```http +GET /api/v1/reviews?merchantId={merchantId}&rating=&pageIndex=1&pageSize=20 + +Response: +{ + "success": true, + "data": { + "items": [ + { + "id": "uuid", + "userName": "张三", + "userAvatar": "https://...", + "rating": 5, + "content": "非常好吃", + "images": ["https://..."], + "createdAt": "2024-01-01T12:00:00Z", + "replyContent": "感谢支持", + "replyAt": "2024-01-01T13:00:00Z" + } + ], + "totalCount": 100, + "statistics": { + "avgRating": 4.8, + "totalReviews": 100, + "rating5Count": 80, + "rating4Count": 15, + "rating3Count": 3, + "rating2Count": 1, + "rating1Count": 1 + } + } +} +``` + +### 9.3 商家回复评价 +```http +POST /api/v1/reviews/{id}/reply +Authorization: Bearer {token} +Content-Type: application/json + +{ + "replyContent": "感谢您的支持" +} +``` + +## 10. 数据统计接口 + +### 10.1 商家数据概览 +```http +GET /api/v1/statistics/merchant/overview?merchantId={merchantId}&startDate=&endDate= +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "totalOrders": 1000, + "totalRevenue": 50000.00, + "avgOrderAmount": 50.00, + "completionRate": 0.95, + "todayOrders": 50, + "todayRevenue": 2500.00, + "orderTrend": [ + { + "date": "2024-01-01", + "orders": 50, + "revenue": 2500.00 + } + ], + "topDishes": [ + { + "dishId": "uuid", + "dishName": "宫保鸡丁", + "salesCount": 200, + "revenue": 7600.00 + } + ] + } +} +``` + +### 10.2 平台数据大盘 +```http +GET /api/v1/statistics/platform/dashboard?startDate=&endDate= +Authorization: Bearer {token} + +Response: +{ + "success": true, + "data": { + "totalMerchants": 100, + "totalUsers": 10000, + "totalOrders": 50000, + "totalRevenue": 2500000.00, + "activeMerchants": 80, + "activeUsers": 5000, + "todayOrders": 500, + "todayRevenue": 25000.00 + } +} +``` + +## 11. 文件上传接口 + +### 11.1 上传图片 +```http +POST /api/v1/files/upload +Authorization: Bearer {token} +Content-Type: multipart/form-data + +file: +type: dish_image // dish_image, merchant_logo, user_avatar, review_image + +Response: +{ + "success": true, + "data": { + "url": "https://cdn.example.com/images/xxx.jpg", + "fileName": "xxx.jpg", + "fileSize": 102400 + } +} +``` + +## 12. WebSocket实时通知 + +### 12.1 连接WebSocket +```javascript +// 连接地址 +ws://api.example.com/ws?token={jwt_token} + +// 订阅主题 +{ + "action": "subscribe", + "topics": ["order.new", "order.status", "delivery.location"] +} + +// 接收消息 +{ + "topic": "order.new", + "data": { + "orderId": "uuid", + "orderNo": "202401010001", + "merchantId": "uuid" + }, + "timestamp": "2024-01-01T12:00:00Z" +} +``` + +### 12.2 消息主题 +- `order.new`:新订单通知 +- `order.status`:订单状态变更 +- `delivery.location`:配送员位置更新 +- `payment.success`:支付成功通知 + diff --git a/Document/05_部署运维.md b/Document/05_部署运维.md new file mode 100644 index 0000000..f92e303 --- /dev/null +++ b/Document/05_部署运维.md @@ -0,0 +1,1058 @@ +# 外卖SaaS系统 - 部署运维 + +## 1. 环境要求 + +### 1.1 开发环境 +- **.NET SDK**:10.0 或更高版本 +- **IDE**:Visual Studio 2022 / JetBrains Rider / VS Code +- **数据库**:PostgreSQL 16+ +- **缓存**:Redis 7.0+ +- **消息队列**:RabbitMQ 3.12+ +- **Git**:版本控制 +- **Docker Desktop**:容器化开发(可选) + +### 1.2 生产环境 +- **操作系统**:Linux (Ubuntu 22.04 LTS / CentOS 8+) +- **运行时**:.NET Runtime 10.0 +- **Web服务器**:Nginx 1.24+ +- **数据库**:PostgreSQL 16+ (主从复制) +- **缓存**:Redis 7.0+ (哨兵模式) +- **消息队列**:RabbitMQ 3.12+ (集群模式) +- **对象存储**:MinIO / 阿里云OSS / 腾讯云COS +- **监控**:Prometheus + Grafana +- **日志**:ELK Stack (Elasticsearch + Logstash + Kibana) + +### 1.3 硬件要求(生产环境) +- **应用服务器**:4核8GB内存(最低),推荐8核16GB +- **数据库服务器**:8核16GB内存,SSD存储 +- **Redis服务器**:4核8GB内存 +- **负载均衡器**:2核4GB内存 + +## 2. 本地开发环境搭建 + +### 2.1 安装.NET SDK +```bash +# Windows +# 从官网下载安装:https://dotnet.microsoft.com/download + +# Linux (Ubuntu) +wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb +sudo dpkg -i packages-microsoft-prod.deb +sudo apt-get update +sudo apt-get install -y dotnet-sdk-10.0 + +# 验证安装 +dotnet --version +``` + +### 2.2 安装PostgreSQL +```bash +# Ubuntu +sudo apt-get update +sudo apt-get install -y postgresql-16 postgresql-contrib-16 + +# 启动服务 +sudo systemctl start postgresql +sudo systemctl enable postgresql + +# 创建数据库 +sudo -u postgres psql +CREATE DATABASE takeout_saas; +CREATE USER takeout_user WITH PASSWORD 'your_password'; +GRANT ALL PRIVILEGES ON DATABASE takeout_saas TO takeout_user; +\q +``` + +### 2.3 安装Redis +```bash +# Ubuntu +sudo apt-get install -y redis-server + +# 启动服务 +sudo systemctl start redis-server +sudo systemctl enable redis-server + +# 测试连接 +redis-cli ping +``` + +### 2.4 安装RabbitMQ +```bash +# Ubuntu +sudo apt-get install -y rabbitmq-server + +# 启动服务 +sudo systemctl start rabbitmq-server +sudo systemctl enable rabbitmq-server + +# 启用管理插件 +sudo rabbitmq-plugins enable rabbitmq_management + +# 创建用户 +sudo rabbitmqctl add_user admin password +sudo rabbitmqctl set_user_tags admin administrator +sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" + +# 访问管理界面:http://localhost:15672 +``` + +### 2.5 使用Docker Compose(推荐) +```yaml +# docker-compose.yml +version: '3.8' + +services: + postgres: + image: postgres:16 + container_name: takeout_postgres + environment: + POSTGRES_DB: takeout_saas + POSTGRES_USER: takeout_user + POSTGRES_PASSWORD: your_password + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + container_name: takeout_redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + + rabbitmq: + image: rabbitmq:3.12-management + container_name: takeout_rabbitmq + environment: + RABBITMQ_DEFAULT_USER: admin + RABBITMQ_DEFAULT_PASS: password + ports: + - "5672:5672" + - "15672:15672" + volumes: + - rabbitmq_data:/var/lib/rabbitmq + + minio: + image: minio/minio:latest + container_name: takeout_minio + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: password123 + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio_data:/data + +volumes: + postgres_data: + redis_data: + rabbitmq_data: + minio_data: +``` + +```bash +# 启动所有服务 +docker-compose up -d + +# 查看服务状态 +docker-compose ps + +# 停止服务 +docker-compose down +``` + +### 2.6 配置项目 +```bash +# 克隆项目 +git clone https://github.com/your-org/takeout-saas.git +cd takeout-saas + +# 还原依赖 +dotnet restore + +# 配置appsettings.Development.json +{ + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=takeout_saas;Username=takeout_user;Password=your_password" + }, + "Redis": { + "Configuration": "localhost:6379" + }, + "RabbitMQ": { + "Host": "localhost", + "Port": 5672, + "Username": "admin", + "Password": "password" + } +} + +# 执行数据库迁移 +cd src/TakeoutSaaS.Api +dotnet ef database update + +# 运行项目 +dotnet run +``` + +### 2.7 EF Core 迁移基线 + +现已内置 `dotnet-ef` 本地工具与设计时 DbContext 工厂,可直接在命令行生成/更新数据库。运行前可通过环境变量 `TAKEOUTSAAS_APP_CONNECTION`、`TAKEOUTSAAS_IDENTITY_CONNECTION` 覆盖默认连接串(默认指向本地 PostgreSQL)。 + +```powershell +# 业务主库(TakeoutAppDbContext,含租户/商户/门店/商品/订单等) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.App.Persistence.TakeoutAppDbContext + +# 身份库(IdentityDbContext) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.Identity.Persistence.IdentityDbContext + +# 业务/字典库(DictionaryDbContext,归属 AppDatabase) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext +``` + +> Hangfire 使用 Scheduler.ConnectionString 指向的数据库,首次启动服务会自动建表;只需提前创建空数据库并授予账号权限。 + +## 3. Docker部署 + +### 3.1 创建Dockerfile +```dockerfile +# Dockerfile +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src +COPY ["src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj", "src/TakeoutSaaS.Api/"] +COPY ["src/TakeoutSaaS.Application/TakeoutSaaS.Application.csproj", "src/TakeoutSaaS.Application/"] +COPY ["src/TakeoutSaaS.Domain/TakeoutSaaS.Domain.csproj", "src/TakeoutSaaS.Domain/"] +COPY ["src/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj", "src/TakeoutSaaS.Infrastructure/"] +COPY ["src/TakeoutSaaS.Shared/TakeoutSaaS.Shared.csproj", "src/TakeoutSaaS.Shared/"] +RUN dotnet restore "src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj" +COPY . . +WORKDIR "/src/src/TakeoutSaaS.Api" +RUN dotnet build "TakeoutSaaS.Api.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "TakeoutSaaS.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TakeoutSaaS.Api.dll"] +``` + +### 3.2 构建镜像 +```bash +# 构建镜像 +docker build -t takeout-saas-api:latest . + +# 查看镜像 +docker images | grep takeout-saas + +# 运行容器 +docker run -d \ + --name takeout-api \ + -p 8080:80 \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e ConnectionStrings__DefaultConnection="Host=postgres;Port=5432;Database=takeout_saas;Username=takeout_user;Password=your_password" \ + takeout-saas-api:latest +``` + +### 3.3 生产环境Docker Compose +```yaml +# docker-compose.prod.yml +version: '3.8' + +services: + api: + image: takeout-saas-api:latest + container_name: takeout_api + restart: always + environment: + ASPNETCORE_ENVIRONMENT: Production + ConnectionStrings__DefaultConnection: "Host=postgres;Port=5432;Database=takeout_saas;Username=takeout_user;Password=${DB_PASSWORD}" + Redis__Configuration: "redis:6379" + RabbitMQ__Host: "rabbitmq" + ports: + - "8080:80" + depends_on: + - postgres + - redis + - rabbitmq + networks: + - takeout_network + + nginx: + image: nginx:latest + container_name: takeout_nginx + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/ssl:/etc/nginx/ssl + depends_on: + - api + networks: + - takeout_network + +networks: + takeout_network: + driver: bridge +``` + +## 4. Nginx配置 + +### 4.1 基础配置 +```nginx +# /etc/nginx/nginx.conf +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_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml text/javascript + application/json application/javascript application/xml+rss + application/rss+xml font/truetype font/opentype + application/vnd.ms-fontobject image/svg+xml; + + # 限流配置 + limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s; + limit_conn_zone $binary_remote_addr zone=conn_limit:10m; + + include /etc/nginx/conf.d/*.conf; +} +``` + +### 4.2 API服务配置 +```nginx +# /etc/nginx/conf.d/api.conf +upstream api_backend { + least_conn; + server api1:80 weight=1 max_fails=3 fail_timeout=30s; + server api2:80 weight=1 max_fails=3 fail_timeout=30s; + keepalive 32; +} + +server { + listen 80; + server_name api.example.com; + + # 重定向到HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name api.example.com; + + # SSL证书配置 + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # 安全头 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # 客户端请求体大小限制 + client_max_body_size 10M; + + # API接口 + location /api/ { + # 限流 + limit_req zone=api_limit burst=20 nodelay; + limit_conn conn_limit 10; + + proxy_pass http://api_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + 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; + + # 缓冲设置 + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + proxy_busy_buffers_size 8k; + } + + # WebSocket + location /ws { + proxy_pass http://api_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # WebSocket超时 + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + } + + # 健康检查 + location /health { + proxy_pass http://api_backend; + access_log off; + } + + # 静态文件缓存 + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + proxy_pass http://api_backend; + expires 30d; + add_header Cache-Control "public, immutable"; + } +} +``` + +## 5. 数据库部署 + +### 5.1 PostgreSQL主从复制 +```bash +# 主库配置 (postgresql.conf) +listen_addresses = '*' +wal_level = replica +max_wal_senders = 10 +wal_keep_size = 64MB +hot_standby = on + +# 主库配置 (pg_hba.conf) +host replication replicator 192.168.1.0/24 md5 + +# 创建复制用户 +CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'repl_password'; + +# 从库配置 +# 1. 停止从库 +sudo systemctl stop postgresql + +# 2. 清空从库数据目录 +rm -rf /var/lib/postgresql/16/main/* + +# 3. 从主库复制数据 +pg_basebackup -h master_ip -D /var/lib/postgresql/16/main -U replicator -P -v -R -X stream -C -S replica1 + +# 4. 启动从库 +sudo systemctl start postgresql + +# 5. 验证复制状态 +# 主库执行 +SELECT * FROM pg_stat_replication; +``` + +### 5.2 数据库备份脚本 +```bash +#!/bin/bash +# backup_db.sh + +BACKUP_DIR="/backup/postgres" +DATE=$(date +%Y%m%d_%H%M%S) +DB_NAME="takeout_saas" +DB_USER="takeout_user" +RETENTION_DAYS=30 + +# 创建备份目录 +mkdir -p $BACKUP_DIR + +# 全量备份 +pg_dump -h localhost -U $DB_USER -d $DB_NAME -F c -f $BACKUP_DIR/full_$DATE.dump + +# 压缩备份 +gzip $BACKUP_DIR/full_$DATE.dump + +# 删除过期备份 +find $BACKUP_DIR -name "full_*.dump.gz" -mtime +$RETENTION_DAYS -delete + +# 上传到对象存储(可选) +# aws s3 cp $BACKUP_DIR/full_$DATE.dump.gz s3://your-bucket/backups/ + +echo "Backup completed: full_$DATE.dump.gz" +``` + +### 5.3 定时备份(Crontab) +```bash +# 编辑crontab +crontab -e + +# 每天凌晨2点执行备份 +0 2 * * * /path/to/backup_db.sh >> /var/log/backup.log 2>&1 +``` + +## TODO:基础设施部署脚本 + +- [ ] PostgreSQL 主从:整理主库/从库初始化脚本、basebackup 步骤与故障切换手册。 +- [ ] Redis 哨兵/集群:补充 redis.conf/sentinel.conf 模板以及一主两从搭建命令。 +- [ ] RabbitMQ:编写单节点到镜像队列的安装脚本,记录 VHost、用户、权限、监控等操作。 +- [ ] 腾讯云 COS:整理桶创建、ACL、CDN 绑定与密钥轮换流程,并提供 coscmd/SDK 示例。 +- [ ] Hangfire 存储:确认 PostgreSQL Schema 初始化脚本,补充定期备份、清理、监控的 SOP。 + +## 6. Redis部署 + +### 6.1 Redis哨兵模式 +```bash +# redis.conf (主节点) +bind 0.0.0.0 +port 6379 +requirepass your_password +masterauth your_password + +# sentinel.conf +port 26379 +sentinel monitor mymaster 192.168.1.100 6379 2 +sentinel auth-pass mymaster your_password +sentinel down-after-milliseconds mymaster 5000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 10000 +``` + +### 6.2 Redis持久化配置 +```bash +# redis.conf +# RDB持久化 +save 900 1 +save 300 10 +save 60 10000 +rdbcompression yes +rdbchecksum yes +dbfilename dump.rdb + +# AOF持久化 +appendonly yes +appendfilename "appendonly.aof" +appendfsync everysec +no-appendfsync-on-rewrite no +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb +``` + +## 7. CI/CD配置 + +### 7.1 GitHub Actions +```yaml +# .github/workflows/deploy.yml +name: Deploy to Production + +on: + push: + branches: [ main ] + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '10.0.x' + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + - name: Publish + run: dotnet publish src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj -c Release -o ./publish + + - name: Build Docker image + run: | + docker build -t takeout-saas-api:${{ github.sha }} . + docker tag takeout-saas-api:${{ github.sha }} takeout-saas-api:latest + + - name: Push to Registry + run: | + echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + docker push takeout-saas-api:${{ github.sha }} + docker push takeout-saas-api:latest + + - name: Deploy to Server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + cd /opt/takeout-saas + docker-compose pull + docker-compose up -d + docker system prune -f +``` + +### 7.2 GitLab CI +```yaml +# .gitlab-ci.yml +stages: + - build + - test + - deploy + +variables: + DOCKER_IMAGE: registry.example.com/takeout-saas-api + +build: + stage: build + image: mcr.microsoft.com/dotnet/sdk:10.0 + script: + - dotnet restore + - dotnet build --configuration Release + artifacts: + paths: + - src/*/bin/Release/ + +test: + stage: test + image: mcr.microsoft.com/dotnet/sdk:10.0 + script: + - dotnet test --configuration Release + +deploy: + stage: deploy + image: docker:latest + services: + - docker:dind + script: + - docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA . + - docker tag $DOCKER_IMAGE:$CI_COMMIT_SHA $DOCKER_IMAGE:latest + - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA + - docker push $DOCKER_IMAGE:latest + only: + - main +``` + +## 8. 监控告警 + +### 8.1 Prometheus配置 +```yaml +# prometheus.yml +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'takeout-api' + static_configs: + - targets: ['api:80'] + metrics_path: '/metrics' + + - job_name: 'postgres' + static_configs: + - targets: ['postgres-exporter:9187'] + + - job_name: 'redis' + static_configs: + - targets: ['redis-exporter:9121'] + + - job_name: 'node' + static_configs: + - targets: ['node-exporter:9100'] +``` + +### 8.2 应用监控指标(OpenTelemetry + Prometheus Exporter) +```csharp +// Program.cs - 指标与探针 +builder.Services.AddHealthChecks(); +builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddPrometheusExporter(); // /metrics + }); + +var app = builder.Build(); +app.MapHealthChecks("/healthz"); // 存活/就绪探针 +app.MapPrometheusScrapingEndpoint(); // 默认 /metrics +``` + +自定义业务指标(使用 `System.Diagnostics.Metrics`,由 Prometheus Exporter 暴露): +```csharp +internal static class BusinessMetrics +{ + private static readonly Meter Meter = new("TakeoutSaaS.App", "1.0.0"); + public static readonly Counter OrdersCreated = Meter.CreateCounter("orders_created_total", "个", "订单创建计数"); + public static readonly Histogram OrderProcessingSeconds = Meter.CreateHistogram("order_processing_duration_seconds", "s", "订单处理耗时"); +} +``` + +Prometheus 抓取示例:见 `deploy/prometheus/prometheus.yml`,默认拉取 `/metrics`,告警规则见 `deploy/prometheus/alert.rules.yml`。 + +### 8.3 Grafana仪表板 +```json +{ + "dashboard": { + "title": "外卖SaaS系统监控", + "panels": [ + { + "title": "API请求速率", + "targets": [ + { + "expr": "rate(http_requests_total[5m])" + } + ] + }, + { + "title": "订单创建数", + "targets": [ + { + "expr": "increase(orders_created_total[1h])" + } + ] + }, + { + "title": "数据库连接数", + "targets": [ + { + "expr": "pg_stat_activity_count" + } + ] + } + ] + } +} +``` + +### 8.4 告警规则 +```yaml +# alert.rules.yml +groups: + - name: takeout_alerts + interval: 30s + rules: + - alert: HighErrorRate + expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05 + for: 5m + labels: + severity: critical + annotations: + summary: "高错误率告警" + description: "API错误率超过5%" + + - alert: DatabaseDown + expr: up{job="postgres"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "数据库宕机" + description: "PostgreSQL数据库不可用" + + - alert: HighMemoryUsage + expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9 + for: 5m + labels: + severity: warning + annotations: + summary: "内存使用率过高" + description: "内存使用率超过90%" +``` + +## 9. 日志管理 + +### 9.1 Serilog配置 +```json +{ + "Serilog": { + "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.Elasticsearch"], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "logs/log-.txt", + "rollingInterval": "Day", + "retainedFileCountLimit": 30 + } + }, + { + "Name": "Elasticsearch", + "Args": { + "nodeUris": "http://elasticsearch:9200", + "indexFormat": "takeout-logs-{0:yyyy.MM.dd}", + "autoRegisterTemplate": true + } + } + ], + "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"] + } +} +``` + +### 9.2 ELK Stack部署 +```yaml +# docker-compose.elk.yml +version: '3.8' + +services: + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0 + environment: + - discovery.type=single-node + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ports: + - "9200:9200" + volumes: + - es_data:/usr/share/elasticsearch/data + + logstash: + image: docker.elastic.co/logstash/logstash:8.11.0 + volumes: + - ./logstash/pipeline:/usr/share/logstash/pipeline + ports: + - "5044:5044" + depends_on: + - elasticsearch + + kibana: + image: docker.elastic.co/kibana/kibana:8.11.0 + ports: + - "5601:5601" + environment: + ELASTICSEARCH_HOSTS: http://elasticsearch:9200 + depends_on: + - elasticsearch + +volumes: + es_data: +``` + +## 10. 安全加固 + +### 10.1 防火墙配置 +```bash +# UFW防火墙 +sudo ufw enable +sudo ufw allow 22/tcp # SSH +sudo ufw allow 80/tcp # HTTP +sudo ufw allow 443/tcp # HTTPS +sudo ufw deny 5432/tcp # 禁止外部访问数据库 +sudo ufw deny 6379/tcp # 禁止外部访问Redis +``` + +### 10.2 SSL证书(Let's Encrypt) +```bash +# 安装Certbot +sudo apt-get install certbot python3-certbot-nginx + +# 获取证书 +sudo certbot --nginx -d api.example.com + +# 自动续期 +sudo certbot renew --dry-run + +# 添加定时任务 +0 3 * * * certbot renew --quiet +``` + +### 10.3 应用安全配置 +```csharp +// Program.cs +builder.Services.AddHsts(options => +{ + options.MaxAge = TimeSpan.FromDays(365); + options.IncludeSubDomains = true; + options.Preload = true; +}); + +builder.Services.AddHttpsRedirection(options => +{ + options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect; + options.HttpsPort = 443; +}); + +// 添加安全头 +app.Use(async (context, next) => +{ + context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); + context.Response.Headers.Add("X-Frame-Options", "DENY"); + context.Response.Headers.Add("X-XSS-Protection", "1; mode=block"); + context.Response.Headers.Add("Referrer-Policy", "no-referrer"); + await next(); +}); +``` + +## 11. 性能优化 + +### 11.1 数据库连接池 +```json +{ + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=takeout_saas;Username=user;Password=pass;Pooling=true;MinPoolSize=5;MaxPoolSize=100;ConnectionLifetime=300" + } +} +``` + +### 11.2 Redis连接池 +```csharp +services.AddStackExchangeRedisCache(options => +{ + options.Configuration = configuration["Redis:Configuration"]; + options.InstanceName = "TakeoutSaaS:"; +}); +``` + +### 11.3 响应压缩 +```csharp +builder.Services.AddResponseCompression(options => +{ + options.EnableForHttps = true; + options.Providers.Add(); + options.Providers.Add(); +}); +``` + +## 12. 故障恢复 + +### 12.1 数据库恢复 +```bash +# 从备份恢复 +pg_restore -h localhost -U takeout_user -d takeout_saas -v /backup/full_20240101.dump + +# PITR恢复到指定时间点 +# 1. 停止数据库 +sudo systemctl stop postgresql + +# 2. 恢复基础备份 +rm -rf /var/lib/postgresql/16/main/* +tar -xzf /backup/base_backup.tar.gz -C /var/lib/postgresql/16/main/ + +# 3. 配置recovery.conf +restore_command = 'cp /backup/wal_archive/%f %p' +recovery_target_time = '2024-01-01 12:00:00' + +# 4. 启动数据库 +sudo systemctl start postgresql +``` + +### 12.2 应用回滚 +```bash +# Docker回滚到上一个版本 +docker-compose down +docker-compose up -d --force-recreate --no-deps api + +# 或使用特定版本 +docker pull takeout-saas-api:previous-version +docker-compose up -d +``` + +## 13. 网关 TakeoutSaaS.ApiGateway 部署 + +1. **部署拓扑** + - Nginx 负责域名 `kjkj.qiyuesns.cn`(含后续 HTTPS 证书),并将所有流量反代到本机 `http://127.0.0.1:5000`。 + - .NET 网关容器(TakeoutSaaS.ApiGateway)负责 YARP 路由、限流、日志与 OpenTelemetry 埋点,向下游 49.7.179.246 的 Admin/User/Mini API 转发。 + +2. **构建与运行** + ```bash + # 构建镜像 + docker build -f src/Gateway/TakeoutSaaS.ApiGateway/Dockerfile -t takeoutsaas/apigateway:latest . + + # 启动容器(生产环境建议挂载独立配置) + docker run -d --name takeout-gateway -p 5000:5000 ^ + -e ASPNETCORE_ENVIRONMENT=Production ^ + -v /opt/takeoutsaas/gateway/appsettings.Production.json:/app/appsettings.Production.json ^ + takeoutsaas/apigateway:latest + ``` + - `appsettings.json` 默认将 `/api/admin|mini|user/**` 指向主应用服务器(7801/7701/7901 端口)。 + - `appsettings.Development.json` 可在本地覆盖为 `localhost:5001/5002/5003`,无需改代码。 + +3. **Nginx 参考配置** + ```nginx + server { + listen 80; + server_name kjkj.qiyuesns.cn; + + location / { + proxy_pass http://127.0.0.1:5000; + 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; + } + } + ``` + - 后续启用 HTTPS 时,将 `listen 443 ssl` 与证书配置加入同一 `server`,其余保持不变。 + +4. **关键配置项说明** + - `Gateway:RateLimiting`:按客户端 IP 固定窗口限流(默认 300 次/60 秒),可通过配置文件调整或关闭。 + - `ReverseProxy`:集中声明路由规则(`/api/{service}/**`),后端地址变更时只需改配置即可。 + - `OpenTelemetry`:默认开启 OTLP 导出,Collector 地址通过 `OpenTelemetry:OtlpEndpoint` 指定。 + - `Serilog`:统一输出到控制台,日志采集器可以直接收集 Docker stdout。 + +5. **健康检查** + - `GET /healthz`:基础健康,用于探活或监控告警。 + - `GET /`:返回服务元信息,可作为简易诊断接口。 diff --git a/Document/06_开发规范.md b/Document/06_开发规范.md new file mode 100644 index 0000000..f558dfb --- /dev/null +++ b/Document/06_开发规范.md @@ -0,0 +1,395 @@ +# 外卖SaaS系统 - 开发规范 + +## 1. 代码规范 + +### 1.1 命名规范 + +#### C#命名规范 +```csharp +// 类名:PascalCase +public class OrderService { } + +// 接口:I + PascalCase +public interface IOrderRepository { } + +// 方法:PascalCase +public async Task CreateOrderAsync() { } + +// 私有字段:_camelCase +private readonly IOrderRepository _orderRepository; + +// 公共属性:PascalCase +public string OrderNo { get; set; } + +// 局部变量:camelCase +var orderTotal = 100.00m; + +// 常量:PascalCase +public const int MaxOrderItems = 50; + +// 枚举:PascalCase +public enum OrderStatus +{ + Pending = 1, + Confirmed = 2, + Completed = 3 +} +``` + +#### 数据库命名规范 +```sql +-- 表名:小写,下划线分隔,复数 +orders +order_items +merchant_stores + +-- 字段名:小写,下划线分隔 +order_no +created_at +total_amount + +-- 索引:idx_表名_字段名 +idx_orders_merchant_id +idx_orders_created_at + +-- 外键:fk_表名_引用表名 +fk_orders_merchants +``` + +### 1.2 代码组织 + +#### 项目结构 +``` +TakeoutSaaS/ +├── src/ +│ ├── TakeoutSaaS.Api/ # Web API层 +│ │ ├── Controllers/ # 控制器 +│ │ ├── Filters/ # 过滤器 +│ │ ├── Middleware/ # 中间件 +│ │ ├── Models/ # DTO模型 +│ │ └── Program.cs +│ ├── TakeoutSaaS.Application/ # 应用层 +│ │ ├── Services/ # 应用服务 +│ │ ├── DTOs/ # 数据传输对象 +│ │ ├── Interfaces/ # 服务接口 +│ │ ├── Validators/ # 验证器 +│ │ ├── Mappings/ # 对象映射 +│ │ └── Commands/ # CQRS命令 +│ │ └── Queries/ # CQRS查询 +│ ├── TakeoutSaaS.Domain/ # 领域层 +│ │ ├── Entities/ # 实体 +│ │ ├── ValueObjects/ # 值对象 +│ │ ├── Enums/ # 枚举 +│ │ ├── Events/ # 领域事件 +│ │ └── Interfaces/ # 仓储接口 +│ ├── TakeoutSaaS.Infrastructure/ # 基础设施层 +│ │ ├── Data/ # 数据访问 +│ │ │ ├── EFCore/ # EF Core实现 +│ │ │ ├── Dapper/ # Dapper实现 +│ │ │ └── Repositories/ # 仓储实现 +│ │ ├── Cache/ # 缓存实现 +│ │ ├── MessageQueue/ # 消息队列 +│ │ └── ExternalServices/ # 外部服务 +│ └── TakeoutSaaS.Shared/ # 共享层 +│ ├── Constants/ # 常量 +│ ├── Exceptions/ # 异常 +│ ├── Extensions/ # 扩展方法 +│ └── Results/ # 统一返回结果 +├── tests/ +│ ├── TakeoutSaaS.UnitTests/ # 单元测试 +│ ├── TakeoutSaaS.IntegrationTests/ # 集成测试 +│ └── TakeoutSaaS.PerformanceTests/ # 性能测试 +└── docs/ # 文档 +``` + +### 1.3 代码注释 + +```csharp +/// +/// 订单服务接口 +/// +public interface IOrderService +{ + /// + /// 创建订单 + /// + /// 订单创建请求 + /// 订单信息 + /// 业务异常 + Task CreateOrderAsync(CreateOrderRequest request); +} + +// 复杂业务逻辑添加注释 +public async Task CalculateOrderAmount(Order order) +{ + // 1. 计算菜品总金额 + var dishAmount = order.Items.Sum(x => x.Price * x.Quantity); + + // 2. 计算配送费(距离 > 3km,每公里加收2元) + var deliveryFee = CalculateDeliveryFee(order.Distance); + + // 3. 应用优惠券折扣 + var discount = await ApplyCouponDiscountAsync(order.CouponId, dishAmount); + + // 4. 计算最终金额 + return dishAmount + deliveryFee - discount; +} +``` + +### 1.4 异常处理 + +```csharp +// 自定义业务异常 +public class BusinessException : Exception +{ + public int ErrorCode { get; } + + public BusinessException(int errorCode, string message) + : base(message) + { + ErrorCode = errorCode; + } +} + +// 全局异常处理中间件 +public class ExceptionHandlingMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "业务异常:{Message}", ex.Message); + await HandleBusinessExceptionAsync(context, ex); + } + catch (Exception ex) + { + _logger.LogError(ex, "系统异常:{Message}", ex.Message); + await HandleSystemExceptionAsync(context, ex); + } + } + + private static Task HandleBusinessExceptionAsync(HttpContext context, BusinessException ex) + { + context.Response.StatusCode = StatusCodes.Status422UnprocessableEntity; + return context.Response.WriteAsJsonAsync(new + { + success = false, + code = ex.ErrorCode, + message = ex.Message + }); + } +} + +// 使用示例 +public async Task GetOrderAsync(Guid orderId) +{ + var order = await _orderRepository.GetByIdAsync(orderId); + if (order == null) + { + throw new BusinessException(404, "订单不存在"); + } + return order; +} +``` + +## 2. Git工作流 + +### 2.1 分支管理 + +``` +main # 主分支,生产环境代码 +├── develop # 开发分支 +│ ├── feature/order-management # 功能分支 +│ ├── feature/payment-integration # 功能分支 +│ └── bugfix/order-calculation # 修复分支 +└── hotfix/critical-bug # 紧急修复分支 +``` + +### 2.2 分支命名规范 + +- **功能分支**:`feature/功能名称`(如:`feature/order-management`) +- **修复分支**:`bugfix/问题描述`(如:`bugfix/order-calculation`) +- **紧急修复**:`hotfix/问题描述`(如:`hotfix/payment-error`) +- **发布分支**:`release/版本号`(如:`release/v1.0.0`) + +### 2.3 提交信息规范 + +```bash +# 格式:(): + +# type类型: +# feat: 新功能 +# fix: 修复bug +# docs: 文档更新 +# style: 代码格式调整 +# refactor: 重构 +# perf: 性能优化 +# test: 测试相关 +# chore: 构建/工具相关 + +# 示例 +git commit -m "feat(order): 添加订单创建功能" +git commit -m "fix(payment): 修复支付回调处理错误" +git commit -m "docs(api): 更新API文档" +git commit -m "refactor(service): 重构订单服务" +``` + +### 2.4 工作流程 + +```bash +# 1. 从develop创建功能分支 +git checkout develop +git pull origin develop +git checkout -b feature/order-management + +# 2. 开发并提交 +git add . +git commit -m "feat(order): 添加订单创建功能" + +# 3. 推送到远程 +git push origin feature/order-management + +# 4. 创建Pull Request到develop分支 + +# 5. 代码审查通过后合并 + +# 6. 删除功能分支 +git branch -d feature/order-management +git push origin --delete feature/order-management +``` + +## 3. 代码审查 + +### 3.1 审查清单 + +- [ ] 代码符合命名规范 +- [ ] 代码逻辑清晰,易于理解 +- [ ] 适当的注释和文档 +- [ ] 异常处理完善 +- [ ] 单元测试覆盖 +- [ ] 性能考虑(N+1查询、大数据量处理) +- [ ] 安全性考虑(SQL注入、XSS、权限校验) +- [ ] 日志记录完善 +- [ ] 无硬编码配置 +- [ ] 符合SOLID原则 + +### 3.2 审查重点 + +```csharp +// ❌ 不好的实践 +public class OrderService +{ + public Order CreateOrder(CreateOrderRequest request) + { + // 直接在服务层操作DbContext + var order = new Order(); + _dbContext.Orders.Add(order); + _dbContext.SaveChanges(); + + // 硬编码配置 + var deliveryFee = 5.0m; + + // 没有异常处理 + // 没有日志记录 + + return order; + } +} + +// ✅ 好的实践 +public class OrderService : IOrderService +{ + private readonly IOrderRepository _orderRepository; + private readonly IUnitOfWork _unitOfWork; + private readonly ILogger _logger; + private readonly IOptions _settings; + + public async Task CreateOrderAsync(CreateOrderRequest request) + { + try + { + // 参数验证 + if (request == null) + throw new ArgumentNullException(nameof(request)); + + _logger.LogInformation("创建订单:{@Request}", request); + + // 业务逻辑 + var order = new Order + { + // ... 初始化订单 + DeliveryFee = _settings.Value.DefaultDeliveryFee + }; + + // 使用仓储 + await _orderRepository.AddAsync(order); + await _unitOfWork.SaveChangesAsync(); + + _logger.LogInformation("订单创建成功:{OrderId}", order.Id); + + return _mapper.Map(order); + } + catch (Exception ex) + { + _logger.LogError(ex, "创建订单失败:{@Request}", request); + throw; + } + } +} +``` + + + +## 4. 单元测试规范 + +### 4.1 测试命名 +- 命名格式:`MethodName_Scenario_ExpectedResult` +- 测试覆盖率要求:核心业务逻辑 >= 80% + +### 4.2 测试示例 + +```csharp +[Fact] +public async Task CreateOrder_ValidRequest_ReturnsOrderDto() +{ + // Arrange + var request = new CreateOrderRequest { /* ... */ }; + + // Act + var result = await _orderService.CreateOrderAsync(request); + + // Assert + result.Should().NotBeNull(); + result.OrderNo.Should().NotBeNullOrEmpty(); +} +``` + +## 5. 性能优化规范 + +### 5.1 数据库查询优化 +- 避免N+1查询,使用Include预加载 +- 大数据量查询使用Dapper +- 合理使用索引 + +### 5.2 缓存策略 +- 商家信息:30分钟 +- 菜品信息:15分钟 +- 配置信息:1小时 +- 用户会话:2小时 + +## 6. 文档要求 + +### 6.1 代码文档 +- 所有公共API必须有XML文档注释 +- 复杂业务逻辑添加详细注释 +- README.md说明项目结构和运行方式 + +### 6.2 变更日志 +维护CHANGELOG.md记录版本变更 \ No newline at end of file diff --git a/Document/07_系统架构图.md b/Document/07_系统架构图.md new file mode 100644 index 0000000..3a5a350 --- /dev/null +++ b/Document/07_系统架构图.md @@ -0,0 +1,321 @@ +# 外卖SaaS系统 - 系统架构图 + +## 1. 整体架构图 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ 客户端层 │ +│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │ +│ │ Web管理端 │ │ Web用户端 │ │ 小程序端(用户) │ │ +│ │ (React/Vue) │ │ (React/Vue) │ │ (微信/支付宝) │ │ +│ └──────────────┘ └──────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ API网关层 │ +│ ┌───────────────────────────────────────────────────────────────┐ │ +│ │ Nginx / API Gateway │ │ +│ │ - 路由转发 │ │ +│ │ - 负载均衡 │ │ +│ │ - 限流熔断 │ │ +│ │ - SSL终止 │ │ +│ └───────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 应用服务层 │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ 租户服务 │ │ 商家服务 │ │ 菜品服务 │ │ +│ │ - 租户管理 │ │ - 商家管理 │ │ - 菜品管理 │ │ +│ │ - 权限管理 │ │ - 门店管理 │ │ - 分类管理 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ 订单服务 │ │ 配送服务 │ │ 用户服务 │ │ +│ │ - 订单管理 │ │ - 配送员管理 │ │ - 用户管理 │ │ +│ │ - 订单流转 │ │ - 任务分配 │ │ - 地址管理 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ 支付服务 │ │ 营销服务 │ │ 通知服务 │ │ +│ │ - 支付处理 │ │ - 优惠券 │ │ - 短信通知 │ │ +│ │ - 退款处理 │ │ - 活动管理 │ │ - 推送通知 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ 基础设施层 │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ PostgreSQL │ │ Redis │ │ RabbitMQ │ │ +│ │ - 主数据库 │ │ - 缓存 │ │ - 消息队列 │ │ +│ │ - 主从复制 │ │ - 会话存储 │ │ - 异步任务 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ MinIO/OSS │ │ Elasticsearch│ │ Prometheus │ │ +│ │ - 对象存储 │ │ - 日志存储 │ │ - 监控告警 │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +## 2. 应用分层架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Presentation Layer │ +│ (表现层) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ TakeoutSaaS.Api │ │ +│ │ - Controllers (控制器) │ │ +│ │ - Filters (过滤器) │ │ +│ │ - Middleware (中间件) │ │ +│ │ - Models (DTO模型) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (应用层) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ TakeoutSaaS.Application │ │ +│ │ - Services (应用服务) │ │ +│ │ - DTOs (数据传输对象) │ │ +│ │ - Interfaces (服务接口) │ │ +│ │ - Validators (验证器) │ │ +│ │ - Mappings (对象映射) │ │ +│ │ - Commands/Queries (CQRS) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Domain Layer │ +│ (领域层) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ TakeoutSaaS.Domain │ │ +│ │ - Entities (实体) │ │ +│ │ - ValueObjects (值对象) │ │ +│ │ - Enums (枚举) │ │ +│ │ - Events (领域事件) │ │ +│ │ - Interfaces (仓储接口) │ │ +│ │ - Specifications (规约) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Infrastructure Layer │ +│ (基础设施层) │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ TakeoutSaaS.Infrastructure │ │ +│ │ - Data (数据访问) │ │ +│ │ - EFCore (EF Core实现) │ │ +│ │ - Dapper (Dapper实现) │ │ +│ │ - Repositories (仓储实现) │ │ +│ │ - Cache (缓存实现) │ │ +│ │ - MessageQueue (消息队列) │ │ +│ │ - ExternalServices (外部服务) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## 3. 订单处理流程图 + +``` +用户下单 → 创建订单 → 支付 → 商家接单 → 制作 → 配送 → 完成 + │ │ │ │ │ │ │ + │ │ │ │ │ │ └─→ 订单完成 + │ │ │ │ │ └─→ 配送中 + │ │ │ │ └─→ 制作中 + │ │ │ └─→ 待制作 + │ │ └─→ 待接单 + │ └─→ 待支付 + └─→ 订单创建 + +取消流程: +用户取消 ──→ 退款处理 ──→ 订单取消 +商家拒单 ──→ 退款处理 ──→ 订单取消 +超时未支付 ──→ 自动取消 +``` + +## 4. 数据流转图 + +``` +┌──────────┐ +│ 客户端 │ +└────┬─────┘ + │ HTTP Request + ▼ +┌──────────────────┐ +│ API Gateway │ +│ (Nginx) │ +└────┬─────────────┘ + │ 路由转发 + ▼ +┌──────────────────┐ +│ Web API │ +│ - 认证授权 │ +│ - 参数验证 │ +└────┬─────────────┘ + │ 调用服务 + ▼ +┌──────────────────┐ +│ Application │ +│ Service │ +│ - 业务逻辑 │ +└────┬─────────────┘ + │ 数据访问 + ▼ +┌──────────────────┐ ┌──────────┐ +│ Repository │────→│ Cache │ +│ - EF Core │ │ (Redis) │ +│ - Dapper │ └──────────┘ +└────┬─────────────┘ + │ SQL查询 + ▼ +┌──────────────────┐ +│ PostgreSQL │ +│ Database │ +└──────────────────┘ +``` + +## 5. 多租户数据隔离架构 + +``` +┌─────────────────────────────────────────────────┐ +│ 租户识别中间件 │ +│ - 从JWT Token解析租户ID │ +│ - 从HTTP Header获取租户ID │ +└─────────────────────┬───────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────┐ +│ 租户上下文 │ +│ - 当前租户ID │ +│ - 租户配置信息 │ +└─────────────────────┬───────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────┐ +│ 数据访问层 │ +│ - 自动添加租户ID过滤 │ +│ - 全局查询过滤器 │ +└─────────────────────┬───────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────┐ +│ 数据库 │ +│ 租户A数据 │ 租户B数据 │ 租户C数据 │ +│ (tenant_id = A) (tenant_id = B) (tenant_id = C)│ +└─────────────────────────────────────────────────┘ +``` + +## 6. 缓存架构 + +``` +┌──────────────┐ +│ Application │ +└──────┬───────┘ + │ + ▼ +┌──────────────────────────────────┐ +│ Cache Aside Pattern │ +│ 1. 查询缓存 │ +│ 2. 缓存未命中,查询数据库 │ +│ 3. 写入缓存 │ +└──────┬───────────────────────────┘ + │ + ├─→ L1 Cache (Memory Cache) + │ - 进程内缓存 + │ - 热点数据 + │ + └─→ L2 Cache (Redis) + - 分布式缓存 + - 会话数据 + - 共享数据 +``` + +## 7. 消息队列架构 + +``` +┌──────────────┐ +│ Producer │ +│ (订单服务) │ +└──────┬───────┘ + │ 发布事件 + ▼ +┌──────────────────┐ +│ RabbitMQ │ +│ Exchange │ +└──────┬───────────┘ + │ + ├─→ Queue: order.created + │ └─→ Consumer: 通知服务 + │ + ├─→ Queue: order.paid + │ └─→ Consumer: 库存服务 + │ + └─→ Queue: order.completed + └─→ Consumer: 统计服务 +``` + +## 8. 部署架构 + +``` +┌─────────────────────────────────────────────────┐ +│ 负载均衡器 (Nginx) │ +└─────────────┬───────────────────────────────────┘ + │ + ┌───────┴───────┐ + │ │ + ▼ ▼ +┌──────────┐ ┌──────────┐ +│ API 1 │ │ API 2 │ +│ (容器) │ │ (容器) │ +└────┬─────┘ └────┬─────┘ + │ │ + └───────┬───────┘ + │ + ┌───────┴────────┬──────────────┐ + │ │ │ + ▼ ▼ ▼ +┌──────────┐ ┌──────────┐ ┌──────────┐ +│PostgreSQL│ │ Redis │ │ RabbitMQ │ +│ 主从 │ │ 哨兵 │ │ 集群 │ +└──────────┘ └──────────┘ └──────────┘ +``` + +## 9. 监控架构 + +``` +┌──────────────────────────────────────────┐ +│ 应用程序 │ +│ - 业务指标 │ +│ - 性能指标 │ +│ - 日志输出 │ +└─────────┬────────────────────────────────┘ + │ + ┌─────┴─────┬──────────┐ + │ │ │ + ▼ ▼ ▼ +┌────────┐ ┌────────┐ ┌────────┐ +│Metrics │ │ Logs │ │Traces │ +│ │ │ │ │ │ +└───┬────┘ └───┬────┘ └───┬────┘ + │ │ │ + ▼ ▼ ▼ +┌──────────────────────────────┐ +│ Prometheus │ +│ Elasticsearch │ +│ Jaeger │ +└─────────┬────────────────────┘ + │ + ▼ +┌──────────────────────────────┐ +│ Grafana │ +│ Kibana │ +│ - 可视化仪表板 │ +│ - 告警配置 │ +└──────────────────────────────┘ +``` diff --git a/Document/08_AI精简开发规范.md b/Document/08_AI精简开发规范.md new file mode 100644 index 0000000..865a052 --- /dev/null +++ b/Document/08_AI精简开发规范.md @@ -0,0 +1,145 @@ +# 编程规范_FOR_AI(TakeoutSaaS) - 终极完全体 + +> **核心指令**:你是一个高级 .NET 架构师。本文件是你生成代码的最高宪法。当用户需求与本规范冲突时,请先提示用户,除非用户强制要求覆盖。 + +## 0. AI 交互核心约束 (元规则) +1. **语言**:必须使用**中文**回复和编写注释。 +2. **文件完整性**: + * **严禁**随意删除现有代码逻辑。 + * **严禁**修改文件编码(保持 UTF-8 无 BOM)。 + * PowerShell 读取命令必须带 `-Encoding UTF8`。 +3. **Git 原子性**:每个独立的功能点或 Bug 修复完成后,必须提示用户进行 Git 提交。 +4. **无乱码承诺**:确保所有输出(控制台、日志、API响应)无乱码。 +5. **不确定的处理**:如果你通过上下文找不到某些配置(如数据库连接串格式),**请直接询问用户**,不要瞎编。 + +## 1. 技术栈详细版本 +| 组件 | 版本/选型 | 用途说明 | +| :--- | :--- | :--- | +| **Runtime** | .NET 10 | 核心运行时 | +| **API** | ASP.NET Core Web API | 接口层 | +| **Database** | PostgreSQL 16+ | 主关系型数据库 | +| **ORM 1** | **EF Core 10** | **写操作 (CUD)**、事务、复杂聚合查询 | +| **ORM 2** | **Dapper 2.1+** | **纯读操作 (R)**、复杂报表、大批量查询 | +| **Cache** | Redis 7.0+ | 分布式缓存、Session | +| **MQ** | RabbitMQ 3.12+ | 异步解耦 (MassTransit) | +| **Libs** | MediatR, Serilog, FluentValidation | CQRS, 日志, 验证 | + +## 2. 命名与风格 (严格匹配) +* **C# 代码**: + * 类/接口/方法/属性:`PascalCase` (如 `OrderService`) + * **布尔属性**:必须加 `Is` 或 `Has` 前缀 (如 `IsDeleted`, `HasPayment`) + * 私有字段:`_camelCase` (如 `_orderRepository`) + * 参数/变量:`camelCase` (如 `orderId`) +* **PostgreSQL 数据库**: + * 表名:`snake_case` + **复数** (如 `merchant_orders`) + * 列名:`snake_case` (如 `order_no`, `is_active`) + * 主键:`id` (类型 `bigint`) +* **文件规则**: + * **一个文件一个类**。文件名必须与类名完全一致。 + +## 3. 分层架构 (Clean Architecture) +**你生成的代码必须严格归类到以下目录:** +* **`src/Api`**: 仅负责路由与 DTO 转换,**禁止**包含业务逻辑。 +* **`src/Application`**: 业务编排层。必须使用 **CQRS** (`IRequestHandler`) 和 **Mediator**。 +* **`src/Domain`**: 核心领域层。包含实体、枚举、领域异常。**禁止**依赖 EF Core 等外部库。 +* **`src/Infrastructure`**: 基础设施层。实现仓储、数据库上下文、第三方服务。 + +## 4. 注释与文档 +* **强制 XML 注释**:所有 `public` 的类、方法、属性必须有 ``。 +* **步骤注释**:超过 5 行的业务逻辑,必须分步注释: + ```csharp + // 1. 验证库存 + // 2. 扣减余额 + ``` +* **Swagger**:必须开启 JWT 鉴权按钮,Request/Response 示例必须清晰。 + +## 5. 异常处理 (防御性编程) +* **禁止空 Catch**:严禁 `catch (Exception) {}`,必须记录日志或抛出。 +* **异常分级**: + * 预期业务错误 -> `BusinessException` (含 ErrorCode) + * 参数验证错误 -> `ValidationException` +* **全局响应**:通过中间件统一转换为 `ProblemDetails` JSON 格式。 + +## 6. 异步与日志 +* **全异步**:所有 I/O 操作必须 `await`。**严禁** `.Result` 或 `.Wait()`。 +* **结构化日志**: + * ❌ `_logger.LogInfo("订单 " + id + " 创建成功");` + * ✅ `_logger.LogInformation("订单 {OrderId} 创建成功", id);` +* **脱敏**:严禁打印密码、密钥、支付凭证等敏感信息。 + +## 7. 依赖注入 (DI) +* **构造函数注入**:统一使用构造函数注入。 +* **禁止项**: + * ❌ 禁止使用 `[Inject]` 属性注入。 + * ❌ 禁止使用 `ServiceLocator` (服务定位器模式)。 + * ❌ 禁止在静态类中持有 ServiceProvider。 + +## 8. 数据访问规范 (重点执行) +### 8.1 Entity Framework Core (写/事务) +1. **无跟踪查询**:只读查询**必须**加 `.AsNoTracking()`。 +2. **杜绝 N+1**:严禁在 `foreach` 循环中查询数据库。必须使用 `.Include()`。 +3. **复杂查询**:关联表超过 2 层时,考虑使用 `.AsSplitQuery()`。 + +### 8.2 Dapper (读/报表) +1. **SQL 注入防御**:**严禁**拼接 SQL 字符串。必须使用参数化查询 (`@Param`)。 +2. **字段映射**:注意 PostgreSQL (`snake_case`) 与 C# (`PascalCase`) 的映射配置。 + +## 9. 多租户与 ID 策略 +* **ID 生成**: + * **强制**使用 **雪花算法 (Snowflake ID)**。 + * 类型:C# `long` <-> DB `bigint`。 + * **禁止**使用 UUID 或 自增 INT。 +* **租户隔离**: + * 所有业务表必须包含 `tenant_id`。 + * 写入时自动填充,读取时强制过滤。 + +## 10. API 设计与序列化 (前端兼容) +* **大整数处理**: + * 所有 `long` 类型 (Snowflake ID) 在 DTO 中**必须序列化为 string**。 + * 方案:DTO 属性加 `[JsonConverter(typeof(ToStringJsonConverter))]` 或全局配置。 +* **DTO 规范**: + * 输入:`XxxRequest` + * 输出:`XxxDto` + * **禁止** Controller 直接返回 Entity。 + +## 11. 模块化与复用 +* **核心模块划分**:Identity (身份), Tenancy (租户), Dictionary (字典), Storage (存储)。 +* **公共库 (Shared)**:通用工具类、扩展方法、常量定义必须放在 `Core/Shared` 项目中,避免重复造轮子。 + +## 12. 测试规范 +* **模式**:Arrange-Act-Assert (AAA)。 +* **工具**:xUnit + Moq + FluentAssertions。 +* **覆盖率**:核心 Domain 逻辑必须 100% 覆盖;Service 层 ≥ 70%。 + +## 13. Git 工作流 +* **提交格式 (Conventional Commits)**: + * `feat`: 新功能 + * `fix`: 修复 Bug + * `refactor`: 重构 + * `docs`: 文档 + * `style`: 格式调整 +* **分支规范**:`feature/功能名`,`bugfix/问题描述`。 + +## 14. 性能优化 (显式指令) +* **投影查询**:使用 `.Select(x => new Dto { ... })` 只查询需要的字段,减少 I/O。 +* **缓存策略**:Cache-Aside 模式。数据更新后必须立即失效缓存。 +* **批量操作**: + * EF Core 10:使用 `ExecuteUpdateAsync` / `ExecuteDeleteAsync`。 + * Dapper:使用 `ExecuteAsync` 进行批量插入。 + +## 15. 安全规范 +* **SQL 注入**:已在第 8 条强制参数化。 +* **身份认证**:Admin 端使用 JWT + RBAC;小程序端使用 Session/Token。 +* **密码存储**:必须使用 PBKDF2 或 BCrypt 加盐哈希。 + +## 16. 绝对禁止事项 (AI 自检清单) +**生成代码前,请自查是否违反以下红线:** +1. [ ] **SQL 注入**:是否拼接了 SQL 字符串? +2. [ ] **架构违规**:是否在 Controller/Domain 中使用了 DbContext? +3. [ ] **数据泄露**:是否返回了 Entity 或打印了密码? +4. [ ] **同步阻塞**:是否使用了 `.Result` 或 `.Wait()`? +5. [ ] **性能陷阱**:是否在循环中查询了数据库 (N+1)? +6. [ ] **精度丢失**:Long 类型的 ID 是否转为了 String? +7. [ ] **配置硬编码**:是否直接写死了连接串或密钥? + +--- \ No newline at end of file diff --git a/Document/09_服务器文档.md b/Document/09_服务器文档.md new file mode 100644 index 0000000..a682d88 --- /dev/null +++ b/Document/09_服务器文档.md @@ -0,0 +1,79 @@ +# 服务器文档 + +> 汇总原 12~15 号服务器记录,统一追踪账号、密码、用途与到期时间,便于统一维护。 + +## 1. 阿里云网关服务器 + +### 基础信息 +- IP: 47.94.199.87 +- 账户: root +- 密码: cJ5q2k2iW7XnMA^! +- 配置: 2 核 CPU / 2 GB 内存(阿里云轻量应用服务器) +- 地点: 北京 +- 用途: 网关 +- 到期时间: 2026-12-18 + +### 建议补充 +- 系统版本: 待补充(执行 `cat /etc/os-release`) +- 带宽/磁盘: 待补充 +- 安全组/开放端口: 待补充 +- 备份与监控: 待补充 +- 变更记录: 待补充 + +## 2. 腾讯云主机 PostgreSQL 服务器 + +### 基础信息 +- IP: 120.53.222.17 +- 账户: ubuntu +- 密码: P3y$nJt#zaa4%fh5 +- 配置: 2 核 CPU / 4 GB 内存 +- 地点: 北京 +- 用途: 主 PostgreSQL / 数据库服务器 +- 到期时间: 2026-11-26 11:22:01 + +### 建议补充 +- 系统版本: 待补充(执行 `cat /etc/os-release`) +- 带宽/磁盘: 待补充 +- 数据目录: 待补充(示例 `/var/lib/postgresql`) +- 数据备份/监控: 待补充 +- 安全组/开放端口: 待补充 +- 变更记录: 待补充 + +## 3. 天翼云主机 应用服务器 + +### 基础信息 +- IP: 49.7.179.246 +- 账户: root +- 密码: 7zE&84XI6~w57W7N +- 配置: 4 核 CPU / 8 GB 内存(天翼云) +- 地点: 北京 +- 用途: 主应用服务器(承载 Admin/User/Mini API 或网关实例) +- 到期时间: 2027-10-04 17:17:57 + +### 建议补充 +- 系统版本: 待补充(执行 `cat /etc/os-release`) +- 带宽/磁盘: 待补充 +- 部署路径: 待补充(示例 `/opt/takeoutsaas`) +- 进程/端口: 待补充(示例 `AdminApi/UserApi/MiniApi`、`:8080`) +- 日志/监控: 待补充(Serilog 文件目录、进程监控方式) +- 安全组/开放端口: 待补充(按 API/网关暴露的 HTTP/HTTPS 端口) +- 变更记录: 待补充 + +## 4. 腾讯云 Redis/RabbitMQ 服务器 + +### 基础信息 +- IP: 49.232.6.45 +- 账户: ubuntu +- 密码: Z7NsRjT&XnWg7%7X +- 配置: 2 核 CPU / 4 GB 内存 +- 地点: 北京 +- 用途: Redis 与 RabbitMQ +- 到期时间: 2028-11-26 + +### 建议补充 +- 系统版本: 待补充(执行 `cat /etc/os-release`) +- 带宽/磁盘: 待补充 +- 安全组/开放端口: 待补充(Redis 6379,RabbitMQ 5672/15672 等) +- 数据持久化与备份: 待补充 +- 监控与告警: 待补充 +- 变更记录: 待补充 diff --git a/Document/10_设计期DbContext配置指引.md b/Document/10_设计期DbContext配置指引.md new file mode 100644 index 0000000..2da0fd9 --- /dev/null +++ b/Document/10_设计期DbContext配置指引.md @@ -0,0 +1,131 @@ +# 设计时 DbContext 配置指引 + +> 目的:在执行 `dotnet ef` 命令时无需硬编码数据库连接,可根据 appsettings 与环境变量自动加载。本文覆盖环境变量设置、配置目录指定等细节。 + +## 三库迁移命令 只需更改 SnowflakeIds_App 迁移关键字 +> 先生成迁移,再执行数据库更新。启动项目统一用 AdminApi 确保加载最新配置。 + +### 生成迁移 +```bash +# App 主库 +dotnet tool run dotnet-ef migrations add SnowflakeIds_App ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.App.Persistence.TakeoutAppDbContext + +# Identity 库 +dotnet tool run dotnet-ef migrations add SnowflakeIds_Identity ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.Identity.Persistence.IdentityDbContext + +# Dictionary 库 +dotnet tool run dotnet-ef migrations add SnowflakeIds_Dictionary ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext +``` + +### 更新数据库 +```bash +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.App.Persistence.TakeoutAppDbContext + +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.Identity.Persistence.IdentityDbContext + +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj ` + --context TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext +``` + +## 一、设计时工厂读取逻辑概述 +设计时工厂(`DesignTimeDbContextFactoryBase`)按下面顺序解析连接串: +1. 若设置了 `TAKEOUTSAAS_APP_CONNECTION` / `TAKEOUTSAAS_IDENTITY_CONNECTION` / `TAKEOUTSAAS_DICTIONARY_CONNECTION` 等环境变量,则优先使用。 +2. 否则查找配置文件: + - 从当前目录开始向上找到含 `TakeoutSaaS.sln` 的仓库根。 + - 依次检查 `src/Api/TakeoutSaaS.AdminApi`、`src/Api/TakeoutSaaS.UserApi`、`src/Api/TakeoutSaaS.MiniApi` 等目录,如果存在 `appsettings.json` 或 `appsettings.{Environment}.json` 则加载。 + - 若未找到,可通过环境变量 `TAKEOUTSAAS_APPSETTINGS_DIR` 指定包含 appsettings 文件的目录。 + +配置结构示例(出现在 AdminApi/MiniApi/UserApi 的 appsettings): +```json +"Database": { + "DataSources": { + "AppDatabase": { + "Write": "Host=120.53...;Database=takeout_app_db;Username=...;Password=...", + "Reads": [ + "Host=120.53...;Database=takeout_app_db;Username=...;Password=..." + ] + }, + "IdentityDatabase": { + "Write": "...", + "Reads": [ "..." ] + }, + "DictionaryDatabase": { + "Write": "...", + "Reads": [ "..." ] + } + } +} +``` +设计时工厂会根据数据源名称(`DatabaseConstants.AppDataSource` 等)读取 `Write` 连接串,实现与运行时一致。 + +## 二、环境变量配置 +### 1. Windows PowerShell +```powershell +# 指向包含 appsettings.json 的目录 +$env:TAKEOUTSAAS_APPSETTINGS_DIR = \"D:\\HAZCode\\TakeOut\\src\\Api\\TakeoutSaaS.AdminApi\" + +#(可选)覆盖 AppDatabase 连接串 +$env:TAKEOUTSAAS_APP_CONNECTION = \"Host=120.53.222.17;Port=5432;Database=takeout_app_db;Username=app_user;Password=***\" + +#(可选)覆盖 IdentityDatabase 连接串 +$env:TAKEOUTSAAS_IDENTITY_CONNECTION = \"Host=...;Database=takeout_identity_db;Username=...;Password=...\" + +#(可选)覆盖 DictionaryDatabase 连接串 +$env:TAKEOUTSAAS_DICTIONARY_CONNECTION = "Host=...;Database=takeout_dictionary_db;Username=...;Password=..." +``` + +### 2. Linux / macOS +```bash +export TAKEOUTSAAS_APPSETTINGS_DIR=/home/user/TakeOut/src/Api/TakeoutSaaS.AdminApi +export TAKEOUTSAAS_APP_CONNECTION=\"Host=120.53.222.17;Port=5432;Database=takeout_app_db;Username=app_user;Password=***\" +export TAKEOUTSAAS_IDENTITY_CONNECTION=\"Host=...;Database=takeout_identity_db;Username=...;Password=...\" +export TAKEOUTSAAS_DICTIONARY_CONNECTION="Host=...;Database=takeout_dictionary_db;Username=...;Password=..." +``` + +> 注意:若设置了 `TAKEOUTSAAS_APP_CONNECTION`,则无需在 appsettings 中提供 `Write` 连接串,反之亦然。不要将明文密码写入代码仓库,建议使用 Secret Manager 或部署环境的安全存储。 + +## 三、执行脚本示例 +完成上述环境变量配置后即可执行: +```powershell +# TakeoutAppDbContext(业务库) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.App.Persistence.TakeoutAppDbContext + +# IdentityDbContext(身份库) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.Identity.Persistence.IdentityDbContext + +# DictionaryDbContext(字典库) +dotnet tool run dotnet-ef database update ` + --project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --startup-project src/Infrastructure/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj ` + --context TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext +``` + +若需迁移 Identity/Dictionary 等上下文,替换 `--context` 参数为对应类型即可。 + +## 四、常见问题 +1. **未找到 appsettings**:确保 `TAKEOUTSAAS_APPSETTINGS_DIR` 指向存在 `appsettings.json` 的目录,或将命令在 API 项目目录中执行。 +2. **密码错误**:确认远程 PostgreSQL 用户/密码是否与 appsettings 或环境变量一致,避免在 CLI 中使用默认的账号。 +3. **多环境配置**:`ASPNETCORE_ENVIRONMENT` 变量可控制加载 `appsettings.{Environment}.json`;默认是 Development。 diff --git a/Document/11_SystemTodo.md b/Document/11_SystemTodo.md new file mode 100644 index 0000000..dead828 --- /dev/null +++ b/Document/11_SystemTodo.md @@ -0,0 +1,67 @@ +# TODO Roadmap + +> 当前列表为原 11 号文档中的待办事项,已迁移到此处并统一以复选框形式标记。若无特殊说明,均尚未完成。 + +## 1. 配置与基础设施(高优) +- [x] Development/Production 数据库连接与 Secret 落地(Staging 暂不需要)。 +- [x] Redis 服务部署完毕并记录配置。 +- [x] RabbitMQ 服务部署完毕并记录配置。 +- [x] COS 密钥配置补录完毕。 +- [ ] OSS 密钥配置补录完毕(已忽略,待采购后再补录)。 +- [ ] SMS 平台密钥配置补录完毕(已忽略,待采购后再补录)。 +- [x] WeChat Mini 程序密钥配置补录完毕(AppID:wx30f91e6afe79f405,AppSecret:64324a7f604245301066ba7c3add488e,已同步到 admin/mini 配置并登记更新人)。 +- [x] PostgreSQL 基础实例部署完毕并记录配置。 +- [x] Postgres/Redis 接入文档 + IaC/脚本补齐(见 Document/infra/postgres_redis.md 与 deploy/postgres|redis)。 +- [x] RabbitMQ/Redis/Hangfire storage scripts available (see deploy/postgres and deploy/redis). +- [ ] admin/mini/user/gateway 网关域名、证书、CORS 列表整理完成。(忽略,暂时不用完成) +- [ ] Hangfire Dashboard 启用并新增 Admin 角色验证/网关白名单。(忽略,暂时不用完成) + +## 2. 数据与迁移(高优) +- [x] App/Identity/Dictionary/Hangfire 四个 DbContext 均生成初始 Migration 并成功 update database。 +- [x] 商户/门店/商品/订单/支付/配送等实体与仓储实现完成,提供 CRUD + 查询。 +- [x] 系统参数、默认租户、管理员账号、基础字典的种子脚本可重复执行。 + +## 3. 稳定性与质量(低优先级) +- [ ] Dictionary/Identity/Storage/Sms/Messaging/Scheduler 的 xUnit+FluentAssertions 单元测试框架搭建。 +- [ ] WebApplicationFactory + Testcontainers 拉起 Postgres/Redis/RabbitMQ 的集成测试模板。 +- [ ] .editorconfig、.globalconfig、Roslyn 分析器配置仓库通用规则并启用 CI 检查。 + +## 4. 安全与合规 +- [x] RBAC 权限、租户隔离、用户/权限洞察 API 完整演示并在 Swagger 中提供示例。 + - [x] 现状梳理:租户解析/过滤已具备(TenantResolutionMiddleware、TenantAwareDbContext),JWT 已写入 roles/permissions/tenant_id(JwtTokenService),PermissionAuthorize 已在 Admin API 使用,CurrentUserProfile 含角色/权限/租户;但仅有内嵌 string[] 权限存储,无角色/权限表与洞察查询,Swagger 缺少示例与多租户示例。 + - [x] 差距与步骤: + - [x] 增加权限/租户洞察查询(按用户、按租户分页)并确保带 tenant 过滤(TenantAwareDbContext 或 Dapper 参数化)。 + - [x] 输出可读的角色/权限列表(基于现有种子/配置的只读查询)。【已落地:RBAC1 模型 + 角色/权限管理 API;Swagger 示例后续补充】 + - [x] 为洞察接口和 /auth/profile 增加 Swagger 示例,包含 tenant_id、roles、permissions,展示 Bearer 示例与租户 Header 示例。 + - [ ] 若用 Dapper 读侧,SQL 必须参数化并显式过滤 tenant_id。 + - [x] 计划顺序:Step A 设计应用层洞察 DTO/Query;Step B Admin API 只读端点(Authorize/PermissionAuthorize);Step C Swagger 示例扩展;Step D 校验租户过滤与忽略路径配置。 + - [x] Step D 校验:Admin API 管道已在 Auth 之前使用 TenantResolution,中间件未忽略新接口;查询使用 TenantAwareDbContext + ITenantProvider 双重租户校验,暂无需调整。后续若加 Dapper 读侧需显式带 tenant 过滤。 +- [ ] 登录/刷新流程增加 IP 校验、租户隔离、验证码/频率限制。 +- [ ] 登录/权限/敏感操作日志可追溯,提供查询接口或 Kibana Saved Search。 +- [ ] Secret Store/KeyVault/KMS 管理敏感配置,禁止密钥写入 Git/数据库明文。 + +## 5. 观测与运维 +- [x] TraceId 贯通,Serilog 输出 Console/File(ELK 待后续配置)。 +- [x] Prometheus exporter 暴露关键指标,/health 探针与告警规则同步推送。 +- [ ] PostgreSQL 全量/增量备份脚本及一次真实恢复演练报告。 + +## 6. 业务能力补全 +- [ ] 商户/门店/菜品 API 完成并在 MQ 中投递上架/支付成功事件。 +- [ ] 配送对接 API 支持下单/取消/查询并完成签名验签中间件。 +- [ ] 小程序端商品浏览、下单、支付、评价、图片直传等 API 可闭环跑通。 + +## 7. 前后台 UI 对接 +- [ ] Admin UI 通过 OpenAPI 生成或手写界面,接入 Hangfire Dashboard/MQ 监控只读模式。 +- [ ] 小程序端完成登录、菜单浏览、下单、支付、物流轨迹、素材直传闭环。 + +## 8. CI/CD 与发布 +- [ ] CI/CD 流水线覆盖构建、发布、静态扫描、数据库迁移。 + - [x] `.github/workflows/ci-cd.yml` 已覆盖 Admin/Mini/User API 的变更检测、Docker Build/Push 与 SSH 部署。 + - [ ] 尚未集成静态代码扫描、安全扫描与数据库迁移自动化。 +- [ ] Dev/Staging/Prod 多环境配置矩阵 + 基础设施 IaC 脚本。 +- [ ] 版本与发布说明模板整理并在仓库中提供示例。 + +## 9. 文档与知识库 +- [ ] 接口文档、领域模型、关键约束使用 Markdown 或 API Portal 完整记录。 +- [ ] 运行手册包含部署步骤、资源拓扑、故障排查手册。 +- [ ] 安全合规模板覆盖数据分级、密钥管理、审计流程并形成可复用表格。 diff --git a/Document/12_BusinessTodo.md b/Document/12_BusinessTodo.md new file mode 100644 index 0000000..00d98dd --- /dev/null +++ b/Document/12_BusinessTodo.md @@ -0,0 +1,73 @@ +# 里程碑待办追踪 + +> 按“小程序版模块规划”划分四个里程碑;每个里程碑只含对应范围的任务,便于分阶段推进。 + +--- +## Phase 1(当前阶段):租户/商家入驻、门店与菜品、扫码堂食、基础下单支付、预购自提、第三方配送骨架 +- [x] 管理端租户 API:注册、实名认证、套餐订阅/续费/升降配、审核流,Swagger ≥6 个端点,含审核日志。 + - 已交付:`src/Api/TakeoutSaaS.AdminApi/Controllers/TenantsController.cs` 暴露注册、详情、实名提交、审核、订阅创建/升降配、审核日志 8 个端点;对应命令/查询位于 `src/Application/TakeoutSaaS.Application/App/Tenants`,仓储实现 `EfTenantRepository`,并写入 `TenantAuditLog` 记录。Swagger 自动收录上述接口,满足 Phase1 租户管理要求。 +- [x] 商家入驻 API:证照上传、合同管理、类目选择,驱动待审/审核/驳回/通过状态机,文件持久在 COS。 + - 已交付:`src/Api/TakeoutSaaS.AdminApi/Controllers/MerchantsController.cs` 新增证照上传/审核、合同创建与状态更新、商户审核、审核日志、类目列表等 8 个端点;应用层新增 `AddMerchantDocumentCommand`、`CreateMerchantContractCommand`、`ReviewMerchantCommand` 等 Handler;`MerchantDocument/Contract/Audit` DTO 完整返回详情,文件 URL 仍通过 `/api/admin/v1/files/upload` 上 COS。仓储实现扩展 `EfMerchantRepository` 支持文档/合同/AuditLog 持久化,`TakeoutAppDbContext` 新增 `merchant_audit_logs` 表实现状态机追踪。 +- [x] RBAC 模板:平台管理员、租户管理员、店长、店员四角色模板;API 可复制并允许租户自定义扩展。 + - 已交付:角色模板改为数据库驱动,新增 `RoleTemplate/RoleTemplatePermission` 实体与仓储接口/实现;应用层提供模板列表/详情/创建/更新/删除、按模板复制与租户批量初始化命令/查询;Admin 端 `RolesController` 暴露模板 CRUD 与复制/初始化端点(`src/Api/TakeoutSaaS.AdminApi/Controllers/RolesController.cs`),复制时补齐缺失权限且保留租户自定义授权;预置模板/权限种子写入 `src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.*.json`。 +- [x] 配额与套餐:TenantPackage CRUD、订阅/续费/配额校验(门店/账号/短信/配送单量),超额返回 409 并记录 TenantQuotaUsage。 + - 已交付:新增套餐仓储与命令/查询/DTO(`src/Application/TakeoutSaaS.Application/App/Tenants`),Admin 端新增 `TenantPackagesController` 提供套餐列表/详情/创建/更新/删除接口。新增配额校验命令与租户接口 `/api/admin/v1/tenants/{id}/quotas/check`,基于当前订阅套餐限额校验并占用配额,超额抛出 409 并写入 `TenantQuotaUsage`。仓储注册于 `AddAppInfrastructure`。 +- [x] 租户运营面板:欠费/到期告警、账单列表、公告通知接口,支持已读状态并在 Admin UI 展示。 + - 已交付:新增账单/公告/通知实体与仓储,Admin 端提供 `/tenants/{id}/billings`(列表/详情/创建/标记支付)、`/announcements`(列表/详情/创建/更新/删除/已读)、`/notifications`(列表/已读)端点;权限码补充 `tenant-bill:*`、`tenant-announcement:*`、`tenant-notification:*`,种子模板更新;配额/订阅告警可通过通知表承载。 +- [x] 门店管理:Store/StoreBusinessHour/StoreDeliveryZone/StoreHoliday CRUD 完整,含 GeoJSON 配送范围及能力开关。 + - 已交付:营业时间/配送区/节假日命令、查询、验证与处理器齐全,Admin API 子路由完成 CRUD,门店能力开关(预约/排队)对外暴露,仓储读写删除均带租户过滤。 +- [x] 桌码管理:批量生成桌码、绑定区域/容量、导出二维码 ZIP(POST /api/admin/stores/{id}/tables 可下载)。 + - 已交付:桌台区域/桌码 DTO、命令、查询、验证与处理器完善,支持批量生成、区域绑定/更新;Admin API 增加区域/桌码 CRUD 与二维码 ZIP 导出(QRCoder 生成 SVG 打包),仓储补齐查找、更新、删除。 +- [x] 员工排班:创建员工、绑定门店角色、维护 StoreEmployeeShift,可查询未来 7 日排班。 + - 已交付:门店员工 DTO/命令/查询/验证/处理器完成,支持创建/更新/删除/查询;排班 CRUD(默认未来 7 天)含归属与时间冲突校验;Admin API 增加员工与排班控制器及权限种子,仓储含排班查询/更新/删除。 +- [x] 桌码扫码入口:Mini 端解析二维码,GET /api/mini/tables/{code}/context 返回门店、桌台、公告。 + - 已交付:桌码上下文查询 DTO/验证/处理器完成,可按桌码返回门店名称/公告/标签与桌台信息;MiniApi 新增 `TablesController` `/context` 端点,仓储支持按桌码查询。 +- [x] 菜品建模:分类、SPU、SKU、规格/加料组、价格策略、媒资 CRUD + 上下架流程;Mini 端可拉取完整 JSON。 + - 已交付:Admin 侧补齐 SKU/规格/加料/媒资/定价替换命令、验证与端点,并新增上/下架接口与全量详情;权限种子补充 `product:publish` 与子资源读写。Mini 侧新增门店菜单接口,按门店返回分类 + 商品全量 JSON(含 SKU/规格/加料/媒资/定价),支持 `updatedAfter` 增量。 +- [x] 库存体系:SKU 库存、批次、调整、售罄管理,支持预售/档期锁定并在订单中扣减/释放。 + - 已交付:库存模型补充预售/限购/并发字段与批次策略(FIFO/FEFO),新增锁定记录与幂等、过期释放;应用层提供调整/锁定/释放/扣减/批次维护命令与查询,Admin API 暴露库存与批次端点及权限种子。需后续生成迁移落库,并可按需将过期释放接入定时任务。 +- [x] 自提档期:门店配置自提时间窗、容量、截单时间;Mini 端据此限制下单时间。 + - 已交付:新增自提设置与档期实体/表、并发控制,Admin 端提供自提配置与档期 CRUD 权限/接口;Mini 端提供按日期查询可用档期,包含截单与容量校验。下单限制待后续与订单流程联调。 +- [ ] 购物车服务:ShoppingCart/CartItem/CartItemAddon API 支持并发锁、限购、券/积分预校验,保证并发无脏数据。 + - 当前:领域层与表结构已有 `ShoppingCart/CartItem/CartItemAddon`,但缺少 CQRS 命令/查询、并发锁/限购/券积分预校验以及任何 Admin/Mini 端接口。 +- [ ] 订单与支付:堂食/自提/配送下单、微信/支付宝支付、优惠券/积分抵扣、订单状态机与通知链路齐全。 + - 当前:Admin 端 `OrdersController`/`PaymentsController` 仅提供基础 CRUD,未覆盖堂食/自提/配送业务流、微信/支付宝支付、优惠券/积分抵扣、订单状态机、通知链路及与库存/配送的集成,Mini 端也无下单/支付接口。 +- [ ] 桌台账单:合单/拆单、结账、电子小票、桌台释放,完成结账后恢复 Idle 并生成票据 URL。 + - 当前:无桌台账单/合单/拆单/结账或电子小票逻辑,桌台仅有基础实体定义。 +- [ ] 自配送骨架:骑手管理、取送件信息录入、费用补贴记录,Admin 端可派单并更新 DeliveryOrder。 + - 当前:`DeliveryOrder` CRUD 支持录入 `CourierName/Phone`,但缺少骑手管理、派单流程、取送件详情与补贴记录等自配送骨架。 +- [ ] 第三方配送抽象:统一下单/取消/加价/查询接口,支持达达、美团、闪送等,含回调验签与异常补偿骨架。 + - 当前:尚未提供第三方配送抽象、回调验签或补偿逻辑,配送模块仅有基础 CRUD。 +- [ ] 预购自提核销:提货码生成、手机号/二维码核销、自提柜/前台流程,超时自动取消或退款,记录操作者与时间。 + - 当前:存在 `Reservation` 实体及订单字段 `ReservationId/CheckInCode`,但未实现提货码生成、核销接口、超时取消/退款或核销人记录,未与订单支付联动。 +- [ ] 指标与日志:Prometheus 输出订单创建、支付成功率、配送回调耗时等,Grafana ≥8 个图表;关键流程日志记录 TraceId + 业务 ID。 + - 当前:Admin/Mini/User API 与网关已接入 OpenTelemetry(OTLP 与 Prometheus 导出)和 TraceId 结构化日志,但缺少订单/支付/配送等业务指标定义、Prometheus 爬取路径说明及 Grafana 图表配置。 +- [ ] 测试:Phase 1 核心 API 具备 ≥30 条自动化用例(单元 + 集成),覆盖租户→商户→下单链路。 + - 当前:仓库尚无自动化测试项目/用例,Phase 1 链路未覆盖 xUnit/Moq/FluentAssertions 的单元或集成测试。 +--- + +## Phase 2(下一阶段):拼单、优惠券与基础营销、会员积分/会员日、客服聊天、同城自配送调度、搜索 +- [ ] 拼单引擎:GroupOrder/Participant CRUD、发起/加入/成团条件、自动解散与退款、团内消息与提醒。 +- [ ] 优惠券与基础营销:模板管理、领券、核销、库存/有效期/叠加规则,基础抽奖/秒杀/满减活动。 +- [ ] 会员与积分:会员档案、等级/成长值、会员日通知;积分获取/消耗、有效期、黑名单。 +- [ ] 客服聊天:实时会话、机器人/人工切换、排队/转接、消息模板、敏感词审查、工单流转与评价。 +- [ ] 同城自配送调度:骑手智能指派、路线估时、无接触配送、费用补贴策略、调度看板。 +- [ ] 搜索:门店/菜品/活动/优惠券搜索,过滤/排序、热门/历史记录、联想与纠错。 +--- + +## Phase 3:分销返利、签到打卡、预约预订、地图导航、社区、高阶营销、风控与补偿 +- [ ] 分销返利:AffiliatePartner/Order/Payout 管理,佣金阶梯、结算周期、税务信息、违规处理。 +- [ ] 签到打卡:CheckInCampaign/Record、连签奖励、补签、积分/券/成长值奖励、反作弊机制。 +- [ ] 预约预订:档期/资源占用、预约下单/支付、提醒/改期/取消、到店核销与履约记录。 +- [ ] 地图导航扩展:附近门店/推荐、距离/路线规划、跳转原生导航、导航请求埋点。 +- [ ] 社区:动态发布、评论、点赞、话题/标签、图片/视频审核、举报与风控,店铺口碑展示。 +- [ ] 高阶营销:秒杀/抽奖/裂变、裂变海报、爆款推荐位、多渠道投放分析。 +- [ ] 风控与审计:黑名单、频率限制、异常行为监控、审计日志、补偿与告警体系。 +--- + +## Phase 4:性能优化、缓存、运营大盘、测试与文档、上线与监控 +- [ ] 性能与缓存:热点接口缓存、慢查询治理、批处理优化、异步化改造。 +- [ ] 可靠性:幂等与重试策略、任务调度补偿、链路追踪、告警联动。 +- [ ] 运营大盘:交易/营销/履约/用户维度的细分报表、GMV/成本/毛利分析。 +- [ ] 文档与测试:完整测试矩阵、性能测试报告、上线手册、回滚方案。 +- [ ] 监控与运维:上线发布流程、灰度/回滚策略、系统稳定性指标、24x7 监控与告警。 diff --git a/Document/13_AppSeed说明.md b/Document/13_AppSeed说明.md new file mode 100644 index 0000000..95e2d0e --- /dev/null +++ b/Document/13_AppSeed说明.md @@ -0,0 +1,62 @@ +# App 数据种子使用说明(App:Seed) +> 作用:在启动时自动创建默认租户与基础字典,便于本地/测试环境快速落地必备数据。由 `AppDataSeeder` 执行,支持幂等多次运行。 + +## 配置入口 +- 文件位置:`appsettings.Seed.{Environment}.json`(AdminApi 下新增独立种子文件,示例已写入 Development) +- 配置节:`App:Seed` + +示例(已写入 `src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.Development.json`): +```json +{ + "App": { + "Seed": { + "Enabled": true, + "DefaultTenant": { + "TenantId": 1000000000001, + "Code": "demo", + "Name": "Demo租户", + "ShortName": "Demo", + "ContactName": "DemoAdmin", + "ContactPhone": "13800000000" + }, + "DictionaryGroups": [ + { + "Code": "order_status", + "Name": "订单状态", + "Scope": "Business", + "Items": [ + { "Key": "pending", "Value": "待支付", "SortOrder": 10 }, + { "Key": "paid", "Value": "已支付", "SortOrder": 20 }, + { "Key": "finished", "Value": "已完成", "SortOrder": 30 } + ] + }, + { + "Code": "store_tags", + "Name": "门店标签", + "Scope": "Business", + "Items": [ + { "Key": "hot", "Value": "热门", "SortOrder": 10 }, + { "Key": "new", "Value": "新店", "SortOrder": 20 } + ] + } + ] + } + } +} +``` + +## 字段说明 +- `Enabled`: 是否启用种子 +- `DefaultTenant`: 默认租户(使用雪花 long ID;0 表示让雪花生成) +- `DictionaryGroups`: 基础字典,`Scope` 可选 `System`/`Business`,`Items` 支持幂等运行更新 + +## 运行方式 +1. 确保 Admin API 已调用 `AddAppInfrastructure`(Program.cs 已注册,会启用 `AppDataSeeder`)。 +2. 修改 `appsettings.Seed.{Environment}.json` 的 `App:Seed` 后,启动 Admin API,即会自动执行种子逻辑(幂等)。 +3. 查看日志:`AppSeed` 前缀会输出创建/更新结果。 + +## 注意事项 +- ID 必须用 long(雪花),不要再使用 Guid/自增。 +- 系统租户使用 `TenantId = 0`;业务租户请填写实际雪花 ID。 +- 字典分组编码需唯一;重复运行会按编码合并更新。 +- 生产环境请按需开启 `Enabled`,避免误写入。 diff --git a/Document/14_OpenTelemetry接入指引.md b/Document/14_OpenTelemetry接入指引.md new file mode 100644 index 0000000..bdfcd50 --- /dev/null +++ b/Document/14_OpenTelemetry接入指引.md @@ -0,0 +1,40 @@ +# 14_OpenTelemetry 接入指引 + +> 现状:Admin/Mini/User API 已集成 OTel 埋点,可导出到 Collector/控制台/文件日志,默认关闭 OTLP 导出。 + +## 1. 依赖与版本 +- NuGet:`OpenTelemetry.Extensions.Hosting`、`OpenTelemetry.Instrumentation.AspNetCore`、`OpenTelemetry.Instrumentation.Http`、`OpenTelemetry.Instrumentation.EntityFrameworkCore`、`OpenTelemetry.Instrumentation.Runtime`、`OpenTelemetry.Exporter.OpenTelemetryProtocol`、`OpenTelemetry.Exporter.Console`。 +- 当前 EF Core instrumentation 由 NuGet 回退到 `1.10.0-beta.1`(会提示 NU1603/NU1902),待可用时统一升级到稳定版以消除告警。 + +## 2. 程序内配置(Admin/Mini/User API) +- Resource:`ServiceName` 分别为 `TakeoutSaaS.AdminApi|MiniApi|UserApi`,`ServiceInstanceId = Environment.MachineName`。 +- Tracing:开启 ASP.NET Core、HttpClient、EF Core(禁用 SQL 文本)、Runtime;采样器默认 `ParentBased + AlwaysOn`。 +- Metrics:开启 ASP.NET Core、HttpClient、Runtime。 +- Exporter: + - OTLP(可选):读取 `Otel:Endpoint`,非空时启用。 + - Console:`Otel:UseConsoleExporter`(默认 Dev 开启,Prod 关闭)。 +- 日志:Serilog 输出 Console + 文件(按天滚动,保留 7 天),模板已包含 TraceId/SpanId(通过 Enrich FromLogContext)。 + +## 3. appsettings 配置键 +```json +"Otel": { + "Endpoint": "", // 为空则不推 OTLP,例如 http://otel-collector:4317 + "Sampling": "ParentBasedAlwaysOn", + "UseConsoleExporter": true // Dev 默认 true,Prod 建议 false +} +``` +- 环境变量可覆盖:`OTEL_SERVICE_NAME`、`OTEL_EXPORTER_OTLP_ENDPOINT` 等。 + +## 4. Collector/后端接入建议 +- Collector 监听 4317/4318(gRPC/HTTP OTLP),做采样/脱敏/分流,再转发 Jaeger/Tempo/ELK/Datadog 等。 +- 生产注意:限制导出 SQL 文本(已关闭)、对敏感字段脱敏,必要时在 Collector 做 TraceIdRatioBased 采样以控量。 + +## 5. 验证步骤 +1) 开启 `Otel:UseConsoleExporter=true`,本地运行 API,观察控制台是否输出 Span/Metric。 +2) 配置 `Otel:Endpoint=http://localhost:4317` 并启动 Collector,使用 Jaeger/Tempo UI 或 `curl http://localhost:4318/v1/traces` 验证链路。 +3) 文件日志:查看 `logs/admin-api-*.log` 等,确认包含 TraceId/SpanId。 + +## 6. 后续工作 +- 待 NuGet 源更新后,升级到稳定版 OTel 包并消除 NU1603/NU1902 告警。 +- 如需采集日志到 ELK,可直接用 Filebeat/Vector 读取 `logs/*.log` 推送,无需改代码。 +- 如需控制采样率或关闭某些 instrumentation,调整 appsettings 中的 Sampling/开关后重启即可。 diff --git a/Document/15_API边界与自检清单.md b/Document/15_API边界与自检清单.md new file mode 100644 index 0000000..53e284a --- /dev/null +++ b/Document/15_API边界与自检清单.md @@ -0,0 +1,53 @@ +# API 边界与自检清单 + +> 目的:明确 Admin/User/Mini 三个 API 的职责边界,避免跨端耦合。开发新接口或改动现有控制器时请对照自检,确保租户、安全、DTO、路由符合约定。 + +## 1. AdminApi(管理后台) +- **面向对象**:运营、客服、商户管理员。 +- **职责**:租户/门店/商品/订单/支付/配送/字典/权限/RBAC/审计/任务调度等后台管理与洞察。 +- **鉴权**:JWT + RBAC(`[Authorize]` + `PermissionAuthorize`),必须带租户头 `X-Tenant-Id/Code`。 +- **路由前缀**:`api/admin/v{version}/...`。 +- **DTO/约束**:仅管理字段,禁止返回 C 端敏感信息;long -> string;严禁实体直接返回。 +- **现有控制器**:`AuthController`、`DeliveriesController`、`DictionaryController`、`FilesController`、`MerchantsController`、`OrdersController`、`PaymentsController`、`PermissionsController`、`RolesController`、`StoresController`、`SystemParametersController`、`TenantPackagesController`、`TenantsController`、`TenantBillingsController`、`TenantAnnouncementsController`、`TenantNotificationsController`、`UserPermissionsController`、`HealthController`。 +- **自检清单**: + 1. 是否需要权限/租户过滤?未加则补 `[Authorize]` + 租户解析。 + 2. 是否调用了应用层 CQRS,而非在 Controller 写业务? + 3. DTO 是否按管理口径,未暴露用户端字段? + 4. 是否使用参数化/AsNoTracking/投影,避免 N+1? + 5. 路由和 Swagger 示例是否含租户/权限说明? +- **自检记录**:RolesController 新增模板列表/详情/复制/初始化端点,均已套用 `[Authorize]` + `PermissionAuthorize`、仅调用 CQRS/DTO,依赖租户头隔离。TenantPackagesController 与 TenantsController(配额校验) 均使用权限码、DTO 映射,配额校验要求携带租户头防越权。新增租户账单/公告/通知控制器,全部采用 CQRS、权限校验与租户参数,列表分页、未暴露实体。 + +## 2. UserApi(C 端用户) +- **面向对象**:App/H5 普通用户。 +- **职责**:菜单浏览、下单、支付、评价、地址、售后、订单查询、支付/配送回调(验证签名)等用户闭环。 +- **鉴权**:用户 JWT,租户隔离;幂等接口需校验。 +- **路由前缀**:`api/user/v{version}/...`。 +- **DTO/约束**:仅用户侧可见字段,屏蔽后台配置字段;long -> string。 +- **现有控制器**:当前仅 `HealthController`(业务接口待补)。 +- **自检清单**: + 1. 是否暴露给用户的纯前台功能?后台配置请放 AdminApi。 + 2. 是否做租户隔离、用户鉴权、签名/幂等校验? + 3. 响应是否脱敏且只含用户需要的字段? + 4. 是否避免跨端复用后台 DTO/命令? + 5. 回调路由是否验证签名/防重放? + +## 3. MiniApi(小程序端) +- **面向对象**:微信/小程序前端。 +- **职责**:小程序登录/刷新、当前用户档案、订阅消息、直传凭证、小程序场景特定的下单/浏览等。 +- **鉴权**:小程序登录态/Token,租户隔离;必要时区分渠道。 +- **路由前缀**:`api/mini/v{version}/...`。 +- **DTO/约束**:遵循小程序接口规范,错误码与前端对齐;long -> string。 +- **现有控制器**:`AuthController`、`MeController`、`FilesController`、`HealthController`。 +- **自检清单**: + 1. 是否为小程序特有流程(code2session、订阅消息、直传等)?通用用户接口放 UserApi。 + 2. 是否完成租户/鉴权校验,区分渠道标识? + 3. 请求/响应是否符合小程序对错误码与字段的约定? + 4. 是否避免使用后台管理 DTO/权限模型? + 5. 上传/直传接口是否限制 MIME/大小并做鉴权? + +## 4. 共通约束 +- **分层**:Controller 仅做路由/DTO 转换,业务放 Application 层 Handler。 +- **租户**:所有写/读需租户过滤;严禁跨租户访问。 +- **日志/观测**:TraceId/SpanId 已贯通;/metrics、/healthz 按服务暴露。 +- **命名**:输入 `XxxRequest`、输出 `XxxDto`;文件名与类名一致;布尔属性加 `Is/Has`。 +- **发布前检查**:运行 `dotnet build`,必要时补 Swagger 示例、单元测试(核心逻辑 100% 覆盖,服务 ≥70%)。 diff --git a/Document/Completed/后端套餐管理.md b/Document/Completed/后端套餐管理.md new file mode 100644 index 0000000..23c0496 --- /dev/null +++ b/Document/Completed/后端套餐管理.md @@ -0,0 +1,79 @@ +# TakeoutSaaS 开发 TODO(自动维护) + +> 说明:该文档用于记录本仓库待办与进度。我会在完成每个任务后更新标记状态,并尽量保持“一个功能点一个原子提交”。 +> +> 状态标记:[x] 已完成 / [~] 部分完成 / [ ] 未开始 + +## 一期:套餐管理 MVP(已落地 + 待收口) + +[x] 1. 套餐增删改:新建/编辑/复制套餐,支持草稿保存 + +- [x] 新建套餐(表单:基础信息 + 定价 + 权益配额) +- [x] 编辑套餐(同新增表单) +- [x] 复制套餐(基于现有套餐快速创建新套餐) +- [x] 删除套餐(软删) +- [x] 草稿保存(草稿/发布状态、草稿发布、回滚草稿) +- [x] 修复“保存草稿却变发布”根因(EF 默认值哨兵导致 insert 省略字段,已将发布状态默认值调整为草稿并补齐哨兵配置) + +[x] 2. 上架体系:上架/下架、是否对外可见、是否允许新租户购买 + +- [x] 上架/下架(启用/禁用套餐,含二次确认与提示) +- [x] 是否对外可见(展示与可售解耦) +- [x] 是否允许新租户购买(可售开关与可见开关解耦) +- [x] 修复“新增时开关 false 无法落库”根因(EF 默认值哨兵导致 insert 省略字段,已将哨兵改为 true) + +[~] 3. 价格与计费周期:月付/年付、阶梯价/按量计费 + +- [x] 月付/年付价格(`monthlyPrice` / `yearlyPrice`) +- [ ] 阶梯价/按量计费 + +[x] 4. 权益/配额配置:功能开关 + 数值配额(门店/账号/存储/短信/配送单量/更多) + +- [x] 数值配额(门店数、账号数、存储、短信、配送单量) +- [x] 功能策略(`featurePoliciesJson`:可视化编辑 + JSON 预览,保留未知字段) +- [x] 商品/菜单上限 +- [x] API 调用次数 +- [x] 报表/导出权限 +- [x] 打印/小票能力 +- [x] 营销功能(优惠券/满减/会员/积分等开关) + +[~] 5. 展示配置:卖点文案、推荐标识、排序、标签、对比页字段 + +- [x] 排序(`sortOrder`) +- [~] 卖点/描述(`description`) +- [x] 推荐标识(Recommended) +- [x] 标签(推荐/性价比/旗舰) +- [ ] (已移除)对比页展示字段配置(对比维度/顺序) +- [ ] 自助入驻选套餐页展示推荐/标签(公共套餐列表接口返回 isRecommended/tags + 前端展示/置顶) + +[x] 6. 订阅关联视图:当前使用该套餐的租户数量、MRR/ARR 粗看、到期分布(运营常用) + +- [x] 订阅/租户数量:活跃订阅数、总订阅数、使用租户数 +- [x] 使用租户列表入口:抽屉列表(支持分页与搜索) +- [x] MRR/ARR 粗看 +- [x] 到期分布(7/15/30 天到期租户数、到期列表入口) + +[x] 7. 后端接口补齐(套餐使用统计/使用租户分页查询等) + +[x] 8. 前端联调与回归(包含权限、空态、错误提示) + +## 二期:上架与配置完善(建议) + +- [x] 1. 草稿保存与发布流程(草稿/发布、回滚到草稿) +- [x] 2. 可见性/可售开关拆分(对外可见、允许新租户购买、已订阅不受影响说明) +- [x] 4. 权益/配额可视化编辑器(JSON 结构化编辑、Schema 校验、预设模板) +- [x] 5. 更多常用配额字段补齐(商品/菜单、API 次数、导出/报表、打印等) + +## 三期:计费与权益策略(建议) + +- [ ] 1. 阶梯价/按量计费(超配计费、账单明细) +- [ ] 2. 权益变更影响说明:升配/降配规则(立即/次周期)、影响范围提示 +- [ ] 3. 超配策略(禁止/只读/按量计费/宽限期) + +## 四期:商业化与合规增强(建议) + +- [ ] 1. 附加计费(Add-ons):短信包、存储包、额外门店/账号包、配送单量包(可叠加、单独定价) +- [ ] 2. 版本与历史:套餐版本号、变更记录、回滚、对已订阅租户的影响范围提示 +- [ ] 3. 购买限制:可购买地区/行业、仅邀请可见、最大购买数量、是否允许叠加订阅 +- [ ] 4. 订阅关联视图增强:MRR/ARR、到期分布、续费转化漏斗(运营常用) +- [ ] 5. 操作审计:谁改了套餐、何时改、改了什么(合规必备) diff --git a/Document/README.md b/Document/README.md new file mode 100644 index 0000000..aa08578 --- /dev/null +++ b/Document/README.md @@ -0,0 +1,195 @@ +# 外卖SaaS系统 - 文档中心 + +欢迎查阅外卖SaaS系统的完整文档。本文档中心包含了项目的所有技术文档和开发指南。 + +## 📚 文档目录 + +### 1. [项目概述](01_项目概述.md) +- 项目简介与背景 +- 核心业务模块介绍 +- 用户角色说明 +- 系统特性 +- 技术选型 +- 项目里程碑 + +**适合人群**:项目经理、产品经理、新加入的开发人员 + +--- + +### 2. [技术架构](02_技术架构.md) +- 技术栈详解 +- 系统架构设计 +- 分层架构说明 +- 核心设计模式 +- 数据访问策略(EF Core + Dapper) +- 缓存策略 +- 消息队列应用 +- 安全设计 + +**适合人群**:架构师、技术负责人、高级开发人员 + +--- + +### 3. [数据库设计](03_数据库设计.md) +- 数据库设计原则 +- 命名规范 +- 核心表结构 + - 租户管理 + - 商家管理 + - 菜品管理 + - 订单管理 + - 配送管理 + - 支付管理 + - 营销管理 + - 系统管理 +- 索引策略 +- 数据库优化 +- 备份策略 + +**适合人群**:数据库管理员、后端开发人员 + +--- + +### 4A. [管理后台 API 设计](04A_管理后台API.md) +- 角色与权限(平台/租户/商家) +- 租户与商家管理 +- 菜品与分类管理 +- 订单流转与售后 +- 优惠券与评价管理 +- 统计报表与文件上传 + +### 4B. [小程序/用户端 API 设计](04B_小程序API.md) +- 小程序登录与用户信息 +- 商家与门店浏览 +- 菜品与分类列表 +- 购物车同步 +- 订单创建/查询/取消 +- 支付对接(微信/支付宝) +- 优惠券领取与使用、评价发布 + +**适合人群**:前端开发人员(小程序/Web用户端)、后端开发人员、接口对接人员 + +--- + +### 5. [部署运维](05_部署运维.md) +- 环境要求 +- 本地开发环境搭建 +- Docker部署 +- Nginx配置 +- 数据库部署(主从复制) +- Redis部署(哨兵模式) +- CI/CD配置 +- 监控告警(Prometheus + Grafana) +- 日志管理(ELK Stack) +- 安全加固 +- 性能优化 +- 故障恢复 + +**适合人群**:运维工程师、DevOps工程师、系统管理员 + +--- + +### 6. [开发规范](06_开发规范.md) +- 代码规范 + - 命名规范 + - 代码组织 + - 代码注释 + - 异常处理 +- Git工作流 + - 分支管理 + - 提交信息规范 +- 代码审查标准 +- 单元测试规范 +- 性能优化规范 +- 安全规范 +- 日志规范 +- 配置管理 +- API设计规范 + +**适合人群**:所有开发人员 + +--- + +### 7. [系统架构图](07_系统架构图.md) +- 整体架构图 +- 应用分层架构 +- 订单处理流程图 +- 数据流转图 +- 多租户数据隔离架构 +- 缓存架构 +- 消息队列架构 +- 部署架构 +- 监控架构 + +**适合人群**:架构师、技术负责人、所有开发人员 + +--- + +## 🚀 快速导航 + +### 我是新人,从哪里开始? +1. 先阅读 [项目概述](01_项目概述.md) 了解项目背景和业务 +2. 查看 [系统架构图](07_系统架构图.md) 理解系统整体架构 +3. 阅读 [开发规范](06_开发规范.md) 了解开发要求 +4. 参考 [部署运维](05_部署运维.md) 搭建本地开发环境 + +### 我要开发新功能 +1. 查看 [数据库设计](03_数据库设计.md) 了解数据模型 +2. 参考 [API接口设计](04_API接口设计.md) 设计接口 +3. 遵循 [开发规范](06_开发规范.md) 编写代码 +4. 参考 [技术架构](02_技术架构.md) 选择合适的技术方案 + +### 我要部署系统 +1. 阅读 [部署运维](05_部署运维.md) 了解部署流程 +2. 参考 [系统架构图](07_系统架构图.md) 理解部署架构 +3. 按照文档配置监控和日志系统 + +### 我要对接API +1. 查看 [API接口设计](04_API接口设计.md) 了解接口规范 +2. 参考接口文档进行开发和测试 + +--- + +## 📖 文档更新记录 + +### v1.0.0 (2024-01-01) +- ✅ 完成项目概述文档 +- ✅ 完成技术架构文档 +- ✅ 完成数据库设计文档 +- ✅ 完成API接口设计文档 +- ✅ 完成部署运维文档 +- ✅ 完成开发规范文档 +- ✅ 完成系统架构图文档 + +--- + +## 💡 文档贡献 + +如果您发现文档有任何问题或需要改进的地方,欢迎: +1. 提交 Issue 反馈问题 +2. 提交 Pull Request 改进文档 +3. 联系项目负责人 + +--- + +## 📞 联系方式 + +- 项目地址:https://github.com/your-org/takeout-saas +- 问题反馈:https://github.com/your-org/takeout-saas/issues +- 邮箱:dev@example.com + +--- + +## 📝 文档规范 + +本文档使用 Markdown 格式编写,遵循以下规范: +- 使用清晰的标题层级 +- 代码示例使用语法高亮 +- 重要内容使用加粗或引用 +- 保持文档简洁易读 +- 及时更新文档内容 + +--- + +**最后更新时间**:2024-01-01 +**文档版本**:v1.0.0 diff --git a/Document/infra/postgres_redis.md b/Document/infra/postgres_redis.md new file mode 100644 index 0000000..bf6e076 --- /dev/null +++ b/Document/infra/postgres_redis.md @@ -0,0 +1,101 @@ +# PostgreSQL 与 Redis 接入手册 + +> 本文档补齐 `Document/10_TODO.md` 中“Postgres/Redis 接入文档与 IaC/脚本”的要求,统一描述连接信息、账号权限、运维流程,以及可复用的部署脚本位置。 + +## 1. 运行环境总览 + +| 组件 | 地址/端口 | 主要数据库/实例 | 说明 | +| --- | --- | --- | --- | +| PostgreSQL | `120.53.222.17:5432` | `takeout_app_db`、`takeout_identity_db`、`takeout_dictionary_db`、`takeout_hangfire_db` | 线上实例,所有业务上下文共用。 | +| Redis | `49.232.6.45:6379` | 单节点 | 业务缓存/登录限流/刷新令牌存储。 | + +> 注意:所有业务账号都只具备既有库的读写权限,无 `CREATEDB`。若需新库,需使用平台管理员账号(`postgres`)或联系 DBA。 + +## 2. 账号与库映射 + +| 数据库 | 角色 | 密码 | 用途 | +| --- | --- | --- | --- | +| `takeout_app_db` | `app_user` | `AppUser112233` | 业务域 (`TakeoutAppDbContext`) | +| `takeout_identity_db` | `identity_user` | `IdentityUser112233` | 身份域 (`IdentityDbContext`) | +| `takeout_dictionary_db` | `dictionary_user` | `DictionaryUser112233` | 字典域 (`DictionaryDbContext`) | +| `takeout_hangfire_db` | `hangfire_user` | `HangFire112233` | 后台调度/Hangfire | + +Redis 密码:`MsuMshk112233`,见 `appsettings.*.json -> Redis`。 + +## 3. 环境变量/配置注入 + +### PowerShell + +```powershell +$env:TAKEOUTSAAS_APPSETTINGS_DIR = "D:\HAZCode\TakeOut\src\Api\TakeoutSaaS.AdminApi" +$env:TAKEOUTSAAS_APP_CONNECTION = "Host=120.53.222.17;Port=5432;Database=takeout_app_db;Username=app_user;Password=AppUser112233;Pooling=true" +$env:TAKEOUTSAAS_IDENTITY_CONNECTION = "Host=120.53.222.17;Port=5432;Database=takeout_identity_db;Username=identity_user;Password=IdentityUser112233;Pooling=true" +$env:TAKEOUTSAAS_DICTIONARY_CONNECTION = "Host=120.53.222.17;Port=5432;Database=takeout_dictionary_db;Username=dictionary_user;Password=DictionaryUser112233;Pooling=true" +``` + +### Bash + +```bash +export TAKEOUTSAAS_APPSETTINGS_DIR=/home/user/TakeOut/src/Api/TakeoutSaaS.AdminApi +export TAKEOUTSAAS_APP_CONNECTION="Host=120.53.222.17;Port=5432;Database=takeout_app_db;Username=app_user;Password=AppUser112233;Pooling=true" +export TAKEOUTSAAS_IDENTITY_CONNECTION="Host=120.53.222.17;Port=5432;Database=takeout_identity_db;Username=identity_user;Password=IdentityUser112233;Pooling=true" +export TAKEOUTSAAS_DICTIONARY_CONNECTION="Host=120.53.222.17;Port=5432;Database=takeout_dictionary_db;Username=dictionary_user;Password=DictionaryUser112233;Pooling=true" +``` + +Redis 连接字符串直接写入 `appsettings.*.json` 即可,如: + +```jsonc +"Redis": "49.232.6.45:6379,password=MsuMshk112233,abortConnect=false" +``` + +## 4. 运维指南 + +### PostgreSQL + +1. **只读账号验证** + ```powershell + psql "host=120.53.222.17 port=5432 dbname=takeout_app_db user=app_user password=AppUser112233" + ``` +2. **备份** + ```bash + pg_dump -h 120.53.222.17 -p 5432 -U postgres -F c -d takeout_app_db -f backup/takeout_app_db_$(date +%Y%m%d).dump + pg_dumpall -h 120.53.222.17 -p 5432 -U postgres > backup/all_$(date +%Y%m%d).sql + ``` +3. **恢复** + ```bash + pg_restore -h 120.53.222.17 -p 5432 -U postgres -d takeout_app_db backup/takeout_app_db_xxx.dump + psql -h 120.53.222.17 -p 5432 -U postgres -f backup/all_yyyymmdd.sql + ``` +4. **账号/权限策略** + - `app_user` / `identity_user` / `dictionary_user` 拥有 `CONNECT`、`TEMP`、Schema `public` 的 CRUD 权限。 + - `hangfire_user` 仅能访问 `takeout_hangfire_db`,不可访问业务库。 + - 创建新表/列时,通过 EF Migration 自动添加 COMMENT。 + +### Redis + +1. **连接验证** + ```bash + redis-cli -h 49.232.6.45 -p 6379 -a MsuMshk112233 ping + ``` +2. **备份** + ```bash + redis-cli -h 49.232.6.45 -p 6379 -a MsuMshk112233 save # 触发 RDB + redis-cli -h 49.232.6.45 -p 6379 -a MsuMshk112233 bgsave # 后台 + ``` + RDB/AOF 文件在服务器 `redis.conf` 定义的目录(默认 `/var/lib/redis`)。 +3. **常见运维项** + - `CONFIG GET dir` / `CONFIG GET dbfilename` 可查看持久化路径。 + - `INFO memory` 监控内存;开启 `maxmemory` + `allkeys-lru` 保护。 + +## 5. IaC / 脚本 + +| 文件 | 说明 | +| --- | --- | +| `deploy/postgres/create_databases.sql` | 基于 `postgres` 管理员执行,创建四个业务库及角色、授予权限、补 COMMENT。 | +| `deploy/postgres/bootstrap.ps1` | PowerShell 包装脚本,调用 `psql` 执行上面的 SQL(默认读取 `postgres` 管理员账号)。 | +| `deploy/postgres/README.md` | 介绍如何在本地/测试环境执行 bootstrap 并校验连接。 | +| `deploy/redis/docker-compose.yml` | 可复用的 Redis 部署(Redis 7 + AOF),便于本地或测试环境一键拉起。 | +| `deploy/redis/redis.conf` | compose/裸机均可共用的配置(`requirepass`、持久化等已写好)。 | +| `deploy/redis/README.md` | 说明如何使用 compose 或将 `redis.conf` 部署到现有实例。 | + +> 线上目前为裸机安装(非容器),如需创建新环境/快速恢复,可直接运行上述脚本达到同样配置;即使在现有机器上,也可把 SQL/配置当作“最终规范”确保环境一致性。 diff --git a/Document/swagger/swagger20251215095917.json b/Document/swagger/swagger20251215095917.json new file mode 100644 index 0000000..d1efe82 --- /dev/null +++ b/Document/swagger/swagger20251215095917.json @@ -0,0 +1,19254 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "外卖SaaS - 管理后台 1.0", + "description": "管理后台 API 文档", + "version": "1.0" + }, + "paths": { + "/api/admin/v1/auth/login": { + "post": { + "tags": [ + "Auth" + ], + "summary": "登录获取 Token", + "requestBody": { + "description": "登录请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TokenResponseApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/auth/login/simple": { + "post": { + "tags": [ + "Auth" + ], + "summary": "免租户号登录(仅账号+密码)。", + "description": "用于前端简化登录,无需额外传递租户号。", + "requestBody": { + "description": "登录请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/AdminLoginRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TokenResponseApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/auth/refresh": { + "post": { + "tags": [ + "Auth" + ], + "summary": "刷新 Token", + "requestBody": { + "description": "刷新令牌请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TokenResponseApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/auth/profile": { + "get": { + "tags": [ + "Auth" + ], + "summary": "获取当前用户信息", + "description": "示例:\n```\nGET /api/admin/v1/auth/profile\nHeader: Authorization: Bearer \n响应:\n{\n \"success\": true,\n \"code\": 200,\n \"message\": \"操作成功\",\n \"data\": {\n \"userId\": \"900123456789012345\",\n \"account\": \"admin@tenant1\",\n \"displayName\": \"租户管理员\",\n \"tenantId\": \"100000000000000001\",\n \"merchantId\": null,\n \"roles\": [\"TenantAdmin\"],\n \"permissions\": [\"identity:permission:read\", \"merchant:read\", \"order:read\"],\n \"avatar\": \"https://cdn.example.com/avatar.png\"\n }\n}\n```", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentUserProfileApiResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentUserProfileApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/auth/menu": { + "get": { + "tags": [ + "Auth" + ], + "summary": "获取当前用户的菜单树(按权限过滤)。", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuNodeDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/auth/permissions/{userId}": { + "get": { + "tags": [ + "Auth" + ], + "summary": "查询指定用户的角色与权限概览(当前租户范围)。", + "description": "示例:\n```\nGET /api/admin/v1/auth/permissions/900123456789012346\nHeader: Authorization: Bearer \n响应:\n{\n \"success\": true,\n \"code\": 200,\n \"data\": {\n \"userId\": \"900123456789012346\",\n \"tenantId\": \"100000000000000001\",\n \"merchantId\": \"200000000000000001\",\n \"account\": \"ops.manager\",\n \"displayName\": \"运营经理\",\n \"roles\": [\"OpsManager\", \"Reporter\"],\n \"permissions\": [\"delivery:read\", \"order:read\", \"payment:read\"],\n \"createdAt\": \"2025-12-01T08:30:00Z\"\n }\n}\n```", + "parameters": [ + { + "name": "userId", + "in": "path", + "description": "目标用户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPermissionDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPermissionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/deliveries": { + "post": { + "tags": [ + "Deliveries" + ], + "summary": "创建配送单。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateDeliveryOrderCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDeliveryOrderCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateDeliveryOrderCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateDeliveryOrderCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeliveryOrderDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Deliveries" + ], + "summary": "查询配送单列表。", + "parameters": [ + { + "name": "orderId", + "in": "query", + "description": "订单 ID。", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "配送状态。", + "schema": { + "$ref": "#/components/schemas/DeliveryStatus" + } + }, + { + "name": "page", + "in": "query", + "description": "页码。", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页大小。", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "description": "排序字段。", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "description": "是否倒序。", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeliveryOrderDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/deliveries/{deliveryOrderId}": { + "get": { + "tags": [ + "Deliveries" + ], + "summary": "获取配送单详情。", + "parameters": [ + { + "name": "deliveryOrderId", + "in": "path", + "description": "配送单 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeliveryOrderDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Deliveries" + ], + "summary": "更新配送单。", + "parameters": [ + { + "name": "deliveryOrderId", + "in": "path", + "description": "配送单 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDeliveryOrderCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDeliveryOrderCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDeliveryOrderCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDeliveryOrderCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeliveryOrderDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Deliveries" + ], + "summary": "删除配送单。", + "parameters": [ + { + "name": "deliveryOrderId", + "in": "path", + "description": "配送单 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/dictionaries": { + "get": { + "tags": [ + "Dictionary" + ], + "summary": "查询字典分组。", + "parameters": [ + { + "name": "Scope", + "in": "query", + "description": "参数字典作用域。", + "schema": { + "$ref": "#/components/schemas/DictionaryScope" + } + }, + { + "name": "IncludeItems", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryGroupDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Dictionary" + ], + "summary": "创建字典分组。", + "requestBody": { + "description": "创建分组请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryGroupRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryGroupRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryGroupRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryGroupRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryGroupDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/dictionaries/{groupId}": { + "put": { + "tags": [ + "Dictionary" + ], + "summary": "更新字典分组。", + "parameters": [ + { + "name": "groupId", + "in": "path", + "description": "分组 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryGroupRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryGroupRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryGroupRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryGroupRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryGroupDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Dictionary" + ], + "summary": "删除字典分组。", + "parameters": [ + { + "name": "groupId", + "in": "path", + "description": "分组 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/dictionaries/{groupId}/items": { + "post": { + "tags": [ + "Dictionary" + ], + "summary": "创建字典项。", + "parameters": [ + { + "name": "groupId", + "in": "path", + "description": "分组 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "创建请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryItemRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryItemRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryItemRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateDictionaryItemRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/dictionaries/items/{itemId}": { + "put": { + "tags": [ + "Dictionary" + ], + "summary": "更新字典项。", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "字典项 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryItemRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryItemRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryItemRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateDictionaryItemRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryItemDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Dictionary" + ], + "summary": "删除字典项。", + "parameters": [ + { + "name": "itemId", + "in": "path", + "description": "字典项 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/dictionaries/batch": { + "post": { + "tags": [ + "Dictionary" + ], + "summary": "批量获取字典项(命中缓存)。", + "requestBody": { + "description": "批量查询请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/DictionaryBatchQueryRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryBatchQueryRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryBatchQueryRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DictionaryBatchQueryRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringDictionaryItemDtoIReadOnlyListIReadOnlyDictionaryApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/files/upload": { + "post": { + "tags": [ + "Files" + ], + "summary": "上传图片或文件。", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "File" + ], + "type": "object", + "properties": { + "File": { + "type": "string", + "description": "上传文件。", + "format": "binary" + }, + "Type": { + "type": "string", + "description": "上传类型。" + } + } + }, + "encoding": { + "File": { + "style": "form" + }, + "Type": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileUploadResponseApiResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileUploadResponseApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/Health": { + "get": { + "tags": [ + "Health" + ], + "summary": "获取服务健康状态。", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/{productSkuId}": { + "get": { + "tags": [ + "Inventory" + ], + "summary": "查询库存。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "productSkuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/adjust": { + "post": { + "tags": [ + "Inventory" + ], + "summary": "调整库存(入库/盘点/报损)。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/AdjustInventoryCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdjustInventoryCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/AdjustInventoryCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/AdjustInventoryCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/lock": { + "post": { + "tags": [ + "Inventory" + ], + "summary": "锁定库存(下单占用)。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/LockInventoryCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/LockInventoryCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LockInventoryCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/LockInventoryCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/release": { + "post": { + "tags": [ + "Inventory" + ], + "summary": "释放库存(取消订单等)。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReleaseInventoryCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReleaseInventoryCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReleaseInventoryCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReleaseInventoryCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/deduct": { + "post": { + "tags": [ + "Inventory" + ], + "summary": "扣减库存(支付或履约成功)。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/DeductInventoryCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeductInventoryCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/DeductInventoryCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/DeductInventoryCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryItemDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/{productSkuId}/batches": { + "get": { + "tags": [ + "Inventory" + ], + "summary": "查询批次列表。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "productSkuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryBatchDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Inventory" + ], + "summary": "新增或更新批次。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "productSkuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpsertInventoryBatchCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertInventoryBatchCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpsertInventoryBatchCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpsertInventoryBatchCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryBatchDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/inventory/locks/expire": { + "post": { + "tags": [ + "Inventory" + ], + "summary": "释放过期锁定。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Int32ApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/menus": { + "get": { + "tags": [ + "Menus" + ], + "summary": "获取当前租户的菜单列表(平铺)。", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Menus" + ], + "summary": "创建菜单。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateMenuCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMenuCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateMenuCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateMenuCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/menus/{menuId}": { + "get": { + "tags": [ + "Menus" + ], + "summary": "获取菜单详情。", + "parameters": [ + { + "name": "menuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Menus" + ], + "summary": "更新菜单。", + "parameters": [ + { + "name": "menuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MenuDefinitionDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Menus" + ], + "summary": "删除菜单。", + "parameters": [ + { + "name": "menuId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchant-categories": { + "get": { + "tags": [ + "MerchantCategories" + ], + "summary": "列出所有类目。", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantCategoryDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "MerchantCategories" + ], + "summary": "新增类目。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCategoryCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCategoryCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCategoryCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCategoryCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantCategoryDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchant-categories/{categoryId}": { + "delete": { + "tags": [ + "MerchantCategories" + ], + "summary": "删除类目。", + "parameters": [ + { + "name": "categoryId", + "in": "path", + "description": "类目 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchant-categories/reorder": { + "post": { + "tags": [ + "MerchantCategories" + ], + "summary": "批量调整类目排序。", + "requestBody": { + "description": "排序命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReorderMerchantCategoriesCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReorderMerchantCategoriesCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReorderMerchantCategoriesCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReorderMerchantCategoriesCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants": { + "post": { + "tags": [ + "Merchants" + ], + "summary": "创建商户。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Merchants" + ], + "summary": "查询商户列表。", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "状态筛选。", + "schema": { + "$ref": "#/components/schemas/MerchantStatus" + } + }, + { + "name": "page", + "in": "query", + "description": "页码。", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页大小。", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "description": "排序字段。", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "description": "是否倒序。", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}": { + "put": { + "tags": [ + "Merchants" + ], + "summary": "更新商户。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "description": "商户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Merchants" + ], + "summary": "删除商户。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "description": "商户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Merchants" + ], + "summary": "获取商户概览。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "description": "商户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/detail": { + "get": { + "tags": [ + "Merchants" + ], + "summary": "获取商户详细资料(含证照、合同)。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDetailDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/documents": { + "post": { + "tags": [ + "Merchants" + ], + "summary": "上传商户证照信息(先通过文件上传接口获取 COS 地址)。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/AddMerchantDocumentCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddMerchantDocumentCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/AddMerchantDocumentCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/AddMerchantDocumentCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDocumentDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Merchants" + ], + "summary": "商户证照列表。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDocumentDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/documents/{documentId}/review": { + "post": { + "tags": [ + "Merchants" + ], + "summary": "审核指定证照。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "documentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantDocumentCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantDocumentCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantDocumentCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantDocumentCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDocumentDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/contracts": { + "post": { + "tags": [ + "Merchants" + ], + "summary": "新增商户合同。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantContractCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantContractCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantContractCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateMerchantContractCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantContractDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Merchants" + ], + "summary": "合同列表。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantContractDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/contracts/{contractId}/status": { + "put": { + "tags": [ + "Merchants" + ], + "summary": "更新合同状态(生效/终止等)。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "contractId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantContractStatusCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantContractStatusCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantContractStatusCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateMerchantContractStatusCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantContractDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/review": { + "post": { + "tags": [ + "Merchants" + ], + "summary": "审核商户(通过/驳回)。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReviewMerchantCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/{merchantId}/audits": { + "get": { + "tags": [ + "Merchants" + ], + "summary": "审核日志。", + "parameters": [ + { + "name": "merchantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MerchantAuditLogDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/merchants/categories": { + "get": { + "tags": [ + "Merchants" + ], + "summary": "可选商户类目列表。", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/orders": { + "post": { + "tags": [ + "Orders" + ], + "summary": "创建订单。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateOrderCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrderCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateOrderCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateOrderCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrderDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Orders" + ], + "summary": "查询订单列表。", + "parameters": [ + { + "name": "storeId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "订单状态。", + "schema": { + "$ref": "#/components/schemas/OrderStatus" + } + }, + { + "name": "paymentStatus", + "in": "query", + "description": "支付记录状态。", + "schema": { + "$ref": "#/components/schemas/PaymentStatus" + } + }, + { + "name": "orderNo", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrderDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/orders/{orderId}": { + "get": { + "tags": [ + "Orders" + ], + "summary": "获取订单详情。", + "parameters": [ + { + "name": "orderId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrderDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Orders" + ], + "summary": "更新订单。", + "parameters": [ + { + "name": "orderId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateOrderCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateOrderCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateOrderCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateOrderCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrderDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Orders" + ], + "summary": "删除订单。", + "parameters": [ + { + "name": "orderId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/payments": { + "post": { + "tags": [ + "Payments" + ], + "summary": "创建支付记录。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreatePaymentCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePaymentCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreatePaymentCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreatePaymentCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Payments" + ], + "summary": "查询支付记录列表。", + "parameters": [ + { + "name": "orderId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "支付记录状态。", + "schema": { + "$ref": "#/components/schemas/PaymentStatus" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/payments/{paymentId}": { + "get": { + "tags": [ + "Payments" + ], + "summary": "获取支付记录详情。", + "parameters": [ + { + "name": "paymentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Payments" + ], + "summary": "更新支付记录。", + "parameters": [ + { + "name": "paymentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdatePaymentCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePaymentCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePaymentCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdatePaymentCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Payments" + ], + "summary": "删除支付记录。", + "parameters": [ + { + "name": "paymentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/permissions": { + "get": { + "tags": [ + "Permissions" + ], + "summary": "分页查询权限。", + "description": "示例:GET /api/admin/v1/permissions?keyword=order&page=1&pageSize=20", + "parameters": [ + { + "name": "Keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "SortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "SortDescending", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoPagedResultApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Permissions" + ], + "summary": "创建权限。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreatePermissionCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePermissionCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreatePermissionCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreatePermissionCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/permissions/tree": { + "get": { + "tags": [ + "Permissions" + ], + "summary": "获取权限树。", + "parameters": [ + { + "name": "keyword", + "in": "query", + "description": "关键字(可选)。", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionTreeDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/permissions/{permissionId}": { + "put": { + "tags": [ + "Permissions" + ], + "summary": "更新权限。", + "parameters": [ + { + "name": "permissionId", + "in": "path", + "description": "权限 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdatePermissionCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePermissionCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePermissionCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdatePermissionCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Permissions" + ], + "summary": "删除权限。", + "parameters": [ + { + "name": "permissionId", + "in": "path", + "description": "权限 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products": { + "post": { + "tags": [ + "Products" + ], + "summary": "创建商品。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateProductCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateProductCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateProductCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateProductCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Products" + ], + "summary": "查询商品列表。", + "parameters": [ + { + "name": "storeId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "categoryId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "商品状态。", + "schema": { + "$ref": "#/components/schemas/ProductStatus" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}": { + "get": { + "tags": [ + "Products" + ], + "summary": "获取商品详情。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Products" + ], + "summary": "更新商品。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateProductCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProductCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProductCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateProductCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Products" + ], + "summary": "删除商品。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/detail": { + "get": { + "tags": [ + "Products" + ], + "summary": "获取商品全量详情。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDetailDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/publish": { + "post": { + "tags": [ + "Products" + ], + "summary": "上架商品。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/PublishProductCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublishProductCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PublishProductCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PublishProductCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/unpublish": { + "post": { + "tags": [ + "Products" + ], + "summary": "下架商品。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UnpublishProductCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnpublishProductCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UnpublishProductCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UnpublishProductCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/skus": { + "put": { + "tags": [ + "Products" + ], + "summary": "替换商品 SKU。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductSkusCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductSkusCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductSkusCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductSkusCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSkuDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/attributes": { + "put": { + "tags": [ + "Products" + ], + "summary": "替换商品规格。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAttributesCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAttributesCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAttributesCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAttributesCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductAttributeGroupDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/addons": { + "put": { + "tags": [ + "Products" + ], + "summary": "替换商品加料。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAddonsCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAddonsCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAddonsCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductAddonsCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductAddonGroupDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/media": { + "put": { + "tags": [ + "Products" + ], + "summary": "替换商品媒资。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductMediaCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductMediaCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductMediaCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductMediaCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductMediaAssetDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/products/{productId}/pricing-rules": { + "put": { + "tags": [ + "Products" + ], + "summary": "替换商品价格策略。", + "parameters": [ + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductPricingRulesCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductPricingRulesCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductPricingRulesCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReplaceProductPricingRulesCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductPricingRuleDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/public/v1/tenant-packages": { + "get": { + "tags": [ + "PublicTenantPackages" + ], + "summary": "分页获取已启用的租户套餐。", + "parameters": [ + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/public/v1/tenants/self-register": { + "post": { + "tags": [ + "PublicTenants" + ], + "summary": "自助注册租户并生成初始管理员。", + "requestBody": { + "description": "自助注册命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/SelfRegisterTenantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/SelfRegisterTenantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SelfRegisterTenantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SelfRegisterTenantCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SelfRegisterResultDtoApiResponse" + } + } + } + } + } + } + }, + "/api/public/v1/tenants/{tenantId}/verification": { + "post": { + "tags": [ + "PublicTenants" + ], + "summary": "自助提交或更新实名资料。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "description": "租户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "实名资料。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantVerificationDtoApiResponse" + } + } + } + } + } + } + }, + "/api/public/v1/tenants/{tenantId}/status": { + "get": { + "tags": [ + "PublicTenants" + ], + "summary": "查询租户入住进度。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "description": "租户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantProgressDtoApiResponse" + } + } + } + } + } + } + }, + "/api/public/v1/tenants/{tenantId}/subscriptions/initial": { + "post": { + "tags": [ + "PublicTenantSubscriptions" + ], + "summary": "初次绑定租户订阅(默认 0 个月)。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "description": "租户 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "绑定请求。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/BindInitialTenantSubscriptionCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/BindInitialTenantSubscriptionCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/BindInitialTenantSubscriptionCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/BindInitialTenantSubscriptionCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates": { + "get": { + "tags": [ + "RoleTemplates" + ], + "summary": "分页查询角色模板。", + "parameters": [ + { + "name": "isActive", + "in": "query", + "description": "是否启用筛选。", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "RoleTemplates" + ], + "summary": "创建角色模板。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleTemplateCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleTemplateCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleTemplateCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleTemplateCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates/{templateCode}/clone": { + "post": { + "tags": [ + "RoleTemplates" + ], + "summary": "克隆角色模板。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "源模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "克隆命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CloneRoleTemplateCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloneRoleTemplateCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CloneRoleTemplateCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CloneRoleTemplateCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates/{templateCode}": { + "get": { + "tags": [ + "RoleTemplates" + ], + "summary": "获取角色模板详情。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "RoleTemplates" + ], + "summary": "更新角色模板。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "更新命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleTemplateCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleTemplateCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleTemplateCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleTemplateCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleTemplateDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "RoleTemplates" + ], + "summary": "删除角色模板。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates/{templateCode}/permissions": { + "get": { + "tags": [ + "RoleTemplates" + ], + "summary": "获取模板的权限列表。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionTemplateDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates/init": { + "post": { + "tags": [ + "RoleTemplates" + ], + "summary": "为当前租户批量初始化预置角色模板。", + "requestBody": { + "description": "初始化命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/InitializeRoleTemplatesCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitializeRoleTemplatesCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/InitializeRoleTemplatesCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/InitializeRoleTemplatesCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/role-templates/{templateCode}/initialize-tenant": { + "post": { + "tags": [ + "RoleTemplates" + ], + "summary": "将单个模板初始化到当前租户。", + "parameters": [ + { + "name": "templateCode", + "in": "path", + "description": "模板编码。", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/pickup/settings": { + "get": { + "tags": [ + "StorePickup" + ], + "summary": "获取自提配置。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StorePickupSettingDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "StorePickup" + ], + "summary": "更新自提配置。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpsertStorePickupSettingCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertStorePickupSettingCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpsertStorePickupSettingCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpsertStorePickupSettingCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StorePickupSettingDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/pickup/slots": { + "get": { + "tags": [ + "StorePickup" + ], + "summary": "查询档期列表。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StorePickupSlotDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "StorePickup" + ], + "summary": "创建档期。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStorePickupSlotCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStorePickupSlotCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStorePickupSlotCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStorePickupSlotCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StorePickupSlotDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/pickup/slots/{slotId}": { + "put": { + "tags": [ + "StorePickup" + ], + "summary": "更新档期。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "slotId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStorePickupSlotCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStorePickupSlotCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStorePickupSlotCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStorePickupSlotCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StorePickupSlotDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "StorePickup" + ], + "summary": "删除档期。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "slotId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores": { + "post": { + "tags": [ + "Stores" + ], + "summary": "创建门店。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Stores" + ], + "summary": "查询门店列表。", + "parameters": [ + { + "name": "merchantId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "门店运营状态。", + "schema": { + "$ref": "#/components/schemas/StoreStatus" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}": { + "get": { + "tags": [ + "Stores" + ], + "summary": "获取门店详情。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "Stores" + ], + "summary": "更新门店。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Stores" + ], + "summary": "删除门店。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/business-hours": { + "get": { + "tags": [ + "Stores" + ], + "summary": "查询门店营业时段。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreBusinessHourDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Stores" + ], + "summary": "新增营业时段。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreBusinessHourCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreBusinessHourCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreBusinessHourCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreBusinessHourCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreBusinessHourDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/business-hours/{businessHourId}": { + "put": { + "tags": [ + "Stores" + ], + "summary": "更新营业时段。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "businessHourId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreBusinessHourCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreBusinessHourCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreBusinessHourCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreBusinessHourCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreBusinessHourDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Stores" + ], + "summary": "删除营业时段。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "businessHourId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/delivery-zones": { + "get": { + "tags": [ + "Stores" + ], + "summary": "查询配送区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDeliveryZoneDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Stores" + ], + "summary": "新增配送区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreDeliveryZoneCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreDeliveryZoneCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreDeliveryZoneCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreDeliveryZoneCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDeliveryZoneDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/delivery-zones/{deliveryZoneId}": { + "put": { + "tags": [ + "Stores" + ], + "summary": "更新配送区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "deliveryZoneId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreDeliveryZoneCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreDeliveryZoneCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreDeliveryZoneCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreDeliveryZoneCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreDeliveryZoneDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Stores" + ], + "summary": "删除配送区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "deliveryZoneId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/holidays": { + "get": { + "tags": [ + "Stores" + ], + "summary": "查询门店节假日。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreHolidayDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Stores" + ], + "summary": "新增节假日配置。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreHolidayCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreHolidayCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreHolidayCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreHolidayCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreHolidayDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/holidays/{holidayId}": { + "put": { + "tags": [ + "Stores" + ], + "summary": "更新节假日配置。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "holidayId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreHolidayCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreHolidayCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreHolidayCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreHolidayCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreHolidayDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Stores" + ], + "summary": "删除节假日配置。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "holidayId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/shifts": { + "get": { + "tags": [ + "StoreShifts" + ], + "summary": "查询排班(默认未来 7 天)。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "from", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "to", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "staffId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreEmployeeShiftDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "StoreShifts" + ], + "summary": "创建排班。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreEmployeeShiftCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreEmployeeShiftCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreEmployeeShiftCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreEmployeeShiftCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreEmployeeShiftDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/shifts/{shiftId}": { + "put": { + "tags": [ + "StoreShifts" + ], + "summary": "更新排班。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "shiftId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreEmployeeShiftCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreEmployeeShiftCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreEmployeeShiftCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreEmployeeShiftCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreEmployeeShiftDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "StoreShifts" + ], + "summary": "删除排班。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "shiftId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/staffs": { + "get": { + "tags": [ + "StoreStaffs" + ], + "summary": "查询门店员工列表。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "role", + "in": "query", + "description": "商户员工角色。", + "schema": { + "$ref": "#/components/schemas/StaffRoleType" + } + }, + { + "name": "status", + "in": "query", + "description": "员工账号状态。", + "schema": { + "$ref": "#/components/schemas/StaffStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreStaffDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "StoreStaffs" + ], + "summary": "创建门店员工。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreStaffCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreStaffCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreStaffCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreStaffCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreStaffDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/staffs/{staffId}": { + "put": { + "tags": [ + "StoreStaffs" + ], + "summary": "更新门店员工。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "staffId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreStaffCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreStaffCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreStaffCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreStaffCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreStaffDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "StoreStaffs" + ], + "summary": "删除门店员工。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "staffId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/table-areas": { + "get": { + "tags": [ + "StoreTableAreas" + ], + "summary": "查询区域列表。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableAreaDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "StoreTableAreas" + ], + "summary": "创建区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreTableAreaCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreTableAreaCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreTableAreaCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateStoreTableAreaCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableAreaDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/table-areas/{areaId}": { + "put": { + "tags": [ + "StoreTableAreas" + ], + "summary": "更新区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "areaId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableAreaCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableAreaCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableAreaCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableAreaCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableAreaDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "StoreTableAreas" + ], + "summary": "删除区域。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "areaId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/tables": { + "get": { + "tags": [ + "StoreTables" + ], + "summary": "查询桌码列表。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "areaId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "桌台占用状态。", + "schema": { + "$ref": "#/components/schemas/StoreTableStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "StoreTables" + ], + "summary": "批量生成桌码。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/GenerateStoreTablesCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateStoreTablesCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/GenerateStoreTablesCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/GenerateStoreTablesCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableDtoIReadOnlyListApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/tables/{tableId}": { + "put": { + "tags": [ + "StoreTables" + ], + "summary": "更新桌码。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "tableId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateStoreTableCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTableDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "StoreTables" + ], + "summary": "删除桌码。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "tableId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/stores/{storeId}/tables/export": { + "post": { + "tags": [ + "StoreTables" + ], + "summary": "导出桌码二维码 ZIP。", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ExportStoreTableQRCodesQuery" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExportStoreTableQRCodesQuery" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ExportStoreTableQRCodesQuery" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ExportStoreTableQRCodesQuery" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/system-parameters": { + "post": { + "tags": [ + "SystemParameters" + ], + "summary": "创建系统参数。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateSystemParameterCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSystemParameterCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateSystemParameterCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateSystemParameterCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemParameterDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "SystemParameters" + ], + "summary": "查询系统参数列表。", + "parameters": [ + { + "name": "keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "isEnabled", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + }, + { + "name": "sortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "sortDesc", + "in": "query", + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemParameterDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/system-parameters/{parameterId}": { + "get": { + "tags": [ + "SystemParameters" + ], + "summary": "获取系统参数详情。", + "parameters": [ + { + "name": "parameterId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemParameterDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "SystemParameters" + ], + "summary": "更新系统参数。", + "parameters": [ + { + "name": "parameterId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateSystemParameterCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSystemParameterCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateSystemParameterCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateSystemParameterCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SystemParameterDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "SystemParameters" + ], + "summary": "删除系统参数。", + "parameters": [ + { + "name": "parameterId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/announcements": { + "get": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "分页查询公告。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "TenantId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "AnnouncementType", + "in": "query", + "description": "租户公告类型。", + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementType" + } + }, + { + "name": "IsActive", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "OnlyEffective", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoPagedResultApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "创建公告。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantAnnouncementCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantAnnouncementCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantAnnouncementCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantAnnouncementCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}": { + "get": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "公告详情。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "announcementId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "更新公告。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "announcementId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantAnnouncementCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantAnnouncementCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantAnnouncementCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantAnnouncementCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "删除公告。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "announcementId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/announcements/{announcementId}/read": { + "post": { + "tags": [ + "TenantAnnouncements" + ], + "summary": "标记公告已读。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "announcementId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAnnouncementDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/billings": { + "get": { + "tags": [ + "TenantBillings" + ], + "summary": "分页查询账单。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "TenantId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "Status", + "in": "query", + "description": "账单状态。", + "schema": { + "$ref": "#/components/schemas/TenantBillingStatus" + } + }, + { + "name": "From", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "To", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoPagedResultApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "TenantBillings" + ], + "summary": "创建账单。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantBillingCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantBillingCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantBillingCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantBillingCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/billings/{billingId}": { + "get": { + "tags": [ + "TenantBillings" + ], + "summary": "账单详情。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "billingId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/billings/{billingId}/pay": { + "post": { + "tags": [ + "TenantBillings" + ], + "summary": "标记账单已支付。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "billingId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/MarkTenantBillingPaidCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkTenantBillingPaidCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/MarkTenantBillingPaidCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/MarkTenantBillingPaidCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantBillingDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/notifications": { + "get": { + "tags": [ + "TenantNotifications" + ], + "summary": "分页查询通知。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "TenantId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "Severity", + "in": "query", + "description": "租户通知的重要程度。", + "schema": { + "$ref": "#/components/schemas/TenantNotificationSeverity" + } + }, + { + "name": "UnreadOnly", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantNotificationDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/notifications/{notificationId}/read": { + "post": { + "tags": [ + "TenantNotifications" + ], + "summary": "标记通知已读。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "notificationId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantNotificationDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantNotificationDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenant-packages": { + "get": { + "tags": [ + "TenantPackages" + ], + "summary": "分页查询租户套餐。", + "parameters": [ + { + "name": "Keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "IsActive", + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoPagedResultApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "TenantPackages" + ], + "summary": "创建套餐。", + "requestBody": { + "description": "创建命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantPackageCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantPackageCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantPackageCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantPackageCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenant-packages/{tenantPackageId}": { + "get": { + "tags": [ + "TenantPackages" + ], + "summary": "查看套餐详情。", + "parameters": [ + { + "name": "tenantPackageId", + "in": "path", + "description": "套餐 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "TenantPackages" + ], + "summary": "更新套餐。", + "parameters": [ + { + "name": "tenantPackageId", + "in": "path", + "description": "套餐 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "更新命令。", + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantPackageCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantPackageCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantPackageCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantPackageCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantPackageDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "TenantPackages" + ], + "summary": "删除套餐。", + "parameters": [ + { + "name": "tenantPackageId", + "in": "path", + "description": "套餐 ID。", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/roles": { + "get": { + "tags": [ + "TenantRoles" + ], + "summary": "租户角色分页。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "TenantId", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "Keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "SortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "SortDescending", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoPagedResultApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "TenantRoles" + ], + "summary": "创建角色。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateRoleCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/roles/{roleId}": { + "get": { + "tags": [ + "TenantRoles" + ], + "summary": "角色详情(含权限)。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "roleId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDetailDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDetailDtoApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "TenantRoles" + ], + "summary": "更新角色。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "roleId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/UpdateRoleCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleDtoApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "TenantRoles" + ], + "summary": "删除角色。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "roleId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/roles/{roleId}/permissions": { + "get": { + "tags": [ + "TenantRoles" + ], + "summary": "获取角色权限列表。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "roleId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoIReadOnlyListApiResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionDtoIReadOnlyListApiResponse" + } + } + } + } + } + }, + "put": { + "tags": [ + "TenantRoles" + ], + "summary": "覆盖角色权限。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "roleId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/BindRolePermissionsCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/BindRolePermissionsCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/BindRolePermissionsCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/BindRolePermissionsCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BooleanApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants": { + "post": { + "tags": [ + "Tenants" + ], + "summary": "注册租户并初始化套餐。", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RegisterTenantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterTenantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RegisterTenantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RegisterTenantCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantDtoApiResponse" + } + } + } + } + } + }, + "get": { + "tags": [ + "Tenants" + ], + "summary": "分页查询租户。", + "parameters": [ + { + "name": "Status", + "in": "query", + "description": "租户服务状态。", + "schema": { + "$ref": "#/components/schemas/TenantStatus" + } + }, + { + "name": "VerificationStatus", + "in": "query", + "description": "租户实名认证状态。", + "schema": { + "$ref": "#/components/schemas/TenantVerificationStatus" + } + }, + { + "name": "Name", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "ContactName", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "ContactPhone", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}": { + "get": { + "tags": [ + "Tenants" + ], + "summary": "查看租户详情。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantDetailDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/verification": { + "post": { + "tags": [ + "Tenants" + ], + "summary": "提交或更新实名认证资料。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SubmitTenantVerificationCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantVerificationDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/review": { + "post": { + "tags": [ + "Tenants" + ], + "summary": "审核租户。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ReviewTenantCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReviewTenantCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ReviewTenantCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ReviewTenantCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/subscriptions": { + "post": { + "tags": [ + "Tenants" + ], + "summary": "创建或续费租户订阅。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantSubscriptionCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantSubscriptionCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantSubscriptionCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CreateTenantSubscriptionCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/subscriptions/{subscriptionId}/plan": { + "put": { + "tags": [ + "Tenants" + ], + "summary": "套餐升降配。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "subscriptionId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ChangeTenantSubscriptionPlanCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangeTenantSubscriptionPlanCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ChangeTenantSubscriptionPlanCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ChangeTenantSubscriptionPlanCommand" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantSubscriptionDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/audits": { + "get": { + "tags": [ + "Tenants" + ], + "summary": "查询审核日志。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TenantAuditLogDtoPagedResultApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/tenants/{tenantId}/quotas/check": { + "post": { + "tags": [ + "Tenants" + ], + "summary": "配额校验并占用额度(门店/账号/短信/配送)。", + "description": "需在请求头携带 X-Tenant-Id 对应的租户。", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CheckTenantQuotaCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckTenantQuotaCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CheckTenantQuotaCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CheckTenantQuotaCommand" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuotaCheckResultDtoApiResponse" + } + } + } + } + } + } + }, + "/api/admin/v1/users/permissions": { + "get": { + "tags": [ + "UserPermissions" + ], + "summary": "分页查询当前租户用户的角色与权限概览。", + "description": "示例:\n```\nGET /api/admin/v1/users/permissions?keyword=ops&page=1&pageSize=20&sortBy=createdAt&sortDescending=true\nHeader: Authorization: Bearer \n响应:\n{\n \"success\": true,\n \"code\": 200,\n \"data\": {\n \"items\": [\n {\n \"userId\": \"900123456789012346\",\n \"tenantId\": \"100000000000000001\",\n \"merchantId\": \"200000000000000001\",\n \"account\": \"ops.manager\",\n \"displayName\": \"运营经理\",\n \"roles\": [\"OpsManager\", \"Reporter\"],\n \"permissions\": [\"delivery:read\", \"order:read\", \"payment:read\"],\n \"createdAt\": \"2025-12-01T08:30:00Z\"\n }\n ],\n \"page\": 1,\n \"pageSize\": 20,\n \"totalCount\": 1,\n \"totalPages\": 1\n }\n}\n```", + "parameters": [ + { + "name": "Keyword", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "SortBy", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "SortDescending", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPermissionDtoPagedResultApiResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AddMerchantDocumentCommand": { + "required": [ + "documentType", + "fileUrl", + "merchantId" + ], + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "documentType": { + "$ref": "#/components/schemas/MerchantDocumentType" + }, + "fileUrl": { + "maxLength": 512, + "minLength": 1, + "type": "string" + }, + "documentNumber": { + "maxLength": 64, + "type": "string", + "nullable": true + }, + "issuedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "AdjustInventoryCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "quantityDelta": { + "type": "integer", + "format": "int32" + }, + "adjustmentType": { + "$ref": "#/components/schemas/InventoryAdjustmentType" + }, + "reason": { + "type": "string", + "nullable": true + }, + "safetyStock": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "isSoldOut": { + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": false + }, + "AdminLoginRequest": { + "required": [ + "account", + "password" + ], + "type": "object", + "properties": { + "account": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "password": { + "maxLength": 128, + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "BindInitialTenantSubscriptionCommand": { + "required": [ + "tenantId", + "tenantPackageId" + ], + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "tenantPackageId": { + "type": "integer", + "format": "int64" + }, + "autoRenew": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "BindRolePermissionsCommand": { + "type": "object", + "properties": { + "roleId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "permissionIds": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "BooleanApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "boolean", + "description": "业务数据。" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "BusinessHourType": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "营业时段类型。", + "format": "int32" + }, + "ChangeTenantSubscriptionPlanCommand": { + "required": [ + "targetPackageId", + "tenantId", + "tenantSubscriptionId" + ], + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "tenantSubscriptionId": { + "type": "integer", + "format": "int64" + }, + "targetPackageId": { + "type": "integer", + "format": "int64" + }, + "immediate": { + "type": "boolean" + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CheckTenantQuotaCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "quotaType": { + "$ref": "#/components/schemas/TenantQuotaType" + }, + "delta": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "CloneRoleTemplateCommand": { + "type": "object", + "properties": { + "sourceTemplateCode": { + "type": "string", + "nullable": true + }, + "newTemplateCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean", + "nullable": true + }, + "permissionCodes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ContractStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "商户合同状态。", + "format": "int32" + }, + "CreateDeliveryOrderCommand": { + "type": "object", + "properties": { + "orderId": { + "type": "integer", + "format": "int64" + }, + "provider": { + "$ref": "#/components/schemas/DeliveryProvider" + }, + "providerOrderId": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DeliveryStatus" + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "courierName": { + "type": "string", + "nullable": true + }, + "courierPhone": { + "type": "string", + "nullable": true + }, + "dispatchedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pickedUpAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "deliveredAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "failureReason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateDictionaryGroupRequest": { + "required": [ + "code", + "name", + "scope" + ], + "type": "object", + "properties": { + "code": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "name": { + "maxLength": 128, + "minLength": 1, + "type": "string" + }, + "scope": { + "$ref": "#/components/schemas/DictionaryScope" + }, + "description": { + "maxLength": 512, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateDictionaryItemRequest": { + "required": [ + "groupId", + "key", + "value" + ], + "type": "object", + "properties": { + "groupId": { + "type": "integer", + "format": "int64" + }, + "key": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "value": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "isEnabled": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "description": { + "maxLength": 512, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateMenuCommand": { + "type": "object", + "properties": { + "parentId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "component": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "isIframe": { + "type": "boolean" + }, + "link": { + "type": "string", + "nullable": true + }, + "keepAlive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "requiredPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaRoles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "authList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuAuthItemDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateMerchantCategoryCommand": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "displayOrder": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "isActive": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateMerchantCommand": { + "required": [ + "brandName", + "contactPhone" + ], + "type": "object", + "properties": { + "brandName": { + "maxLength": 128, + "minLength": 1, + "type": "string" + }, + "brandAlias": { + "maxLength": 64, + "type": "string", + "nullable": true + }, + "logoUrl": { + "maxLength": 256, + "type": "string", + "nullable": true + }, + "category": { + "maxLength": 64, + "type": "string", + "nullable": true + }, + "contactPhone": { + "maxLength": 32, + "minLength": 1, + "type": "string" + }, + "contactEmail": { + "maxLength": 128, + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/MerchantStatus" + } + }, + "additionalProperties": false + }, + "CreateMerchantContractCommand": { + "required": [ + "contractNumber", + "fileUrl", + "merchantId" + ], + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "contractNumber": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "fileUrl": { + "maxLength": 512, + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "CreateOrderCommand": { + "type": "object", + "properties": { + "orderNo": { + "type": "string", + "nullable": true + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "channel": { + "$ref": "#/components/schemas/OrderChannel" + }, + "deliveryType": { + "$ref": "#/components/schemas/DeliveryType" + }, + "status": { + "$ref": "#/components/schemas/OrderStatus" + }, + "paymentStatus": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "customerName": { + "type": "string", + "nullable": true + }, + "customerPhone": { + "type": "string", + "nullable": true + }, + "tableNo": { + "type": "string", + "nullable": true + }, + "queueNumber": { + "type": "string", + "nullable": true + }, + "reservationId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "itemsAmount": { + "type": "number", + "format": "double" + }, + "discountAmount": { + "type": "number", + "format": "double" + }, + "payableAmount": { + "type": "number", + "format": "double" + }, + "paidAmount": { + "type": "number", + "format": "double" + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "finishedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelledAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelReason": { + "type": "string", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderItemRequest" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "CreatePaymentCommand": { + "type": "object", + "properties": { + "orderId": { + "type": "integer", + "format": "int64" + }, + "method": { + "$ref": "#/components/schemas/PaymentMethod" + }, + "status": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "amount": { + "type": "number", + "format": "double" + }, + "tradeNo": { + "type": "string", + "nullable": true + }, + "channelTransactionId": { + "type": "string", + "nullable": true + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + }, + "payload": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreatePermissionCommand": { + "type": "object", + "properties": { + "parentId": { + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateProductCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "categoryId": { + "type": "integer", + "format": "int64" + }, + "spuCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "subtitle": { + "type": "string", + "nullable": true + }, + "unit": { + "type": "string", + "nullable": true + }, + "price": { + "type": "number", + "format": "double" + }, + "originalPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "stockQuantity": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ProductStatus" + }, + "coverImage": { + "type": "string", + "nullable": true + }, + "galleryImages": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "enableDineIn": { + "type": "boolean" + }, + "enablePickup": { + "type": "boolean" + }, + "enableDelivery": { + "type": "boolean" + }, + "isFeatured": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateRoleCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateRoleTemplateCommand": { + "type": "object", + "properties": { + "templateCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "permissionCodes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateStoreBusinessHourCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "dayOfWeek": { + "$ref": "#/components/schemas/DayOfWeek" + }, + "hourType": { + "$ref": "#/components/schemas/BusinessHourType" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "capacityLimit": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateStoreCommand": { + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "managerName": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "province": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "district": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "longitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "latitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "announcement": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + }, + "deliveryRadiusKm": { + "type": "number", + "format": "double" + }, + "supportsDineIn": { + "type": "boolean" + }, + "supportsPickup": { + "type": "boolean" + }, + "supportsDelivery": { + "type": "boolean" + }, + "supportsReservation": { + "type": "boolean" + }, + "supportsQueueing": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateStoreDeliveryZoneCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "zoneName": { + "type": "string", + "nullable": true + }, + "polygonGeoJson": { + "type": "string", + "nullable": true + }, + "minimumOrderAmount": { + "type": "number", + "format": "double", + "nullable": true + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "estimatedMinutes": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "CreateStoreEmployeeShiftCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "staffId": { + "type": "integer", + "format": "int64" + }, + "shiftDate": { + "type": "string", + "format": "date-time" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateStoreHolidayCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "isClosed": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateStorePickupSlotCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "cutoffMinutes": { + "type": "integer", + "format": "int32" + }, + "capacity": { + "type": "integer", + "format": "int32" + }, + "weekdays": { + "type": "string", + "nullable": true + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateStoreStaffCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + } + }, + "additionalProperties": false + }, + "CreateStoreTableAreaCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "CreateSystemParameterCommand": { + "type": "object", + "properties": { + "key": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateTenantAnnouncementCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string", + "nullable": true + }, + "announcementType": { + "$ref": "#/components/schemas/TenantAnnouncementType" + }, + "priority": { + "type": "integer", + "format": "int32" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time" + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "isActive": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreateTenantBillingCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "statementNo": { + "type": "string", + "nullable": true + }, + "periodStart": { + "type": "string", + "format": "date-time" + }, + "periodEnd": { + "type": "string", + "format": "date-time" + }, + "amountDue": { + "type": "number", + "format": "double" + }, + "amountPaid": { + "type": "number", + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/TenantBillingStatus" + }, + "dueDate": { + "type": "string", + "format": "date-time" + }, + "lineItemsJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CreateTenantPackageCommand": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "packageType": { + "$ref": "#/components/schemas/TenantPackageType" + }, + "monthlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "yearlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "maxStoreCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxAccountCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxStorageGb": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxSmsCredits": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxDeliveryOrders": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "featurePoliciesJson": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "CreateTenantSubscriptionCommand": { + "required": [ + "tenantId", + "tenantPackageId" + ], + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "tenantPackageId": { + "type": "integer", + "format": "int64" + }, + "durationMonths": { + "type": "integer", + "format": "int32" + }, + "autoRenew": { + "type": "boolean" + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CurrentUserProfile": { + "type": "object", + "properties": { + "userId": { + "type": "integer", + "format": "int64" + }, + "account": { + "type": "string", + "nullable": true + }, + "displayName": { + "type": "string", + "nullable": true + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "avatar": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CurrentUserProfileApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/CurrentUserProfile" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DayOfWeek": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "type": "integer", + "format": "int32" + }, + "DeductInventoryCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "isPresaleOrder": { + "type": "boolean" + }, + "idempotencyKey": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DeliveryEventDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "deliveryOrderId": { + "type": "integer", + "format": "int64" + }, + "eventType": { + "$ref": "#/components/schemas/DeliveryEventType" + }, + "message": { + "type": "string", + "nullable": true + }, + "occurredAt": { + "type": "string", + "format": "date-time" + }, + "payload": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DeliveryEventType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "配送事件类型。", + "format": "int32" + }, + "DeliveryOrderDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "provider": { + "$ref": "#/components/schemas/DeliveryProvider" + }, + "providerOrderId": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DeliveryStatus" + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "courierName": { + "type": "string", + "nullable": true + }, + "courierPhone": { + "type": "string", + "nullable": true + }, + "dispatchedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pickedUpAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "deliveredAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "failureReason": { + "type": "string", + "nullable": true + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeliveryEventDto" + }, + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "DeliveryOrderDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/DeliveryOrderDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DeliveryOrderDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeliveryOrderDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "DeliveryOrderDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/DeliveryOrderDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DeliveryProvider": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "配送服务商类型。", + "format": "int32" + }, + "DeliveryStatus": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "type": "integer", + "description": "配送状态。", + "format": "int32" + }, + "DeliveryType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "履约/交付方式。", + "format": "int32" + }, + "DictionaryBatchQueryRequest": { + "required": [ + "codes" + ], + "type": "object", + "properties": { + "codes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "DictionaryGroupDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "scope": { + "$ref": "#/components/schemas/DictionaryScope" + }, + "description": { + "type": "string", + "nullable": true + }, + "isEnabled": { + "type": "boolean" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryItemDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryGroupDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/DictionaryGroupDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DictionaryGroupDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryGroupDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DictionaryItemDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "groupId": { + "type": "integer", + "format": "int64" + }, + "key": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "isEnabled": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryItemDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/DictionaryItemDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "DictionaryScope": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "参数字典作用域。", + "format": "int32" + }, + "ExportStoreTableQRCodesQuery": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "areaId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "qrContentTemplate": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "FileUploadResponse": { + "type": "object", + "properties": { + "url": { + "type": "string", + "nullable": true + }, + "fileName": { + "type": "string", + "nullable": true + }, + "fileSize": { + "type": "integer", + "format": "int64" + } + }, + "additionalProperties": false + }, + "FileUploadResponseApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/FileUploadResponse" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "GenerateStoreTablesCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "tableCodePrefix": { + "type": "string", + "nullable": true + }, + "startNumber": { + "type": "integer", + "format": "int32" + }, + "count": { + "type": "integer", + "format": "int32" + }, + "defaultCapacity": { + "type": "integer", + "format": "int32" + }, + "areaId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "InitializeRoleTemplatesCommand": { + "type": "object", + "properties": { + "templateCodes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "Int32ApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "integer", + "description": "业务数据。", + "format": "int32" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "InventoryAdjustmentType": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "库存调整类型。", + "format": "int32" + }, + "InventoryBatchDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "batchNumber": { + "type": "string", + "nullable": true + }, + "productionDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expireDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "remainingQuantity": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "InventoryBatchDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/InventoryBatchDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "InventoryBatchDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InventoryBatchDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "InventoryItemDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "batchNumber": { + "type": "string", + "nullable": true + }, + "quantityOnHand": { + "type": "integer", + "format": "int32" + }, + "quantityReserved": { + "type": "integer", + "format": "int32" + }, + "safetyStock": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "location": { + "type": "string", + "nullable": true + }, + "expireDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "isPresale": { + "type": "boolean" + }, + "presaleStartTime": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "presaleEndTime": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "presaleCapacity": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "presaleLocked": { + "type": "integer", + "format": "int32" + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "isSoldOut": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "InventoryItemDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/InventoryItemDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "LockInventoryCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "isPresaleOrder": { + "type": "boolean" + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "idempotencyKey": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "MarkTenantBillingPaidCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "billingId": { + "type": "integer", + "format": "int64" + }, + "amountPaid": { + "type": "number", + "format": "double" + }, + "paidAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "MediaAssetType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "商品媒资类型。", + "format": "int32" + }, + "MenuAuthItemDto": { + "required": [ + "authMark", + "title" + ], + "type": "object", + "properties": { + "title": { + "type": "string", + "nullable": true + }, + "authMark": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "MenuDefinitionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "parentId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "component": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "isIframe": { + "type": "boolean" + }, + "link": { + "type": "string", + "nullable": true + }, + "keepAlive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "requiredPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaRoles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "authList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuAuthItemDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "MenuDefinitionDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MenuDefinitionDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MenuDefinitionDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuDefinitionDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MenuMetaDto": { + "required": [ + "title" + ], + "type": "object", + "properties": { + "title": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "keepAlive": { + "type": "boolean" + }, + "isIframe": { + "type": "boolean" + }, + "link": { + "type": "string", + "nullable": true + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "authList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuAuthItemDto" + }, + "nullable": true + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "MenuNodeDto": { + "required": [ + "component", + "meta", + "name", + "path" + ], + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "component": { + "type": "string", + "nullable": true + }, + "meta": { + "$ref": "#/components/schemas/MenuMetaDto" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuNodeDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "MenuNodeDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuNodeDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantAuditAction": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "商户审核日志动作。", + "format": "int32" + }, + "MerchantAuditLogDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "action": { + "$ref": "#/components/schemas/MerchantAuditAction" + }, + "title": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "operatorName": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "MerchantAuditLogDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantAuditLogDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "MerchantAuditLogDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantAuditLogDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantCategoryDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "displayOrder": { + "type": "integer", + "format": "int32" + }, + "isActive": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "MerchantCategoryDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantCategoryDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantCategoryDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantCategoryDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantCategoryOrderItem": { + "required": [ + "categoryId" + ], + "type": "object", + "properties": { + "categoryId": { + "type": "integer", + "format": "int64" + }, + "displayOrder": { + "maximum": 100000, + "minimum": -1000, + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "MerchantContractDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "contractNumber": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ContractStatus" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "fileUrl": { + "type": "string", + "nullable": true + }, + "signedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "terminatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "terminationReason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "MerchantContractDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantContractDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantContractDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantContractDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantDetailDto": { + "type": "object", + "properties": { + "merchant": { + "$ref": "#/components/schemas/MerchantDto" + }, + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantDocumentDto" + }, + "nullable": true + }, + "contracts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantContractDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "MerchantDetailDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantDetailDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantDocumentDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "documentType": { + "$ref": "#/components/schemas/MerchantDocumentType" + }, + "status": { + "$ref": "#/components/schemas/MerchantDocumentStatus" + }, + "fileUrl": { + "type": "string", + "nullable": true + }, + "documentNumber": { + "type": "string", + "nullable": true + }, + "issuedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "remarks": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "MerchantDocumentDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantDocumentDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantDocumentDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantDocumentDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantDocumentStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "证照审核状态。", + "format": "int32" + }, + "MerchantDocumentType": { + "enum": [ + 0, + 1, + 2, + 99 + ], + "type": "integer", + "description": "商户证照类型。", + "format": "int32" + }, + "MerchantDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "brandName": { + "type": "string", + "nullable": true + }, + "brandAlias": { + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "category": { + "type": "string", + "nullable": true + }, + "contactPhone": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/MerchantStatus" + }, + "joinedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "MerchantDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/MerchantDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "MerchantStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "商户入驻状态。", + "format": "int32" + }, + "ObjectApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "OrderChannel": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "下单渠道。", + "format": "int32" + }, + "OrderDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "orderNo": { + "type": "string", + "nullable": true + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "channel": { + "$ref": "#/components/schemas/OrderChannel" + }, + "deliveryType": { + "$ref": "#/components/schemas/DeliveryType" + }, + "status": { + "$ref": "#/components/schemas/OrderStatus" + }, + "paymentStatus": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "customerName": { + "type": "string", + "nullable": true + }, + "customerPhone": { + "type": "string", + "nullable": true + }, + "tableNo": { + "type": "string", + "nullable": true + }, + "queueNumber": { + "type": "string", + "nullable": true + }, + "reservationId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "itemsAmount": { + "type": "number", + "format": "double" + }, + "discountAmount": { + "type": "number", + "format": "double" + }, + "payableAmount": { + "type": "number", + "format": "double" + }, + "paidAmount": { + "type": "number", + "format": "double" + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "finishedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelledAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelReason": { + "type": "string", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderItemDto" + }, + "nullable": true + }, + "statusHistory": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderStatusHistoryDto" + }, + "nullable": true + }, + "refunds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RefundRequestDto" + }, + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "OrderDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/OrderDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "OrderDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "OrderDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/OrderDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "OrderItemDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "productName": { + "type": "string", + "nullable": true + }, + "skuName": { + "type": "string", + "nullable": true + }, + "unit": { + "type": "string", + "nullable": true + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "unitPrice": { + "type": "number", + "format": "double" + }, + "discountAmount": { + "type": "number", + "format": "double" + }, + "subTotal": { + "type": "number", + "format": "double" + }, + "attributesJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "OrderItemRequest": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "productName": { + "type": "string", + "nullable": true + }, + "skuName": { + "type": "string", + "nullable": true + }, + "unit": { + "type": "string", + "nullable": true + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "unitPrice": { + "type": "number", + "format": "double" + }, + "discountAmount": { + "type": "number", + "format": "double" + }, + "subTotal": { + "type": "number", + "format": "double" + }, + "attributesJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "OrderStatus": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "订单状态。", + "format": "int32" + }, + "OrderStatusHistoryDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/OrderStatus" + }, + "operatorId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "notes": { + "type": "string", + "nullable": true + }, + "occurredAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "PaymentDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "method": { + "$ref": "#/components/schemas/PaymentMethod" + }, + "status": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "amount": { + "type": "number", + "format": "double" + }, + "tradeNo": { + "type": "string", + "nullable": true + }, + "channelTransactionId": { + "type": "string", + "nullable": true + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + }, + "payload": { + "type": "string", + "nullable": true + }, + "refunds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentRefundDto" + }, + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "PaymentDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/PaymentDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PaymentDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "PaymentDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/PaymentDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PaymentMethod": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "支付方式。", + "format": "int32" + }, + "PaymentRefundDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "paymentRecordId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "amount": { + "type": "number", + "format": "double" + }, + "channelRefundId": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/PaymentRefundStatus" + }, + "payload": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "PaymentRefundStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "支付退款状态。", + "format": "int32" + }, + "PaymentStatus": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "支付记录状态。", + "format": "int32" + }, + "PermissionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "parentId": { + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "PermissionDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/PermissionDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PermissionDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PermissionDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "PermissionDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/PermissionDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PermissionTemplateDto": { + "type": "object", + "properties": { + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "PermissionTemplateDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionTemplateDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PermissionTreeDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "parentId": { + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionTreeDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PermissionTreeDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionTreeDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "PricingRuleType": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "价格策略类型。", + "format": "int32" + }, + "ProductAddonGroupDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "minSelect": { + "type": "integer", + "format": "int32" + }, + "maxSelect": { + "type": "integer", + "format": "int32" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAddonOptionDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ProductAddonGroupDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAddonGroupDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductAddonOptionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "addonGroupId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "extraPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "ProductAttributeGroupDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "selectionType": { + "type": "integer", + "format": "int32" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAttributeOptionDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ProductAttributeGroupDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAttributeGroupDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductAttributeOptionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "attributeGroupId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "ProductDetailDto": { + "type": "object", + "properties": { + "product": { + "$ref": "#/components/schemas/ProductDto" + }, + "skus": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSkuDto" + }, + "nullable": true + }, + "attributeGroups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAttributeGroupDto" + }, + "nullable": true + }, + "addonGroups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAddonGroupDto" + }, + "nullable": true + }, + "pricingRules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductPricingRuleDto" + }, + "nullable": true + }, + "mediaAssets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductMediaAssetDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ProductDetailDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/ProductDetailDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "categoryId": { + "type": "integer", + "format": "int64" + }, + "spuCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "subtitle": { + "type": "string", + "nullable": true + }, + "unit": { + "type": "string", + "nullable": true + }, + "price": { + "type": "number", + "format": "double" + }, + "originalPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "stockQuantity": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ProductStatus" + }, + "coverImage": { + "type": "string", + "nullable": true + }, + "galleryImages": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "enableDineIn": { + "type": "boolean" + }, + "enablePickup": { + "type": "boolean" + }, + "enableDelivery": { + "type": "boolean" + }, + "isFeatured": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "ProductDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/ProductDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "ProductDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/ProductDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductMediaAssetDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "mediaType": { + "$ref": "#/components/schemas/MediaAssetType" + }, + "url": { + "type": "string", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "ProductMediaAssetDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductMediaAssetDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductPricingRuleDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "ruleType": { + "$ref": "#/components/schemas/PricingRuleType" + }, + "price": { + "type": "number", + "format": "double" + }, + "conditionsJson": { + "type": "string", + "nullable": true + }, + "weekdaysJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ProductPricingRuleDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductPricingRuleDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductSkuDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "productId": { + "type": "integer", + "format": "int64" + }, + "skuCode": { + "type": "string", + "nullable": true + }, + "barcode": { + "type": "string", + "nullable": true + }, + "price": { + "type": "number", + "format": "double" + }, + "originalPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "stockQuantity": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "nullable": true + }, + "attributesJson": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "ProductSkuDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSkuDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "ProductStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "商品状态。", + "format": "int32" + }, + "PublishProductCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "QuotaCheckResultDto": { + "type": "object", + "properties": { + "quotaType": { + "$ref": "#/components/schemas/TenantQuotaType" + }, + "limit": { + "type": "number", + "format": "double", + "nullable": true + }, + "used": { + "type": "number", + "format": "double" + }, + "remaining": { + "type": "number", + "format": "double", + "nullable": true + } + }, + "additionalProperties": false + }, + "QuotaCheckResultDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/QuotaCheckResultDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RefreshTokenRequest": { + "required": [ + "refreshToken" + ], + "type": "object", + "properties": { + "refreshToken": { + "maxLength": 256, + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "RefundRequestDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "refundNo": { + "type": "string", + "nullable": true + }, + "amount": { + "type": "number", + "format": "double" + }, + "reason": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/RefundStatus" + }, + "requestedAt": { + "type": "string", + "format": "date-time" + }, + "processedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewNotes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RefundStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "退款申请状态。", + "format": "int32" + }, + "RegisterTenantCommand": { + "required": [ + "code", + "name", + "tenantPackageId" + ], + "type": "object", + "properties": { + "code": { + "maxLength": 64, + "minLength": 0, + "type": "string" + }, + "name": { + "maxLength": 128, + "minLength": 0, + "type": "string" + }, + "shortName": { + "type": "string", + "nullable": true + }, + "industry": { + "type": "string", + "nullable": true + }, + "contactName": { + "type": "string", + "nullable": true + }, + "contactPhone": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "tenantPackageId": { + "type": "integer", + "format": "int64" + }, + "durationMonths": { + "type": "integer", + "format": "int32" + }, + "autoRenew": { + "type": "boolean" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "ReleaseInventoryCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "isPresaleOrder": { + "type": "boolean" + }, + "idempotencyKey": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ReorderMerchantCategoriesCommand": { + "required": [ + "items" + ], + "type": "object", + "properties": { + "items": { + "minItems": 1, + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantCategoryOrderItem" + } + } + }, + "additionalProperties": false + }, + "ReplaceProductAddonsCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "addonGroups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAddonGroupDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ReplaceProductAttributesCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "attributeGroups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductAttributeGroupDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ReplaceProductMediaCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "mediaAssets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductMediaAssetDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ReplaceProductPricingRulesCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "pricingRules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductPricingRuleDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ReplaceProductSkusCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "skus": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSkuDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ReviewMerchantCommand": { + "required": [ + "merchantId" + ], + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "approve": { + "type": "boolean" + }, + "remarks": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ReviewMerchantDocumentCommand": { + "required": [ + "documentId", + "merchantId" + ], + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "documentId": { + "type": "integer", + "format": "int64" + }, + "approve": { + "type": "boolean" + }, + "remarks": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ReviewTenantCommand": { + "required": [ + "tenantId" + ], + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "approve": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RoleDetailDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "RoleDetailDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/RoleDetailDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RoleDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "code": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RoleDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/RoleDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RoleDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoleDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RoleDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoleDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "RoleDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/RoleDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RoleTemplateDto": { + "type": "object", + "properties": { + "templateCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionTemplateDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "RoleTemplateDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/RoleTemplateDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "RoleTemplateDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoleTemplateDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "SelfRegisterResultDto": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/TenantStatus" + }, + "verificationStatus": { + "$ref": "#/components/schemas/TenantVerificationStatus" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "adminAccount": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SelfRegisterResultDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/SelfRegisterResultDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "SelfRegisterTenantCommand": { + "required": [ + "adminAccount", + "adminPassword", + "adminPhone" + ], + "type": "object", + "properties": { + "adminAccount": { + "maxLength": 64, + "minLength": 0, + "pattern": "^[A-Za-z0-9]+$", + "type": "string" + }, + "adminDisplayName": { + "maxLength": 64, + "minLength": 0, + "type": "string", + "nullable": true + }, + "adminEmail": { + "maxLength": 128, + "minLength": 0, + "type": "string", + "format": "email", + "nullable": true + }, + "adminPhone": { + "maxLength": 32, + "minLength": 0, + "type": "string" + }, + "adminPassword": { + "maxLength": 128, + "minLength": 8, + "type": "string" + } + }, + "additionalProperties": false + }, + "StaffRoleType": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "商户员工角色。", + "format": "int32" + }, + "StaffStatus": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "员工账号状态。", + "format": "int32" + }, + "StoreBusinessHourDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "dayOfWeek": { + "$ref": "#/components/schemas/DayOfWeek" + }, + "hourType": { + "$ref": "#/components/schemas/BusinessHourType" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "capacityLimit": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "notes": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreBusinessHourDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreBusinessHourDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreBusinessHourDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreBusinessHourDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreDeliveryZoneDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "zoneName": { + "type": "string", + "nullable": true + }, + "polygonGeoJson": { + "type": "string", + "nullable": true + }, + "minimumOrderAmount": { + "type": "number", + "format": "double", + "nullable": true + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "estimatedMinutes": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreDeliveryZoneDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreDeliveryZoneDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreDeliveryZoneDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreDeliveryZoneDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "managerName": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "province": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "district": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "longitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "latitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "announcement": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + }, + "deliveryRadiusKm": { + "type": "number", + "format": "double" + }, + "supportsDineIn": { + "type": "boolean" + }, + "supportsPickup": { + "type": "boolean" + }, + "supportsDelivery": { + "type": "boolean" + }, + "supportsReservation": { + "type": "boolean" + }, + "supportsQueueing": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "StoreDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreEmployeeShiftDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "staffId": { + "type": "integer", + "format": "int64" + }, + "shiftDate": { + "type": "string", + "format": "date-time" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + }, + "notes": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreEmployeeShiftDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreEmployeeShiftDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreEmployeeShiftDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreEmployeeShiftDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreHolidayDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "isClosed": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreHolidayDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreHolidayDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreHolidayDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreHolidayDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StorePickupSettingDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "allowToday": { + "type": "boolean" + }, + "allowDaysAhead": { + "type": "integer", + "format": "int32" + }, + "defaultCutoffMinutes": { + "type": "integer", + "format": "int32" + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "StorePickupSettingDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StorePickupSettingDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StorePickupSlotDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "cutoffMinutes": { + "type": "integer", + "format": "int32" + }, + "capacity": { + "type": "integer", + "format": "int32" + }, + "reservedCount": { + "type": "integer", + "format": "int32" + }, + "weekdays": { + "type": "string", + "nullable": true + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "StorePickupSlotDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StorePickupSlotDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StorePickupSlotDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StorePickupSlotDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreStaffDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + }, + "status": { + "$ref": "#/components/schemas/StaffStatus" + } + }, + "additionalProperties": false + }, + "StoreStaffDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreStaffDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreStaffDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreStaffDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "门店运营状态。", + "format": "int32" + }, + "StoreTableAreaDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreTableAreaDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreTableAreaDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreTableAreaDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreTableAreaDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreTableDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "areaId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "tableCode": { + "type": "string", + "nullable": true + }, + "capacity": { + "type": "integer", + "format": "int32" + }, + "tags": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StoreTableStatus" + }, + "qrCodeUrl": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "StoreTableDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/StoreTableDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreTableDtoIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StoreTableDto" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StoreTableStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "桌台占用状态。", + "format": "int32" + }, + "StringDictionaryItemDtoIReadOnlyListIReadOnlyDictionaryApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryItemDto" + }, + "nullable": true + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "StringIReadOnlyListApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "type": "array", + "items": { + "type": "string" + }, + "description": "业务数据。", + "nullable": true + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "SubmitTenantVerificationCommand": { + "required": [ + "tenantId" + ], + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "businessLicenseNumber": { + "type": "string", + "nullable": true + }, + "businessLicenseUrl": { + "type": "string", + "nullable": true + }, + "legalPersonName": { + "type": "string", + "nullable": true + }, + "legalPersonIdNumber": { + "type": "string", + "nullable": true + }, + "legalPersonIdFrontUrl": { + "type": "string", + "nullable": true + }, + "legalPersonIdBackUrl": { + "type": "string", + "nullable": true + }, + "bankAccountName": { + "type": "string", + "nullable": true + }, + "bankAccountNumber": { + "type": "string", + "nullable": true + }, + "bankName": { + "type": "string", + "nullable": true + }, + "additionalDataJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SubscriptionStatus": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "订阅状态。", + "format": "int32" + }, + "SystemParameterDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "key": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "isEnabled": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "SystemParameterDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/SystemParameterDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "SystemParameterDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SystemParameterDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "SystemParameterDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/SystemParameterDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantAnnouncementDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string", + "nullable": true + }, + "announcementType": { + "$ref": "#/components/schemas/TenantAnnouncementType" + }, + "priority": { + "type": "integer", + "format": "int32" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time" + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "isRead": { + "type": "boolean" + }, + "readAt": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "TenantAnnouncementDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantAnnouncementDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantAnnouncementDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantAnnouncementDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantAnnouncementDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantAnnouncementDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantAnnouncementType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "租户公告类型。", + "format": "int32" + }, + "TenantAuditAction": { + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "type": "integer", + "description": "租户运营审核动作。", + "format": "int32" + }, + "TenantAuditLogDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "action": { + "$ref": "#/components/schemas/TenantAuditAction" + }, + "title": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "operatorName": { + "type": "string", + "nullable": true + }, + "previousStatus": { + "$ref": "#/components/schemas/TenantStatus" + }, + "currentStatus": { + "$ref": "#/components/schemas/TenantStatus" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "TenantAuditLogDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantAuditLogDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantAuditLogDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantAuditLogDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantBillingDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "statementNo": { + "type": "string", + "nullable": true + }, + "periodStart": { + "type": "string", + "format": "date-time" + }, + "periodEnd": { + "type": "string", + "format": "date-time" + }, + "amountDue": { + "type": "number", + "format": "double" + }, + "amountPaid": { + "type": "number", + "format": "double" + }, + "status": { + "$ref": "#/components/schemas/TenantBillingStatus" + }, + "dueDate": { + "type": "string", + "format": "date-time" + }, + "lineItemsJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "TenantBillingDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantBillingDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantBillingDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantBillingDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantBillingDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantBillingDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantBillingStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "账单状态。", + "format": "int32" + }, + "TenantDetailDto": { + "type": "object", + "properties": { + "tenant": { + "$ref": "#/components/schemas/TenantDto" + }, + "verification": { + "$ref": "#/components/schemas/TenantVerificationDto" + }, + "subscription": { + "$ref": "#/components/schemas/TenantSubscriptionDto" + }, + "package": { + "$ref": "#/components/schemas/TenantPackageDto" + } + }, + "additionalProperties": false + }, + "TenantDetailDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantDetailDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "shortName": { + "type": "string", + "nullable": true + }, + "contactName": { + "type": "string", + "nullable": true + }, + "contactPhone": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/TenantStatus" + }, + "verificationStatus": { + "$ref": "#/components/schemas/TenantVerificationStatus" + }, + "currentPackageId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "effectiveFrom": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "autoRenew": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "TenantDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantNotificationChannel": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "通知推送渠道。", + "format": "int32" + }, + "TenantNotificationDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string", + "nullable": true + }, + "message": { + "type": "string", + "nullable": true + }, + "channel": { + "$ref": "#/components/schemas/TenantNotificationChannel" + }, + "severity": { + "$ref": "#/components/schemas/TenantNotificationSeverity" + }, + "sentAt": { + "type": "string", + "format": "date-time" + }, + "readAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "metadataJson": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "TenantNotificationDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantNotificationDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantNotificationDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantNotificationDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantNotificationDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantNotificationDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantNotificationSeverity": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "description": "租户通知的重要程度。", + "format": "int32" + }, + "TenantPackageDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "packageType": { + "$ref": "#/components/schemas/TenantPackageType" + }, + "monthlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "yearlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "maxStoreCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxAccountCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxStorageGb": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxSmsCredits": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxDeliveryOrders": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "featurePoliciesJson": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "TenantPackageDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantPackageDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantPackageDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TenantPackageDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "TenantPackageDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantPackageDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantPackageType": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "套餐类型枚举。", + "format": "int32" + }, + "TenantProgressDto": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/TenantStatus" + }, + "verificationStatus": { + "$ref": "#/components/schemas/TenantVerificationStatus" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "TenantProgressDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantProgressDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantQuotaType": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "integer", + "description": "配额类型,覆盖容量及调用次数。", + "format": "int32" + }, + "TenantStatus": { + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "type": "integer", + "description": "租户服务状态。", + "format": "int32" + }, + "TenantSubscriptionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "tenantPackageId": { + "type": "integer", + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/SubscriptionStatus" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time" + }, + "effectiveTo": { + "type": "string", + "format": "date-time" + }, + "nextBillingDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "autoRenew": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "TenantSubscriptionDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantSubscriptionDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantVerificationDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/TenantVerificationStatus" + }, + "businessLicenseNumber": { + "type": "string", + "nullable": true + }, + "businessLicenseUrl": { + "type": "string", + "nullable": true + }, + "legalPersonName": { + "type": "string", + "nullable": true + }, + "legalPersonIdNumber": { + "type": "string", + "nullable": true + }, + "legalPersonIdFrontUrl": { + "type": "string", + "nullable": true + }, + "legalPersonIdBackUrl": { + "type": "string", + "nullable": true + }, + "bankAccountName": { + "type": "string", + "nullable": true + }, + "bankAccountNumber": { + "type": "string", + "nullable": true + }, + "bankName": { + "type": "string", + "nullable": true + }, + "additionalDataJson": { + "type": "string", + "nullable": true + }, + "submittedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewedBy": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "reviewRemarks": { + "type": "string", + "nullable": true + }, + "reviewedByName": { + "type": "string", + "nullable": true + }, + "reviewedAt": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "TenantVerificationDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TenantVerificationDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "TenantVerificationStatus": { + "enum": [ + 0, + 1, + 2, + 3 + ], + "type": "integer", + "description": "租户实名认证状态。", + "format": "int32" + }, + "TokenResponse": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "nullable": true + }, + "accessTokenExpiresAt": { + "type": "string", + "format": "date-time" + }, + "refreshToken": { + "type": "string", + "nullable": true + }, + "refreshTokenExpiresAt": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "#/components/schemas/CurrentUserProfile" + }, + "isNewUser": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "TokenResponseApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/TokenResponse" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "UnpublishProductCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateDeliveryOrderCommand": { + "type": "object", + "properties": { + "deliveryOrderId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "provider": { + "$ref": "#/components/schemas/DeliveryProvider" + }, + "providerOrderId": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/DeliveryStatus" + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "courierName": { + "type": "string", + "nullable": true + }, + "courierPhone": { + "type": "string", + "nullable": true + }, + "dispatchedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pickedUpAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "deliveredAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "failureReason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateDictionaryGroupRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "maxLength": 128, + "minLength": 1, + "type": "string" + }, + "description": { + "maxLength": 512, + "type": "string", + "nullable": true + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateDictionaryItemRequest": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "isEnabled": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "description": { + "maxLength": 512, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateMenuCommand": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "parentId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "component": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "isIframe": { + "type": "boolean" + }, + "link": { + "type": "string", + "nullable": true + }, + "keepAlive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "requiredPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaPermissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "metaRoles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "authList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuAuthItemDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateMerchantCommand": { + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "brandName": { + "type": "string", + "nullable": true + }, + "brandAlias": { + "type": "string", + "nullable": true + }, + "logoUrl": { + "type": "string", + "nullable": true + }, + "category": { + "type": "string", + "nullable": true + }, + "contactPhone": { + "type": "string", + "nullable": true + }, + "contactEmail": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/MerchantStatus" + } + }, + "additionalProperties": false + }, + "UpdateMerchantContractStatusCommand": { + "required": [ + "contractId", + "merchantId", + "status" + ], + "type": "object", + "properties": { + "merchantId": { + "type": "integer", + "format": "int64" + }, + "contractId": { + "type": "integer", + "format": "int64" + }, + "status": { + "$ref": "#/components/schemas/ContractStatus" + }, + "signedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateOrderCommand": { + "type": "object", + "properties": { + "orderId": { + "type": "integer", + "format": "int64" + }, + "orderNo": { + "type": "string", + "nullable": true + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "channel": { + "$ref": "#/components/schemas/OrderChannel" + }, + "deliveryType": { + "$ref": "#/components/schemas/DeliveryType" + }, + "status": { + "$ref": "#/components/schemas/OrderStatus" + }, + "paymentStatus": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "customerName": { + "type": "string", + "nullable": true + }, + "customerPhone": { + "type": "string", + "nullable": true + }, + "tableNo": { + "type": "string", + "nullable": true + }, + "queueNumber": { + "type": "string", + "nullable": true + }, + "reservationId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "itemsAmount": { + "type": "number", + "format": "double" + }, + "discountAmount": { + "type": "number", + "format": "double" + }, + "payableAmount": { + "type": "number", + "format": "double" + }, + "paidAmount": { + "type": "number", + "format": "double" + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "finishedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelledAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "cancelReason": { + "type": "string", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdatePaymentCommand": { + "type": "object", + "properties": { + "paymentId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "method": { + "$ref": "#/components/schemas/PaymentMethod" + }, + "status": { + "$ref": "#/components/schemas/PaymentStatus" + }, + "amount": { + "type": "number", + "format": "double" + }, + "tradeNo": { + "type": "string", + "nullable": true + }, + "channelTransactionId": { + "type": "string", + "nullable": true + }, + "paidAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "remark": { + "type": "string", + "nullable": true + }, + "payload": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdatePermissionCommand": { + "type": "object", + "properties": { + "permissionId": { + "type": "integer", + "format": "int64" + }, + "parentId": { + "type": "integer", + "format": "int64" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateProductCommand": { + "type": "object", + "properties": { + "productId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "categoryId": { + "type": "integer", + "format": "int64" + }, + "spuCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "subtitle": { + "type": "string", + "nullable": true + }, + "unit": { + "type": "string", + "nullable": true + }, + "price": { + "type": "number", + "format": "double" + }, + "originalPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "stockQuantity": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ProductStatus" + }, + "coverImage": { + "type": "string", + "nullable": true + }, + "galleryImages": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "enableDineIn": { + "type": "boolean" + }, + "enablePickup": { + "type": "boolean" + }, + "enableDelivery": { + "type": "boolean" + }, + "isFeatured": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateRoleCommand": { + "type": "object", + "properties": { + "roleId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateRoleTemplateCommand": { + "type": "object", + "properties": { + "templateCode": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "permissionCodes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateStoreBusinessHourCommand": { + "type": "object", + "properties": { + "businessHourId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "dayOfWeek": { + "$ref": "#/components/schemas/DayOfWeek" + }, + "hourType": { + "$ref": "#/components/schemas/BusinessHourType" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "capacityLimit": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateStoreCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64" + }, + "code": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "managerName": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StoreStatus" + }, + "province": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "district": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "longitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "latitude": { + "type": "number", + "format": "double", + "nullable": true + }, + "announcement": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "string", + "nullable": true + }, + "deliveryRadiusKm": { + "type": "number", + "format": "double" + }, + "supportsDineIn": { + "type": "boolean" + }, + "supportsPickup": { + "type": "boolean" + }, + "supportsDelivery": { + "type": "boolean" + }, + "supportsReservation": { + "type": "boolean" + }, + "supportsQueueing": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateStoreDeliveryZoneCommand": { + "type": "object", + "properties": { + "deliveryZoneId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "zoneName": { + "type": "string", + "nullable": true + }, + "polygonGeoJson": { + "type": "string", + "nullable": true + }, + "minimumOrderAmount": { + "type": "number", + "format": "double", + "nullable": true + }, + "deliveryFee": { + "type": "number", + "format": "double", + "nullable": true + }, + "estimatedMinutes": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "UpdateStoreEmployeeShiftCommand": { + "type": "object", + "properties": { + "shiftId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "staffId": { + "type": "integer", + "format": "int64" + }, + "shiftDate": { + "type": "string", + "format": "date-time" + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + }, + "notes": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateStoreHolidayCommand": { + "type": "object", + "properties": { + "holidayId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "isClosed": { + "type": "boolean" + }, + "reason": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "UpdateStorePickupSlotCommand": { + "type": "object", + "properties": { + "slotId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "startTime": { + "type": "string", + "format": "date-span" + }, + "endTime": { + "type": "string", + "format": "date-span" + }, + "cutoffMinutes": { + "type": "integer", + "format": "int32" + }, + "capacity": { + "type": "integer", + "format": "int32" + }, + "weekdays": { + "type": "string", + "nullable": true + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateStoreStaffCommand": { + "type": "object", + "properties": { + "staffId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "roleType": { + "$ref": "#/components/schemas/StaffRoleType" + }, + "status": { + "$ref": "#/components/schemas/StaffStatus" + } + }, + "additionalProperties": false + }, + "UpdateStoreTableAreaCommand": { + "type": "object", + "properties": { + "areaId": { + "type": "integer", + "format": "int64" + }, + "storeId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "UpdateStoreTableCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "tableId": { + "type": "integer", + "format": "int64" + }, + "areaId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "tableCode": { + "type": "string", + "nullable": true + }, + "capacity": { + "type": "integer", + "format": "int32" + }, + "tags": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StoreTableStatus" + } + }, + "additionalProperties": false + }, + "UpdateSystemParameterCommand": { + "type": "object", + "properties": { + "parameterId": { + "type": "integer", + "format": "int64" + }, + "key": { + "type": "string", + "nullable": true + }, + "value": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "isEnabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateTenantAnnouncementCommand": { + "type": "object", + "properties": { + "tenantId": { + "type": "integer", + "format": "int64" + }, + "announcementId": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string", + "nullable": true + }, + "announcementType": { + "$ref": "#/components/schemas/TenantAnnouncementType" + }, + "priority": { + "type": "integer", + "format": "int32" + }, + "effectiveFrom": { + "type": "string", + "format": "date-time" + }, + "effectiveTo": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "isActive": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "UpdateTenantPackageCommand": { + "type": "object", + "properties": { + "tenantPackageId": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "packageType": { + "$ref": "#/components/schemas/TenantPackageType" + }, + "monthlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "yearlyPrice": { + "type": "number", + "format": "double", + "nullable": true + }, + "maxStoreCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxAccountCount": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxStorageGb": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxSmsCredits": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "maxDeliveryOrders": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "featurePoliciesJson": { + "type": "string", + "nullable": true + }, + "isActive": { + "type": "boolean" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "UpsertInventoryBatchCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "productSkuId": { + "type": "integer", + "format": "int64" + }, + "batchNumber": { + "type": "string", + "nullable": true + }, + "productionDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expireDate": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "remainingQuantity": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "UpsertStorePickupSettingCommand": { + "type": "object", + "properties": { + "storeId": { + "type": "integer", + "format": "int64" + }, + "allowToday": { + "type": "boolean" + }, + "allowDaysAhead": { + "type": "integer", + "format": "int32" + }, + "defaultCutoffMinutes": { + "type": "integer", + "format": "int32" + }, + "maxQuantityPerOrder": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "UserPermissionDto": { + "type": "object", + "properties": { + "userId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "integer", + "format": "int64" + }, + "merchantId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "account": { + "type": "string", + "nullable": true + }, + "displayName": { + "type": "string", + "nullable": true + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "UserPermissionDtoApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/UserPermissionDto" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + }, + "UserPermissionDtoPagedResult": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserPermissionDto" + }, + "description": "数据列表。", + "nullable": true + }, + "page": { + "type": "integer", + "description": "当前页码,从 1 开始。", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "description": "每页条数。", + "format": "int32" + }, + "totalCount": { + "type": "integer", + "description": "总条数。", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "description": "总页数。", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false, + "description": "分页结果包装,携带列表与总条数等元数据。" + }, + "UserPermissionDtoPagedResultApiResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "是否成功。" + }, + "code": { + "type": "integer", + "description": "状态/错误码(默认 200)。", + "format": "int32" + }, + "message": { + "type": "string", + "description": "提示信息。", + "nullable": true + }, + "data": { + "$ref": "#/components/schemas/UserPermissionDtoPagedResult" + }, + "errors": { + "description": "错误详情(如字段验证错误)。", + "nullable": true + }, + "traceId": { + "type": "string", + "description": "TraceId,便于链路追踪。", + "nullable": true + }, + "timestamp": { + "type": "string", + "description": "时间戳(UTC)。", + "format": "date-time" + } + }, + "additionalProperties": false, + "description": "统一的 API 返回结果包装。" + } + }, + "securitySchemes": { + "Bearer": { + "type": "http", + "description": "在下方输入Bearer Token,格式:Bearer {token}", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + }, + "security": [ + { + "Bearer": [ ] + } + ], + "tags": [ + { + "name": "Auth", + "description": "管理后台认证接口" + }, + { + "name": "Deliveries", + "description": "配送单管理。" + }, + { + "name": "Dictionary", + "description": "参数字典管理。" + }, + { + "name": "Files", + "description": "管理后台文件上传。" + }, + { + "name": "Health", + "description": "管理后台 - 健康检查。" + }, + { + "name": "Inventory", + "description": "库存管理。" + }, + { + "name": "Menus", + "description": "菜单管理(可增删改查)。" + }, + { + "name": "MerchantCategories", + "description": "商户类目管理。" + }, + { + "name": "Merchants", + "description": "商户管理。" + }, + { + "name": "Orders", + "description": "订单管理。" + }, + { + "name": "Payments", + "description": "支付记录管理。" + }, + { + "name": "Permissions", + "description": "权限管理。" + }, + { + "name": "Products", + "description": "商品管理。" + }, + { + "name": "PublicTenantPackages", + "description": "公共租户套餐查询接口。" + }, + { + "name": "PublicTenants", + "description": "公域租户自助入住接口。" + }, + { + "name": "PublicTenantSubscriptions", + "description": "公域租户订阅自助接口(需登录,无权限校验)。" + }, + { + "name": "RoleTemplates", + "description": "角色模板管理(平台蓝本)。" + }, + { + "name": "StorePickup", + "description": "门店自提管理。" + }, + { + "name": "Stores", + "description": "门店管理。" + }, + { + "name": "StoreShifts", + "description": "门店排班管理。" + }, + { + "name": "StoreStaffs", + "description": "门店员工管理。" + }, + { + "name": "StoreTableAreas", + "description": "门店桌台区域管理。" + }, + { + "name": "StoreTables", + "description": "门店桌码管理。" + }, + { + "name": "SystemParameters", + "description": "系统参数管理。" + }, + { + "name": "TenantAnnouncements", + "description": "租户公告管理。" + }, + { + "name": "TenantBillings", + "description": "租户账单管理。" + }, + { + "name": "TenantNotifications", + "description": "租户通知接口。" + }, + { + "name": "TenantPackages", + "description": "租户套餐管理。" + }, + { + "name": "TenantRoles", + "description": "租户角色管理(实例层)。" + }, + { + "name": "Tenants", + "description": "租户管理。" + }, + { + "name": "UserPermissions", + "description": "用户权限洞察接口。" + } + ] +} \ No newline at end of file diff --git a/Document/xmind_小程序版模块规划.md b/Document/xmind_小程序版模块规划.md new file mode 100644 index 0000000..3af2007 --- /dev/null +++ b/Document/xmind_小程序版模块规划.md @@ -0,0 +1,89 @@ +# 外卖SaaS(小程序导向)模块脑图 + +## 1. 目标 +- 聚焦小程序用户体验:高效点单、扫码堂食、同城履约、实时状态 +- 平台/租户多租户隔离,支持商家快速入驻与运营 +- 门店同城即时配送为主,第三方配送对接兜底,统一回调验签 + +## 2. 端侧 +- 小程序用户端:扫码进店/堂食点餐、店铺浏览、菜品/套餐、购物车、下单支付、拼单、预购自提、同城配送、订单跟踪、优惠券/积分/会员、营销活动、评价、地址管理、搜索、签到、预约、社区、客服聊天、地图导航 +- 管理后台(商家/店员):门店配置、桌码管理、菜品与库存、订单/拼单/自提处理、配送调度与回调、营销配置、分销配置、客服工单/会话、数据看板 +- 平台运营后台:租户/商家审核、套餐与配额、全局配置、日志审计、监控看板、分销/返利策略、活动审核 + +## 3. 核心业务模块 +- 租户与平台 + - 租户生命周期:注册、实名认证/资质审核、套餐订阅、续费/升降配、停用/注销 + - 配额与权限:门店数/账号数/存储/短信/配送单量,RBAC 角色模板,租户级配置继承与覆盖 + - 平台运营:租户监控、账单/计费/催缴、公告/通知、合规与风控(黑名单/频率限制) +- 商家与门店 + - 入驻与资质审核:证照上传、合同、店铺类目、品牌与连锁 + - 门店配置:多店管理、营业时间、休息/打烊、门店状态、区域/配送范围、到店导航信息 + - 桌码/场景:桌码生成与绑定、桌台容量/区域、桌码标签(堂食/快餐/档口) + - 门店运营:员工账号与分工(前台/后厨/配送)、交班与收银、公告/售罄提示 +- 菜品与库存 + - 菜品建模:分类/标签/排序、规格与属性(辣度/份量)、套餐与组合、加料/做法/备注 + - 价格策略:会员价、门店价、时间段价、限时折扣、区域价 + - 库存与可售:总库存/门店库存/档期库存、售罄/预售、批量导入与同步、条码/编码 + - 媒资:图片/视频、SPU/SKU 编码、营养/过敏源/溯源信息 +- 扫码点餐/堂食 + - 桌码入口:扫码识别门店/桌台、桌台上下文、预加载菜单 + - 点餐流程:购物车并发锁、加菜/催单、口味备注、拆单/并单、多人同桌分付/代付 + - 账单与核销:桌台账单合并、结账/买单、电子小票、发票抬头、桌台释放 +- 预购自提 + - 档期配置:自提时间窗/容量、预售库存、截单时间 + - 下单与支付:自提地址确认、档期校验、预售与现制、库存锁定 + - 提货核销:提货码/手机号核销、自提柜/前台、超时/取消/退款、代取人 +- 下单与支付 + - 购物车与校验:库存/限购/门店状态/配送范围/桌台状态、券和积分叠加规则 + - 支付与结算:微信/支付宝/余额/优惠券/积分抵扣、回调幂等、预授权、分账/对账 + - 售后与状态机:退单/部分退款、异常单处理、状态机(待付→待接→制作→配送/自提→完成/取消) +- 同城即时配送(门店履约) + - 自配送:骑手管理、取/送件信息、路线估时、导航、取餐码、费用与补贴 + - 第三方配送:统一抽象(下单/取消/加价/查询)、多渠道择优、回调验签、异常重试与补偿 + - 配送体验:预计送达/计价、配送进度推送、无接触配送、收货码、投诉与赔付 +- 拼单 + - 团单规则:发起/加入、成团条件(人数/金额/时间)、拼主与参团人、支付/锁单策略 + - 失败与退款:超时/人数不足自动解散、自动退款/原路退、通知 + - 履约:同单配送至拼主地址、收件信息可见范围、团内消息/提醒 +- 优惠券与营销工具 + - 券种与发放:满减/折扣/新人券/裂变券/桌码专属券/会员券,渠道(领券中心/活动/分享/桌码) + - 核销规则:适用范围(门店/品类/菜品)、叠加/互斥、最低消费、有效期、库存与风控 + - 活动组件:抽奖、分享裂变、秒杀/限时抢购、满减活动、爆款/推荐位、裂变海报 +- 会员与积分/会员营销 + - 会员体系:付费会员/积分会员、等级/成长值、会员权益(专享价/券/运费/客服) + - 积分运营:获取(消费/签到/任务/活动)、消耗(抵扣/兑换)、有效期、黑名单 + - 会员运营:会员日、续费提醒、沉睡唤醒、专属活动、分层运营与 A/B +- 客服工具 + - 会话:实时聊天、机器人/人工切换、快捷回复、排队/转接、消息模板/通知 + - 质量与风控:会话记录与审计、敏感词、评价与回访、工单流转 +- 分销返利 + - 规则:商品/类目返利、佣金阶梯、结算周期、税务信息、违规处理 + - 链路:分享链接/小程序码、点击与下单跟踪、确认收货后结算、售后扣回 +- 地图导航 + - 门店/自提点定位、距离/路况、路线规划、附近门店/推荐、跳转原生导航 +- 签到打卡 + - 规则:每日/任务签到、连签奖励、补签、积分/券/成长值奖励、反作弊 +- 预约预订 + - 档期/资源:时间段、人员/座位/设备占用、容量校验 + - 流程:预约下单/支付、提醒/改期/取消、到店核销与履约记录 +- 搜索查询 + - 内容:门店/菜品/活动/优惠券搜索,过滤/排序,热门/历史搜索 + - 体验:纠错/联想、推荐、结果埋点与转化分析 +- 用户社区 + - 社区运营:动态发布/评论/点赞、话题/标签、图片/视频审核、举报与风控 + - 互动:店铺口碑、菜品晒图、官方/商家号发布、置顶与精选 +- 数据与分析 + - 交易分析:销售/订单/客单/转化漏斗、支付与退款、品类/单品分析 + - 营销分析:券发放与核销、活动效果(拼单/秒杀/抽奖/分销)、会员留存与复购 + - 履约分析:配送时效、超时/异常、堂食与自提拆分、投诉与赔付 + - 运营大盘:租户/商家健康度、活跃度、留存、GMV、成本与毛利 +- 系统与运维 + - 安全与合规:RBAC、租户隔离、限流与风控(设备/IP/账户/店铺)、敏感词与内容安全 + - 配置与网关:字典/参数、灰度/开关、网关限流/鉴权/租户透传 + - 可靠性:任务调度(订单/拼单超时、券过期、回调补偿)、幂等与重试、健康检查/告警、日志/链路追踪、备份恢复 + +## 4. 里程碑(建议) +- Phase 1:租户/商家入驻、门店与菜品、桌码扫码堂食、基础下单支付、预购自提、第三方配送骨架 +- Phase 2:拼单、优惠券与基础营销组件、会员积分/会员日、客服聊天、同城自配送调度、搜索 +- Phase 3:分销返利、签到打卡、预约预订、地图导航、社区、营销活动丰富(秒杀/抽奖等)、风控与审计、补偿与告警体系 +- Phase 4:性能优化与缓存、运营大盘与细分报表、测试与文档完善、上线与监控 diff --git a/Document/xmind_小程序版模块规划.xmind b/Document/xmind_小程序版模块规划.xmind new file mode 100644 index 0000000..9df8073 Binary files /dev/null and b/Document/xmind_小程序版模块规划.xmind differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..caaabce --- /dev/null +++ b/README.md @@ -0,0 +1,231 @@ +# 外卖SaaS系统 (TakeoutSaaS) + +## 项目简介 + +外卖SaaS系统是一个基于.NET 10的多租户外卖管理平台,为中小型餐饮企业提供完整的外卖业务解决方案。系统采用现代化的技术栈,支持商家管理、菜品管理、订单处理、配送管理、支付集成等核心功能。 + +### 核心特性 + +- **多租户架构**:支持多租户数据隔离,SaaS模式运营 +- **商家管理**:完善的商家入驻、门店管理、菜品管理功能 +- **订单管理**:订单全生命周期管理,实时状态跟踪 +- **配送管理**:配送任务、路线规划、第三方配送对接 +- **支付集成**:支持微信、支付宝等多种支付方式 +- **营销功能**:优惠券、满减活动、会员积分 +- **字典管理**:系统字典、租户覆盖、批量导入导出、缓存监控 +- **数据分析**:实时数据统计、经营报表、趋势分析 +- **安全可靠**:JWT认证、权限控制、数据加密 + +## 技术栈 + +### 后端技术 +- **.NET 10**:最新的.NET平台 +- **ASP.NET Core Web API**:RESTful API服务 +- **Entity Framework Core 10**:最新ORM框架 +- **Dapper 2.1+**:高性能数据访问 +- **PostgreSQL 16+**:主数据库 +- **Redis 7.0+**:分布式缓存 +- **RabbitMQ 3.12+**:消息队列 + +### 开发框架 +- **AutoMapper**:对象映射 +- **FluentValidation**:数据验证 +- **Serilog**:结构化日志 +- **MediatR**:CQRS模式 +- **Hangfire**:后台任务 +- **Swagger**:API文档 + +## 运行条件 + +### 开发环境要求 +* .NET SDK 10.0 或更高版本 +* PostgreSQL 16+ +* Redis 7.0+ +* RabbitMQ 3.12+(可选) +* Docker Desktop(推荐,用于容器化开发) + +### 推荐IDE +* Visual Studio 2022 +* JetBrains Rider +* Visual Studio Code + +## 快速开始 + +### 1. 克隆项目 +```bash +git clone https://github.com/your-org/takeout-saas.git +cd takeout-saas +``` + +### 2. 使用Docker Compose启动依赖服务(推荐) +```bash +# 启动PostgreSQL、Redis、RabbitMQ等服务 +docker-compose up -d + +# 查看服务状态 +docker-compose ps +``` + +### 3. 配置数据库连接 +编辑 `src/TakeoutSaaS.Api/appsettings.Development.json` + +### 4. 执行数据库迁移 +```bash +cd src/TakeoutSaaS.Api +dotnet ef database update +``` + +### 5. 运行项目 +```bash +dotnet run +``` + +访问 API 文档: +- 管理后台 AdminApi Swagger:http://localhost:5001/api/docs +- 小程序/用户端 MiniApi Swagger:http://localhost:5002/api/docs + +## 字典管理 + +> 最后更新日期:2025-12-30 + +### 功能概述 + +- 系统/业务字典分组与字典项管理 +- 租户覆盖:隐藏系统项、自定义字典项、拖拽排序 +- CSV/JSON 批量导入导出 +- 两级缓存(Memory + Redis)与缓存监控指标 + +### 配置要点 + +- `ConnectionStrings:Redis`:Redis 连接字符串 +- `Database:DataSources:DictionaryDatabase`:字典库读写连接 +- `Dictionary:Cache:SlidingExpiration`:字典缓存滑动过期 +- `CacheWarmup:DictionaryCodes`:缓存预热字典编码列表 + +## 公告管理 + +> 最后更新日期:2025-12-20 + +### 功能概述 + +- 支持平台公告与租户公告统一管理(TenantId=0 代表平台公告) +- 状态机:草稿 → 已发布 → 已撤销(已发布不可编辑) +- 支持目标受众过滤与未读/已读能力 + +### 快速开始(示例流程) + +1. 创建公告(草稿) +2. 发布公告(进入 Published) +3. 应用端获取可见公告列表与未读公告 + +```mermaid +stateDiagram-v2 + [*] --> Draft + Draft --> Published: publish + Published --> Revoked: revoke + Revoked --> Published: republish +``` + +### 关键概念 + +- `Status`:Draft/Published/Revoked,已发布不可编辑 +- `RowVersion`:并发控制字段(Base64) +- `TargetType/TargetParameters`:目标受众过滤 + +### 相关文档 + +- `docs/api/announcements-api.md` +- `docs/permissions/announcement-permissions.md` +- `docs/adr/0001-announcement-status-state-machine.md` +- `docs/observability/announcement-events.md` +- `docs/migrations/announcement-status-migration.md` +- `docs/technical-debt.md` + +## 项目结构 + +``` +TakeoutSaaS/ +├── 0_Document/ # 项目文档 +│ ├── 01_项目概述.md +│ ├── 02_技术架构.md +│ ├── 03_数据库设计.md +│ ├── 04A_管理后台API.md +│ ├── 04B_小程序API.md +│ ├── 05_部署运维.md +│ └── 06_开发规范.md +├── src/ +│ ├── TakeoutSaaS.AdminApi/ # 管理后台 Web API +│ ├── TakeoutSaaS.MiniApi/ # 小程序/用户端 Web API +│ ├── TakeoutSaaS.Application/ # 应用层 +│ ├── TakeoutSaaS.Domain/ # 领域层 +│ ├── TakeoutSaaS.Infrastructure/ # 基础设施层 +│ └── TakeoutSaaS.Shared/ # 共享层 +├── tests/ +│ ├── TakeoutSaaS.UnitTests/ # 单元测试 +│ └── TakeoutSaaS.IntegrationTests/ # 集成测试 +├── docker-compose.yml # Docker编排文件 +└── README.md +``` + +## 测试说明 + +### 运行单元测试 +```bash +dotnet test tests/TakeoutSaaS.UnitTests +``` + +### 运行集成测试 +```bash +dotnet test tests/TakeoutSaaS.IntegrationTests +``` + +## 部署说明 + +### Docker部署 +```bash +# 构建镜像 +docker build -t takeout-saas-api:latest . + +# 运行容器 +docker run -d -p 8080:80 --name takeout-api takeout-saas-api:latest +``` + +详细部署文档请参考:[部署运维文档](0_Document/05_部署运维.md) + +## 文档 + +- [项目概述](0_Document/01_项目概述.md) - 系统介绍和业务说明 +- [技术架构](0_Document/02_技术架构.md) - 技术栈和架构设计 +- [数据库设计](0_Document/03_数据库设计.md) - 数据模型和表结构 +- [API接口设计](0_Document/04_API接口设计.md) - RESTful API规范 +- [部署运维](0_Document/05_部署运维.md) - 部署和运维指南 +- [开发规范](0_Document/06_开发规范.md) - 代码规范和最佳实践 + +## 开发规范 + +请遵循项目的[开发规范](0_Document/06_开发规范.md) + +## 贡献指南 + +1. Fork 本仓库 +2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'feat: Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 创建 Pull Request + +## 许可证 + +本项目采用 MIT 许可证 + +## 联系方式 + +- 项目地址:https://github.com/your-org/takeout-saas +- 问题反馈:https://github.com/your-org/takeout-saas/issues + +## 协作者 + +感谢所有为本项目做出贡献的开发者! + +--- + +⭐ 如果这个项目对你有帮助,请给我们一个星标! diff --git a/deploy/dbhub/dbhub.toml b/deploy/dbhub/dbhub.toml new file mode 100644 index 0000000..814bcb7 --- /dev/null +++ b/deploy/dbhub/dbhub.toml @@ -0,0 +1,15 @@ +[[sources]] +id = "takeout_app" +dsn = "postgres://app_user:AppUser112233@120.53.222.17:5432/takeout_app_db" + +[[sources]] +id = "takeout_identity" +dsn = "postgres://identity_user:IdentityUser112233@120.53.222.17:5432/takeout_identity_db" + +[[sources]] +id = "takeout_dictionary" +dsn = "postgres://dictionary_user:DictionaryUser112233@120.53.222.17:5432/takeout_dictionary_db" + +[[sources]] +id = "takeout_hangfire" +dsn = "postgres://hangfire_user:HangFire112233@120.53.222.17:5432/takeout_hangfire_db" diff --git a/deploy/postgres/README.md b/deploy/postgres/README.md new file mode 100644 index 0000000..04e7222 --- /dev/null +++ b/deploy/postgres/README.md @@ -0,0 +1,47 @@ +# PostgreSQL 部署脚本 + +本目录提供在测试/预发布环境快速拉起 PostgreSQL 的脚本,复用线上同名数据库与账号,方便迁移/恢复。 + +## 目录结构 + +- `create_databases.sql`:创建四个业务库与对应角色(可多次执行,存在则跳过)。 +- `bootstrap.ps1`:PowerShell 包装脚本,调用 `psql` 执行 SQL。 + +## 前置条件 + +1. 已安装 PostgreSQL 12+,并能以管理员身份访问(默认使用 `postgres`)。 +2. 本地已配置 `psql` 可执行命令。 + +## 使用方法 + +```powershell +cd deploy/postgres +.\bootstrap.ps1 ` + -Host 120.53.222.17 ` + -Port 5432 ` + -AdminUser postgres ` + -AdminPassword "超级管理员密码" +``` + +脚本会: + +1. 创建/更新以下角色与库: + - `app_user` / `takeout_app_db` + - `identity_user` / `takeout_identity_db` + - `dictionary_user` / `takeout_dictionary_db` + - `hangfire_user` / `takeout_hangfire_db` +2. 为库设置 COMMENT,授予 Schema `public` 的 CRUD 权限。 +3. 输出执行日志,失败时终止。 + +## 自定义 + +- 如需修改密码或新增库,编辑 `create_databases.sql` 后重新运行脚本。 +- 若在本地拉起测试库,可把 `Host` 指向 `localhost`,其余参数保持一致。 + +## 常见问题 + +| 问题 | 处理方式 | +| --- | --- | +| `psql : command not found` | 确认 PostgreSQL bin 目录已加入 PATH。 | +| `permission denied to create database` | 改用具有 `CREATEDB` 权限的管理员执行脚本。 | +| 需要删除库 | 先 `DROP DATABASE xxx`,再运行脚本重新创建。 | diff --git a/deploy/postgres/bootstrap.ps1 b/deploy/postgres/bootstrap.ps1 new file mode 100644 index 0000000..3ea0bea --- /dev/null +++ b/deploy/postgres/bootstrap.ps1 @@ -0,0 +1,37 @@ +param( + [string]$Host = "120.53.222.17", + [int]$Port = 5432, + [string]$AdminUser = "postgres", + [string]$AdminPassword = "" +) + +if (-not (Get-Command psql -ErrorAction SilentlyContinue)) { + throw "psql command not found. Add PostgreSQL bin directory to PATH." +} + +if ([string]::IsNullOrWhiteSpace($AdminPassword)) { + Write-Warning "AdminPassword not provided. You will be prompted by psql." +} + +$sqlPath = Join-Path $PSScriptRoot "create_databases.sql" +if (-not (Test-Path $sqlPath)) { + throw "Cannot find create_databases.sql under $PSScriptRoot." +} + +$env:PGPASSWORD = $AdminPassword + +$arguments = @( + "-h", $Host, + "-p", $Port, + "-U", $AdminUser, + "-f", $sqlPath +) + +Write-Host "Executing create_databases.sql on $Host:$Port as $AdminUser ..." +& psql @arguments + +if ($LASTEXITCODE -ne 0) { + throw "psql returned non-zero exit code ($LASTEXITCODE)." +} + +Write-Host "PostgreSQL databases and roles ensured successfully." diff --git a/deploy/postgres/create_databases.sql b/deploy/postgres/create_databases.sql new file mode 100644 index 0000000..afc0817 --- /dev/null +++ b/deploy/postgres/create_databases.sql @@ -0,0 +1,102 @@ +-- Reusable provisioning script for Takeout SaaS PostgreSQL databases. +-- Execute with a superuser (e.g. postgres). Safe to re-run. + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_user') THEN + CREATE ROLE app_user LOGIN PASSWORD 'AppUser112233'; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'identity_user') THEN + CREATE ROLE identity_user LOGIN PASSWORD 'IdentityUser112233'; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dictionary_user') THEN + CREATE ROLE dictionary_user LOGIN PASSWORD 'DictionaryUser112233'; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'hangfire_user') THEN + CREATE ROLE hangfire_user LOGIN PASSWORD 'HangFire112233'; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'logs_user') THEN + CREATE ROLE logs_user LOGIN PASSWORD 'Logs112233'; + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_app_db') THEN + CREATE DATABASE takeout_app_db OWNER app_user ENCODING 'UTF8'; + END IF; +END $$; +COMMENT ON DATABASE takeout_app_db IS 'Takeout SaaS 业务域数据库'; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_identity_db') THEN + CREATE DATABASE takeout_identity_db OWNER identity_user ENCODING 'UTF8'; + END IF; +END $$; +COMMENT ON DATABASE takeout_identity_db IS 'Takeout SaaS 身份域数据库'; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_dictionary_db') THEN + CREATE DATABASE takeout_dictionary_db OWNER dictionary_user ENCODING 'UTF8'; + END IF; +END $$; +COMMENT ON DATABASE takeout_dictionary_db IS 'Takeout SaaS 字典域数据库'; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_hangfire_db') THEN + CREATE DATABASE takeout_hangfire_db OWNER hangfire_user ENCODING 'UTF8'; + END IF; +END $$; +COMMENT ON DATABASE takeout_hangfire_db IS 'Takeout SaaS 调度/Hangfire 数据库'; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_logs_db') THEN + CREATE DATABASE takeout_logs_db OWNER logs_user ENCODING 'UTF8'; + END IF; +END $$; +COMMENT ON DATABASE takeout_logs_db IS 'Takeout SaaS 审计/日志数据库'; + +-- Ensure privileges and default schema permissions +\connect takeout_app_db +GRANT CONNECT, TEMP ON DATABASE takeout_app_db TO app_user; +GRANT USAGE ON SCHEMA public TO app_user; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO app_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO app_user; + +\connect takeout_identity_db +GRANT CONNECT, TEMP ON DATABASE takeout_identity_db TO identity_user; +GRANT USAGE ON SCHEMA public TO identity_user; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO identity_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO identity_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO identity_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO identity_user; + +\connect takeout_dictionary_db +GRANT CONNECT, TEMP ON DATABASE takeout_dictionary_db TO dictionary_user; +GRANT USAGE ON SCHEMA public TO dictionary_user; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO dictionary_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO dictionary_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO dictionary_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO dictionary_user; + +\connect takeout_hangfire_db +GRANT CONNECT, TEMP ON DATABASE takeout_hangfire_db TO hangfire_user; +GRANT USAGE ON SCHEMA public TO hangfire_user; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO hangfire_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO hangfire_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO hangfire_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO hangfire_user; + +\connect takeout_logs_db +GRANT CONNECT, TEMP ON DATABASE takeout_logs_db TO logs_user; +GRANT USAGE ON SCHEMA public TO logs_user; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO logs_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO logs_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO logs_user; +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO logs_user; diff --git a/deploy/postgres/migrate_logs_to_logs_db.sql b/deploy/postgres/migrate_logs_to_logs_db.sql new file mode 100644 index 0000000..95892c8 --- /dev/null +++ b/deploy/postgres/migrate_logs_to_logs_db.sql @@ -0,0 +1,89 @@ +-- 日志库迁移脚本(请在 psql 中按步骤执行) + +-- 1. 在日志库创建表结构(takeout_logs_db) +\connect takeout_logs_db + +CREATE TABLE IF NOT EXISTS tenant_audit_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "TenantId" bigint NOT NULL, + "Action" integer NOT NULL, + "Title" character varying(128) NOT NULL, + "Description" character varying(1024), + "OperatorId" bigint, + "OperatorName" character varying(64), + "PreviousStatus" integer, + "CurrentStatus" integer, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "DeletedAt" timestamp with time zone, + "CreatedBy" bigint, + "UpdatedBy" bigint, + "DeletedBy" bigint +); +CREATE INDEX IF NOT EXISTS "IX_tenant_audit_logs_TenantId" ON tenant_audit_logs ("TenantId"); + +CREATE TABLE IF NOT EXISTS merchant_audit_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "MerchantId" bigint NOT NULL, + "Action" integer NOT NULL, + "Title" character varying(128) NOT NULL, + "Description" character varying(1024), + "OperatorId" bigint, + "OperatorName" character varying(64), + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "DeletedAt" timestamp with time zone, + "CreatedBy" bigint, + "UpdatedBy" bigint, + "DeletedBy" bigint, + "TenantId" bigint NOT NULL +); +CREATE INDEX IF NOT EXISTS "IX_merchant_audit_logs_TenantId_MerchantId" ON merchant_audit_logs ("TenantId", "MerchantId"); + +CREATE TABLE IF NOT EXISTS operation_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "OperationType" character varying(64) NOT NULL, + "TargetType" character varying(64) NOT NULL, + "TargetIds" text, + "OperatorId" character varying(64), + "OperatorName" character varying(128), + "Parameters" text, + "Result" text, + "Success" boolean NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "DeletedAt" timestamp with time zone, + "CreatedBy" bigint, + "UpdatedBy" bigint, + "DeletedBy" bigint +); +CREATE INDEX IF NOT EXISTS "IX_operation_logs_CreatedAt" ON operation_logs ("CreatedAt"); +CREATE INDEX IF NOT EXISTS "IX_operation_logs_OperationType_CreatedAt" ON operation_logs ("OperationType", "CreatedAt"); + +CREATE TABLE IF NOT EXISTS member_growth_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "MemberId" bigint NOT NULL, + "ChangeValue" integer NOT NULL, + "CurrentValue" integer NOT NULL, + "Notes" character varying(256), + "OccurredAt" timestamp with time zone NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "DeletedAt" timestamp with time zone, + "CreatedBy" bigint, + "UpdatedBy" bigint, + "DeletedBy" bigint, + "TenantId" bigint NOT NULL +); +CREATE INDEX IF NOT EXISTS "IX_member_growth_logs_TenantId_MemberId_OccurredAt" ON member_growth_logs ("TenantId", "MemberId", "OccurredAt"); + +-- 2. 迁移数据(建议使用 pg_dump/pg_restore 或应用侧批量拷贝) +-- 示例:pg_dump -t tenant_audit_logs -t merchant_audit_logs -t operation_logs -t member_growth_logs takeout_app_db > logs_dump.sql +-- psql -d takeout_logs_db -f logs_dump.sql + +-- 3. 在业务库删除旧日志表(takeout_app_db) +\connect takeout_app_db +DROP TABLE IF EXISTS tenant_audit_logs; +DROP TABLE IF EXISTS merchant_audit_logs; +DROP TABLE IF EXISTS operation_logs; +DROP TABLE IF EXISTS member_growth_logs; diff --git a/deploy/prometheus/alert.rules.yml b/deploy/prometheus/alert.rules.yml new file mode 100644 index 0000000..a74f845 --- /dev/null +++ b/deploy/prometheus/alert.rules.yml @@ -0,0 +1,34 @@ +groups: + - name: takeoutsaas-app + interval: 30s + rules: + - alert: HighErrorRate + expr: | + sum(rate(http_server_request_duration_seconds_count{http_response_status_code=~"5.."}[5m])) + / sum(rate(http_server_request_duration_seconds_count[5m])) > 0.05 + for: 5m + labels: + severity: critical + annotations: + summary: "API 5xx 错误率过高" + description: "过去 5 分钟 5xx 占比超过 5%,请检查依赖或发布" + + - alert: HighP95Latency + expr: | + histogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket[5m])) by (le, service_name)) + > 1 + for: 5m + labels: + severity: warning + annotations: + summary: "API P95 延迟过高" + description: "过去 5 分钟 P95 超过 1s,请排查热点接口或依赖" + + - alert: InstanceDown + expr: up{job=~"admin-api|mini-api|user-api"} == 0 + for: 2m + labels: + severity: critical + annotations: + summary: "实例不可达" + description: "Prometheus 抓取失败,实例处于 down 状态" diff --git a/deploy/prometheus/prometheus.yml b/deploy/prometheus/prometheus.yml new file mode 100644 index 0000000..3385f12 --- /dev/null +++ b/deploy/prometheus/prometheus.yml @@ -0,0 +1,28 @@ +global: + scrape_interval: 15s + evaluation_interval: 30s + +rule_files: + - alert.rules.yml + +scrape_configs: + - job_name: admin-api + metrics_path: /metrics + static_configs: + - targets: ["admin-api:8080"] + labels: + service: admin-api + + - job_name: mini-api + metrics_path: /metrics + static_configs: + - targets: ["mini-api:8080"] + labels: + service: mini-api + + - job_name: user-api + metrics_path: /metrics + static_configs: + - targets: ["user-api:8080"] + labels: + service: user-api diff --git a/deploy/redis/README.md b/deploy/redis/README.md new file mode 100644 index 0000000..c955032 --- /dev/null +++ b/deploy/redis/README.md @@ -0,0 +1,34 @@ +# Redis 部署脚本 + +本目录提供可复用的 Redis 配置,既可在本地通过 Docker Compose 启动,也可将 `redis.conf` 拷贝到现有服务器,确保与线上一致。 + +## 1. 部署步骤 (裸机)\n\n1. 将 \\ edis.conf\\ 拷贝到服务器(例如 /etc/redis/redis.conf)。\n2. 根据需要修改数据目录(\\dir\\)和绑定地址。\n3. 使用系统服务或 \\ edis-server redis.conf\\ 启动。\n4. 确认开放端口 6379,保证通过 \\ edis-cli -h -a ping\\ 可访问。\n\n## 2. 配置说明\n\n- \\ equirepass\\ 已设置为 MsuMshk112233。\n- 启用 appendonly(AOF),并每秒 fsync。\n- \\maxmemory-policy\\ 为 allkeys-lru,适合缓存场景。\n- \\protected-mode no\\ 允许远程连接,需结合安全组或防火墙限制来源 IP。\n\n## 3. 常用命令使用 `redis.conf` + +1. 把 `redis.conf` 拷贝到服务器 `/etc/redis/redis.conf`(或自定义目录)。 +2. 修改 `dir` 指向实际数据目录。 +3. 使用系统服务或 `redis-server redis.conf` 启动。 + +关键配置已包含: + +- `requirepass`(密码) +- `protected-mode no`(允许远程连接) +- `appendonly yes` + `appendfsync everysec` +- `maxmemory-policy allkeys-lru` + +## 3. 常用命令 + +在应用或 CLI 中使用: + +```bash +redis-cli -h 49.232.6.45 -p 6379 -a MsuMshk112233 ping +``` + +`appsettings.*.json` 的格式:`"Redis": "49.232.6.45:6379,password=MsuMshk112233,abortConnect=false"` + +## 4. 备份 + +- RDB 文件:`dump.rdb` +- AOF 文件:`appendonly.aof` + +通过 `redis-cli -a save` 或 `bgsave` 触发。确保备份目录已纳入快照/对象存储。 + diff --git a/deploy/redis/redis.conf b/deploy/redis/redis.conf new file mode 100644 index 0000000..5d20bb0 --- /dev/null +++ b/deploy/redis/redis.conf @@ -0,0 +1,25 @@ +bind 0.0.0.0 +port 6379 +protected-mode no + +requirepass MsuMshk112233 + +timeout 0 +tcp-keepalive 300 + +daemonize no + +loglevel notice +databases 16 + +save 900 1 +save 300 10 +save 60 10000 + +appendonly yes +appendfilename "appendonly.aof" +appendfsync everysec + +dir /data + +maxmemory-policy allkeys-lru diff --git a/scripts/build-adminapi-forlinux.sh b/scripts/build-adminapi-forlinux.sh new file mode 100755 index 0000000..59f018e --- /dev/null +++ b/scripts/build-adminapi-forlinux.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# 用法:在 Linux 终端执行本脚本,自动构建并重启 AdminApi 容器。 +# 前置:已安装并运行 Docker。 +set -euo pipefail +# 0. 遇到异常时输出错误信息,方便查看 +trap 'echo "发生错误:${BASH_COMMAND}" >&2' ERR +# 1. 基本变量(脚本位于 repo_root/scripts,下移一层再上跳到仓库根) +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/.." && pwd)" +image_name="${IMAGE_NAME:-takeout.api.admin:dev}" +container_name="${CONTAINER_NAME:-takeout.api.admin}" +build_configuration="${BUILD_CONFIGURATION:-Release}" +docker_network="${DOCKER_NETWORK:-web_apps}" +dockerfile_path="${repo_root}/src/Api/TakeoutSaaS.AdminApi/Dockerfile" +echo "工作目录:${repo_root}" + +docker_build_args=() +if [ "${NO_CACHE:-0}" = "1" ]; then + docker_build_args+=(--no-cache) +fi + +# 2. 先构建镜像(减少停机时间;同时避免每次都删除镜像导致缓存失效) +echo "开始构建镜像:${image_name} (Configuration=${build_configuration})" +docker build -f "${dockerfile_path}" -t "${image_name}" --build-arg "BUILD_CONFIGURATION=${build_configuration}" "${docker_build_args[@]}" "${repo_root}" + +# 3. 停止并删除旧容器 +if docker ps -a --format '{{.Names}}' | grep -qx "${container_name}"; then + echo "发现旧容器,正在移除:${container_name}" + docker rm -f "${container_name}" >/dev/null +fi + +# 4. 运行新容器并映射端口 +run_args=() +if [ -n "${docker_network}" ]; then + if ! docker network inspect "${docker_network}" >/dev/null 2>&1; then + echo "Docker network 不存在,正在创建:${docker_network}" + docker network create "${docker_network}" >/dev/null + fi + run_args+=(--network "${docker_network}") +fi + +echo "运行新容器:${container_name} (端口映射 7801:7801,环境 Development,网络 ${docker_network})" +docker run -d --name "${container_name}" "${run_args[@]}" -e ASPNETCORE_ENVIRONMENT=Development -p 7801:7801 "${image_name}" +echo "完成。镜像:${image_name},容器:${container_name}。Swagger 访问:http://localhost:7801/swagger" +# 6. 交互式终端下暂停,方便查看输出 +if [ -t 0 ]; then + read -r -p "按回车关闭窗口" _ +fi diff --git a/scripts/build-adminapi.ps1 b/scripts/build-adminapi.ps1 new file mode 100644 index 0000000..f7b3eae --- /dev/null +++ b/scripts/build-adminapi.ps1 @@ -0,0 +1,51 @@ +<# +用法:在 PowerShell 中执行本脚本,自动构建并重启 AdminApi 容器。 +如果要放到桌面双击运行,可将本文件复制到桌面后右键“使用 PowerShell 运行”。 +前置:已安装并运行 Docker Desktop。 +#> + +# 遇到异常时停住窗口,方便查看错误 +trap { + Write-Host "发生错误:" $_ -ForegroundColor Red + Read-Host "按回车关闭窗口" + exit 1 +} + +$ErrorActionPreference = 'Stop' + +# 1. 基本变量(脚本位于 repo_root/scripts,下移一层再上跳到仓库根) +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$repoRoot = Split-Path -Parent $scriptDir +$imageName = if ($env:IMAGE_NAME) { $env:IMAGE_NAME } else { 'takeout.api.admin:dev' } +$containerName = if ($env:CONTAINER_NAME) { $env:CONTAINER_NAME } else { 'takeout.api.admin' } +$buildConfiguration = if ($env:BUILD_CONFIGURATION) { $env:BUILD_CONFIGURATION } else { 'Release' } +$dockerNetwork = if ($env:DOCKER_NETWORK) { $env:DOCKER_NETWORK } else { 'web_apps' } +$dockerfilePath = Join-Path $repoRoot 'src/Api/TakeoutSaaS.AdminApi/Dockerfile' + +Write-Host "工作目录:$repoRoot" + +# 2. 先构建镜像(减少停机时间;同时避免每次都删除镜像导致缓存失效) +Write-Host "开始构建镜像:$imageName (Configuration=$buildConfiguration)" +docker build -f $dockerfilePath -t $imageName --build-arg "BUILD_CONFIGURATION=$buildConfiguration" $repoRoot + +# 3. 停止并删除旧容器 +if ((docker ps -a --format '{{.Names}}') -contains $containerName) { + Write-Host "发现旧容器,正在移除:$containerName" + docker rm -f $containerName | Out-Null +} + +# 4. 运行新容器并映射端口 +$runArgs = @() +if (-not [string]::IsNullOrWhiteSpace($dockerNetwork)) { + $networkExists = (docker network ls --format '{{.Name}}') -contains $dockerNetwork + if (-not $networkExists) { + Write-Host "Docker network 不存在,正在创建:$dockerNetwork" + docker network create $dockerNetwork | Out-Null + } + $runArgs += @('--network', $dockerNetwork) +} + +Write-Host "运行新容器:$containerName (端口映射 7801:7801,环境 Development,网络 $dockerNetwork)" +docker run -d --name $containerName @runArgs -e ASPNETCORE_ENVIRONMENT=Development -p 7801:7801 $imageName + +Write-Host "完成。镜像:$imageName,容器:$containerName。Swagger 访问:http://localhost:7801/swagger" diff --git a/scripts/watch-adminapi-forlinux.sh b/scripts/watch-adminapi-forlinux.sh new file mode 100755 index 0000000..23ba2d9 --- /dev/null +++ b/scripts/watch-adminapi-forlinux.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# 用法:在 Linux 终端执行本脚本,后台启动 AdminApi 的 dotnet watch(开发用,不需要 docker build)。 +# 前置:已安装并运行 Docker。 +set -euo pipefail +trap 'echo "发生错误:${BASH_COMMAND}" >&2' ERR + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/.." && pwd)" + +container_name="${CONTAINER_NAME:-takeout.api.admin}" +docker_network="${DOCKER_NETWORK:-web_apps}" +host_port="${HOST_PORT:-7801}" +container_port="${CONTAINER_PORT:-7801}" +sdk_image="${SDK_IMAGE:-mcr.microsoft.com/dotnet/sdk:10.0}" +nuget_volume="${NUGET_VOLUME:-takeout-nuget}" +project_path="${PROJECT_PATH:-src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj}" +environment="${ASPNETCORE_ENVIRONMENT:-Development}" + +echo "工作目录:${repo_root}" +echo "使用 SDK 镜像:${sdk_image}" +echo "容器:${container_name} 端口:${host_port}:${container_port} 网络:${docker_network}" + +# Ensure network exists (so it can talk to other services like postgres/redis on the same network). +if [ -n "${docker_network}" ]; then + if ! docker network inspect "${docker_network}" >/dev/null 2>&1; then + echo "Docker network 不存在,正在创建:${docker_network}" + docker network create "${docker_network}" >/dev/null + fi +fi + +# Persist NuGet cache across container restarts to speed up restore/build. +if ! docker volume inspect "${nuget_volume}" >/dev/null 2>&1; then + echo "NuGet volume 不存在,正在创建:${nuget_volume}" + docker volume create "${nuget_volume}" >/dev/null +fi + +# Replace existing container (if any). +if docker ps -a --format '{{.Names}}' | grep -qx "${container_name}"; then + echo "发现旧容器,正在移除:${container_name}" + docker rm -f "${container_name}" >/dev/null +fi + +run_args=() +if [ -n "${docker_network}" ]; then + run_args+=(--network "${docker_network}") +fi + +echo "启动 dotnet watch(后台运行)..." +docker run -d --name "${container_name}" \ + "${run_args[@]}" \ + -p "${host_port}:${container_port}" \ + -v "${repo_root}":/src \ + -w /src \ + -v "${nuget_volume}":/root/.nuget/packages \ + -e ASPNETCORE_ENVIRONMENT="${environment}" \ + -e ASPNETCORE_URLS="http://+:${container_port}" \ + -e DOTNET_USE_POLLING_FILE_WATCHER=1 \ + "${sdk_image}" \ + dotnet watch --project "${project_path}" run + +echo "已启动。查看日志:docker logs -f ${container_name}" +echo "Swagger:http://localhost:${host_port}/swagger" + diff --git a/scripts/watch-adminapi.ps1 b/scripts/watch-adminapi.ps1 new file mode 100644 index 0000000..b338925 --- /dev/null +++ b/scripts/watch-adminapi.ps1 @@ -0,0 +1,68 @@ +<# +用法:在 PowerShell 中执行本脚本,后台启动 AdminApi 的 dotnet watch(开发用,不需要 docker build)。 +前置:已安装并运行 Docker Desktop。 +#> + +trap { + Write-Host "发生错误:" $_ -ForegroundColor Red + Read-Host "按回车关闭窗口" + exit 1 +} + +$ErrorActionPreference = 'Stop' + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$repoRoot = Split-Path -Parent $scriptDir + +$containerName = if ($env:CONTAINER_NAME) { $env:CONTAINER_NAME } else { 'takeout.api.admin' } +$dockerNetwork = if ($env:DOCKER_NETWORK) { $env:DOCKER_NETWORK } else { 'web_apps' } +$hostPort = if ($env:HOST_PORT) { $env:HOST_PORT } else { '7801' } +$containerPort = if ($env:CONTAINER_PORT) { $env:CONTAINER_PORT } else { '7801' } +$sdkImage = if ($env:SDK_IMAGE) { $env:SDK_IMAGE } else { 'mcr.microsoft.com/dotnet/sdk:10.0' } +$nugetVolume = if ($env:NUGET_VOLUME) { $env:NUGET_VOLUME } else { 'takeout-nuget' } +$projectPath = if ($env:PROJECT_PATH) { $env:PROJECT_PATH } else { 'src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj' } +$environment = if ($env:ASPNETCORE_ENVIRONMENT) { $env:ASPNETCORE_ENVIRONMENT } else { 'Development' } + +Write-Host "工作目录:$repoRoot" +Write-Host "使用 SDK 镜像:$sdkImage" +Write-Host "容器:$containerName 端口:$hostPort`:$containerPort 网络:$dockerNetwork" + +if (-not [string]::IsNullOrWhiteSpace($dockerNetwork)) { + $networkExists = (docker network ls --format '{{.Name}}') -contains $dockerNetwork + if (-not $networkExists) { + Write-Host "Docker network 不存在,正在创建:$dockerNetwork" + docker network create $dockerNetwork | Out-Null + } +} + +$volumeExists = (docker volume ls --format '{{.Name}}') -contains $nugetVolume +if (-not $volumeExists) { + Write-Host "NuGet volume 不存在,正在创建:$nugetVolume" + docker volume create $nugetVolume | Out-Null +} + +if ((docker ps -a --format '{{.Names}}') -contains $containerName) { + Write-Host "发现旧容器,正在移除:$containerName" + docker rm -f $containerName | Out-Null +} + +$runArgs = @() +if (-not [string]::IsNullOrWhiteSpace($dockerNetwork)) { + $runArgs += @('--network', $dockerNetwork) +} + +Write-Host "启动 dotnet watch(后台运行)..." +docker run -d --name $containerName @runArgs ` + -p "$hostPort`:$containerPort" ` + -v "$repoRoot`:/src" ` + -w /src ` + -v "$nugetVolume`:/root/.nuget/packages" ` + -e "ASPNETCORE_ENVIRONMENT=$environment" ` + -e "ASPNETCORE_URLS=http://+:$containerPort" ` + -e "DOTNET_USE_POLLING_FILE_WATCHER=1" ` + $sdkImage ` + dotnet watch --project $projectPath run + +Write-Host "已启动。查看日志:docker logs -f $containerName" +Write-Host "Swagger:http://localhost:$hostPort/swagger" + diff --git a/商品模块_API设计_v1.md b/商品模块_API设计_v1.md new file mode 100644 index 0000000..d6a1150 --- /dev/null +++ b/商品模块_API设计_v1.md @@ -0,0 +1,426 @@ +# 商品模块 API 设计(v1) +> 企业级/混合模式版 + +## 0. 目标与范围 +- 面向商业化外卖 SaaS,覆盖“总部主库 + 门店私库 + 多维度经营 + 可配置开关 + 可扩展集成”。 +- 本文仅讨论 API 设计与契约,不涉及实现细节。 +- 默认对齐项目现有约束:多租户、CQRS、统一响应、Snowflake ID、JWT/RBAC。 + +## 1. 通用规范 +### 1.1 路由与版本 +- AdminApi:`/api/admin/v1/...` +- MiniApi:`/api/mini/v1/...` +- UserApi:`/api/user/v1/...` + +### 1.2 鉴权与租户 +- Header:`Authorization: Bearer {token}` +- 租户:`X-Tenant-Id` 或 `X-Tenant-Code`(必填,除白名单路径) +- 角色权限:AdminApi 必须绑定 `PermissionAuthorize` 权限码 + +### 1.3 响应格式 +统一使用 `ApiResponse`,与现有 `Shared.Web` 约定一致。 + +### 1.4 ID 与并发 +- 所有 `long` 类型 ID 在 API 中 **序列化为 string**。 +- 更新类接口需携带 `rowVersion`(Base64)或 `If-Match` 以并发控制。 + +### 1.5 幂等与限流 +- 创建、批量变更、导入等写接口支持 `Idempotency-Key`。 +- 面向公网端启用限流策略,读接口优先缓存。 + +### 1.6 分页与排序 +统一参数:`page`、`pageSize`、`sortBy`、`sortOrder`(`asc|desc`)。 + +## 2. 产品原则(10 年外卖 SaaS 视角) +- 品牌一致性优先:总部主库保证品牌统一,门店仅允许“可控范围内的微调”。 +- 经营灵活性必备:门店私有商品与局部覆盖是应对“城市、商圈、人力”的关键。 +- C 端效率优先:类目不超过 2 级,菜单渲染优先走聚合与缓存。 +- 扩展优先:渠道/场景/时段/三方同步都必须可开关,避免“为少数租户拖累成本”。 + +## 3. 功能域拆分(可开关) +### 3.1 核心域 +- 公共商品库(Master Library,总部商品) +- 门店私有库(Store Library,本地特色) +- 引用/下发机制(Push & Pull) +- 类目管理(2 级以内 + 时段可见) +- 商品(Product/SPU)与规格 SKU +- 场景/渠道/时段维度可见性 +- 计价与打包费策略 +- 库存视图与沽清(含每日重置) + +### 3.2 可选域 +- 加料/口味(Addon/Modifier) +- 套餐/组合(Bundle/N 选 M) +- 称重计价与时价 +- 后厨生产(KDS/打印标签/台位) +- 三方平台同步(美团/饿了么/抖音) +- 审核流与定时上架 +- 多语言(I18n) +- 评分/销量统计视图(Stats) + +## 4. 核心架构:公共商品库 + 门店私有库 + Push/Pull +### 4.1 公共商品库(Tenant/Master Library) +- 定义:总部创建的标准化商品。 +- 作用:维护品牌统一形象(名称、图、描述、营养、后厨分类)。 +- 管控:总部可锁定核心字段,门店仅可引用,不可篡改。 + +### 4.2 门店私有库(Store Private Library) +- 定义:门店为本地市场创建的特色商品。 +- 作用:一店一策(开业活动、地域限定等)。 +- 权限:仅本门店可见,总部可审计但默认不干预。 + +### 4.3 引用与下发(Push & Pull) +- 总部推送(Push):支持“静默上架”或“待门店确认”。 +- 门店拉取(Pull):门店经理从公共库勾选引入到本店经营列表。 +- 门店引用后允许“局部覆盖”,但不破坏主库锁定字段。 + +### 4.4 混合视图标识 +- API 输出 `libraryType` + `masterProductId`,便于后台列表用标签区分来源。 +- `lockedFields` 返回总部锁定字段,避免门店误操作。 + +## 5. 维度管理:类目、场景、渠道与时段 +### 5.1 类目(2 级以内) +- 类目支持“生效时段”,如早餐类目 10:00 后隐藏。 +- 类目可绑定“场景”,如堂食专属类目。 + +### 5.2 场景(履约场景) +- 堂食(DineIn)、外卖(Delivery)、自提(Pickup)。 +- 外卖场景强制打包费规则;堂食可免打包费。 + +### 5.3 渠道(流量入口) +- 微信小程序、POS 点餐、美团、饿了么、抖音等。 +- 支持“渠道隔离”:显示顺序、价格、上下架状态可独立配置。 + +### 5.4 维度优先级(建议) +门店覆盖 > 渠道配置 > 时段配置 > 商品基础配置。 + +## 6. 核心业务规则 +### 6.1 覆盖机制(Override Rule) +- 门店可对价格、场景、上架/沽清做覆盖。 +- 被锁定字段不允许覆盖;如需调整须总部解锁或走审核。 + +### 6.2 计价与打包费 +- 计价模式:固定单价、按克计价(称重菜)、时价(随行就市)。 +- 打包费支持按 SKU 设置,且可按场景配置(堂食可为 0)。 +- 打包费支持单单封顶(不超过 X 元)。 + +### 6.3 库存与自动重置 +- 支持门店级“每日自动恢复初始库存”(默认凌晨执行)。 +- 沽清为临时状态,不影响主库与其他门店。 + +### 6.4 规格、加料与套餐 +- SKU 影响价格与库存。 +- 加料支持“收费加料 + 免费属性”,可配置选配上限/下限。 +- 动态套餐支持“N 选 M”,并要求库存穿透: + - 套餐内关键单品沽清时,套餐自动联动下架。 + +### 6.5 生产与后厨(KDS/打印) +- 商品可绑定“打印标签 + 后厨台位”。 +- 订单下发需标示场景,以区分堂食/外卖/自提出餐逻辑。 + +### 6.6 三方平台同步 +- 内置 Mapping 机制,支持“商品—平台商品”映射。 +- 价格/沽清变动触发事件总线,异步同步到平台接口。 + +## 7. 权限与审计 +- 总部运营:管理主库、类目、全局规则、价格上限、审核流。 +- 门店经理:门店私有商品、门店覆盖、今日沽清。 +- 字段级审计日志:记录“谁在何时修改了哪个门店商品的价格/状态”。 +- 推送审计:记录主库变更的下发范围与结果。 + +## 8. 功能开关(租户级) +用于“商业化套餐可选启用”。建议 AdminApi 提供读取能力,写入由套餐/配置管理模块控制。 + +示例结构: +```json +{ + "enableMasterLibrary": true, + "enableStoreLibrary": true, + "enablePushPull": true, + "enableVariant": true, + "enableAddon": true, + "enableBundle": true, + "enableSceneFilter": true, + "enableChannelIsolation": true, + "enableChannelPrice": true, + "enableTimePrice": true, + "enableStoreOverride": true, + "enablePricingWeight": true, + "enablePricingMarket": true, + "enablePackagingFee": true, + "enablePackagingFeeCap": true, + "enableDailyStockReset": true, + "enableInventory": true, + "enableApproval": false, + "enableScheduledPublish": true, + "enableKds": true, + "enableThirdPartySync": true, + "enableMultiLanguage": false, + "enableNutritionInfo": false +} +``` + +## 9. 关键 DTO(摘要) +> 字段命名遵循现有规范,布尔值使用 `Is/Has` 前缀。 + +### 9.1 CategoryDto +| 字段 | 说明 | +| --- | --- | +| id | 类目 ID(string) | +| parentId | 父级类目 ID | +| name | 类目名称 | +| sortOrder | 排序 | +| isEnabled | 是否启用 | +| availableScenes | 生效场景 | +| availableTimeRanges | 生效时段 | +| createdAt | 创建时间 | + +### 9.2 ProductDto(SPU) +| 字段 | 说明 | +| --- | --- | +| id | 商品 ID(string) | +| libraryType | Master/Store | +| masterProductId | 引用的主库商品 ID | +| storeId | 归属门店 | +| name | 商品名称 | +| categoryId | 类目 ID | +| unit | 单位 | +| tags | 标签 | +| coverImageUrl | 封面图 | +| imageUrls | 轮播图 | +| isEnabled | 是否启用 | +| isPublished | 是否上架 | +| hasSku | 是否包含 SKU | +| hasAddon | 是否包含加料 | +| pricingMode | Fixed/Weight/Market | +| basePrice | 基础价格 | +| packagingFee | 打包费(基础) | +| packagingFeeCap | 打包费封顶 | +| availableScenes | 生效场景 | +| availableChannels | 生效渠道 | +| lockedFields | 被总部锁定字段 | +| rowVersion | 并发字段 | + +### 9.3 SkuDto +| 字段 | 说明 | +| --- | --- | +| id | SKU ID(string) | +| productId | 商品 ID | +| specValues | 规格值列表 | +| price | 价格 | +| stock | 库存 | +| isEnabled | 是否启用 | +| rowVersion | 并发字段 | + +### 9.4 StoreProductOverrideDto +| 字段 | 说明 | +| --- | --- | +| storeId | 门店 ID | +| productId | 商品 ID | +| overridePrice | 覆盖价格 | +| overrideScenes | 覆盖场景 | +| isSoldOut | 是否沽清 | +| isApproved | 是否已审核 | +| overrideReason | 覆盖原因 | + +### 9.5 AddonGroupDto(可选) +| 字段 | 说明 | +| --- | --- | +| id | 组 ID | +| name | 组名 | +| minSelected | 最少选择 | +| maxSelected | 最多选择 | +| isRequired | 是否必选 | +| items | 加料项列表 | + +### 9.6 ChannelSettingDto(可选) +| 字段 | 说明 | +| --- | --- | +| channelCode | 渠道编码 | +| price | 渠道价格 | +| sortOrder | 渠道排序 | +| isEnabled | 是否启用 | + +### 9.7 ProductionProfileDto(可选) +| 字段 | 说明 | +| --- | --- | +| kitchenStationId | 后厨台位 | +| printTagId | 打印标签 | + +## 10. AdminApi(管理端)接口清单 +### 10.1 公共商品库(总部) +- `GET /api/admin/v1/master-products` +- `GET /api/admin/v1/master-products/{id}` +- `POST /api/admin/v1/master-products` +- `PUT /api/admin/v1/master-products/{id}` +- `PUT /api/admin/v1/master-products/{id}/lock-fields` +- `PUT /api/admin/v1/master-products/{id}/publish` +- `PUT /api/admin/v1/master-products/{id}/unpublish` + +### 10.2 门店私有库与经营商品 +- `GET /api/admin/v1/stores/{storeId}/products` +- `POST /api/admin/v1/stores/{storeId}/products`(创建门店私有商品) +- `POST /api/admin/v1/stores/{storeId}/products/pull`(从主库拉取) +- `PUT /api/admin/v1/stores/{storeId}/products/{id}` +- `PUT /api/admin/v1/stores/{storeId}/products/{id}/override` +- `PUT /api/admin/v1/stores/{storeId}/products/{id}/publish` +- `PUT /api/admin/v1/stores/{storeId}/products/{id}/unpublish` +- `PUT /api/admin/v1/stores/{storeId}/products/{id}/sold-out` + +### 10.3 总部推送(Push) +- `POST /api/admin/v1/master-products/{id}/push`(指定门店) +- `GET /api/admin/v1/master-products/{id}/push-tasks` +- `POST /api/admin/v1/push-tasks/{taskId}/retry` + +### 10.4 类目 +- `GET /api/admin/v1/categories` +- `POST /api/admin/v1/categories` +- `PUT /api/admin/v1/categories/{id}` +- `DELETE /api/admin/v1/categories/{id}` +- `PUT /api/admin/v1/categories/{id}/enable` +- `PUT /api/admin/v1/categories/{id}/disable` +- `PUT /api/admin/v1/categories/sort`(批量排序) +- `PUT /api/admin/v1/categories/{id}/schedule`(类目时段) +- `PUT /api/admin/v1/categories/{id}/scenes`(类目场景) + +### 10.5 规格与 SKU +- `GET /api/admin/v1/products/{id}/spec-groups` +- `PUT /api/admin/v1/products/{id}/spec-groups` +- `GET /api/admin/v1/products/{id}/skus` +- `POST /api/admin/v1/products/{id}/skus` +- `PUT /api/admin/v1/skus/{id}` +- `PUT /api/admin/v1/skus/{id}/enable` +- `PUT /api/admin/v1/skus/{id}/disable` +- `PUT /api/admin/v1/skus/{id}/price` +- `PUT /api/admin/v1/skus/{id}/stock` +- `PUT /api/admin/v1/skus/{id}/pricing-mode` +- `PUT /api/admin/v1/skus/{id}/packaging-fee` +- `PUT /api/admin/v1/skus/{id}/inventory-policy` + +### 10.6 场景/渠道/时段 +- `PUT /api/admin/v1/products/{id}/scenes` +- `PUT /api/admin/v1/products/{id}/channels` +- `PUT /api/admin/v1/products/{id}/time-slots` +- `PUT /api/admin/v1/products/{id}/channel-mappings` +- `POST /api/admin/v1/products/{id}/channel-sync` + +### 10.7 加料/口味(可选) +- `GET /api/admin/v1/products/{id}/addon-groups` +- `PUT /api/admin/v1/products/{id}/addon-groups` +- `PUT /api/admin/v1/addon-groups/{id}/items` +- `PUT /api/admin/v1/addon-groups/{id}/enable` +- `PUT /api/admin/v1/addon-groups/{id}/disable` + +### 10.8 套餐/组合(可选) +- `GET /api/admin/v1/bundles` +- `POST /api/admin/v1/bundles` +- `PUT /api/admin/v1/bundles/{id}` +- `PUT /api/admin/v1/bundles/{id}/publish` +- `PUT /api/admin/v1/bundles/{id}/unpublish` +- `PUT /api/admin/v1/bundles/{id}/items` +- `PUT /api/admin/v1/bundles/{id}/rules`(N 选 M) + +### 10.9 后厨生产(可选) +- `PUT /api/admin/v1/products/{id}/production-profile` +- `PUT /api/admin/v1/skus/{id}/production-profile` + +### 10.10 导入导出与索引 +- `POST /api/admin/v1/products/import` +- `GET /api/admin/v1/products/import/{taskId}` +- `GET /api/admin/v1/products/export` +- `POST /api/admin/v1/products/reindex` + +### 10.11 审计与日志 +- `GET /api/admin/v1/products/{id}/audit-logs` +- `GET /api/admin/v1/stores/{storeId}/products/{id}/override-logs` +- `GET /api/admin/v1/master-products/{id}/push-logs` + +### 10.12 功能开关读取 +- `GET /api/admin/v1/products/features` + +## 11. MiniApi(小程序端)接口清单 +- `GET /api/mini/v1/categories?scene=Delivery&channel=WeChatMiniProgram` +- `GET /api/mini/v1/menus/{storeId}?scene=Delivery&channel=WeChatMiniProgram` +- `GET /api/mini/v1/products?storeId=...&scene=Delivery&channel=WeChatMiniProgram` +- `GET /api/mini/v1/products/{id}?scene=Delivery&channel=WeChatMiniProgram` +- `GET /api/mini/v1/products/hot?storeId=...` +- `GET /api/mini/v1/products/recommended?storeId=...` +- `POST /api/mini/v1/products/price-estimate` +- `POST /api/mini/v1/products/checkout-validate` +- `POST /api/mini/v1/products/snapshots`(订单服务调用) + +## 12. UserApi(C 端用户)接口清单 +- `GET /api/user/v1/categories?scene=Delivery&channel=H5` +- `GET /api/user/v1/menus/{storeId}?scene=Delivery&channel=H5` +- `GET /api/user/v1/products?storeId=...&scene=Delivery&channel=H5` +- `GET /api/user/v1/products/{id}?scene=Delivery&channel=H5` +- `GET /api/user/v1/products/hot?storeId=...` +- `GET /api/user/v1/products/recommended?storeId=...` +- `POST /api/user/v1/products/price-estimate` +- `POST /api/user/v1/products/checkout-validate` + +## 13. 事件与扩展点 +采用 Outbox 模式输出领域事件,便于搜索索引、缓存失效、推荐计算与三方同步。 +- `MasterProductCreated` +- `MasterProductUpdated` +- `MasterProductPushed` +- `StoreProductPulled` +- `StoreProductOverridden` +- `ProductPriceChanged` +- `ProductAvailabilityChanged` +- `ProductSoldOutChanged` +- `SkuStockChanged` +- `ProductChannelSyncRequested` + +## 14. 示例(关键请求) +### 14.1 商品创建(总部主库) +```json +{ + "name": "黄金鸡排饭", + "categoryId": "1782328933492367360", + "unit": "份", + "coverImageUrl": "https://cdn/xxx.jpg", + "imageUrls": ["https://cdn/xxx1.jpg", "https://cdn/xxx2.jpg"], + "pricingMode": "Fixed", + "basePrice": 19.9, + "isEnabled": true +} +``` + +### 14.2 门店覆盖(价格 + 场景) +```json +{ + "overridePrice": 21.9, + "overrideScenes": ["Delivery", "Pickup"], + "isSoldOut": false, + "overrideReason": "外卖平台佣金调整" +} +``` + +### 14.3 结算校验(Mini/User) +```json +{ + "storeId": "1782328933492367000", + "scene": "Delivery", + "channel": "WeChatMiniProgram", + "items": [ + { + "productId": "1782328933492367360", + "skuId": "1782328933492367400", + "quantity": 2, + "addonItemIds": ["1782328933492367501", "1782328933492367502"] + } + ] +} +``` + +## 15. 依赖说明 +- 文件上传:复用 Storage 模块(FilesController)获取 URL。 +- 库存:优先对接 Inventory 模块,商品侧仅提供视图与校验。 +- 订单:下单时生成商品快照,避免历史价格漂移。 +- 后厨:KDS/打印由生产模块承接,商品仅配置绑定信息。 +- 三方同步:由集成服务监听事件并进行异步同步与重试。 +- 权限码:`product.read`、`product.write`、`product.publish`、`product.import` 等(待统一权限表配置)。 + +--- +**待确认**:渠道编码标准、称重计价精度与四舍五入规则、库存每日重置默认时间、Push 是否强制门店确认。