# starry-shift **Repository Path**: wangyidao/starry-shift ## Basic Information - **Project Name**: starry-shift - **Description**: **斗转星移(starry-shift)** 是一套开源的**支付路由中间件**。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-15 - **Last Updated**: 2026-09-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 斗转星移 · PayRoute 支付路由中间件 > **斗转星移(starry-shift)** 是一套开源的**支付路由中间件**(英文名 PayRoute)。 > 它向上统一收敛支付入口,向下对接支付宝、微信、Stripe 等多渠道,让业务系统用**一套 API** 完成收款、退款、分账、对账等全链路能力。 > **自身不做清算、不持牌**,只做支付通道的抽象、路由与编排。 - 中文产品名:**斗转星移** - 工程名:`starry-shift` - 版本:`1.0.0` - 技术基座:Spring Boot 4.1.1 + Java 21(单体工程,按包划分模块) --- ## 目录 - [一、核心特性](#一核心特性) - [二、整体架构](#二整体架构) - [三、技术栈](#三技术栈) - [四、工程目录结构](#四工程目录结构) - [五、环境要求](#五环境要求) - [六、快速开始](#六快速开始) - [七、配置说明](#七配置说明) - [八、数据库与 Flyway 迁移](#八数据库与-flyway-迁移) - [九、API 接口清单](#九api-接口清单) - [十、核心业务流程](#十核心业务流程) - [十一、路由引擎与规则 DSL](#十一路由引擎与规则-dsl) - [十二、渠道适配器](#十二渠道适配器) - [十三、安全设计](#十三安全设计) - [十四、定时任务](#十四定时任务) - [十五、部署与运维](#十五部署与运维) - [十六、常见问题与排错](#十六常见问题与排错) - [十七、相关文档](#十七相关文档) - [十八、License](#十八license) --- ## 一、核心特性 | 能力 | 说明 | |---|---| | **统一收口** | 商户侧一套 REST API + 一套回调地址,屏蔽下游通道差异 | | **智能路由** | 基于权重、成本、可用性、熔断的多渠道路由与故障自动切换(渠道路由 + 账号路由两级) | | **配置驱动** | 组织 / 商户 / 渠道 / 账号 / 路由规则后台可视化配置,热生效 | | **多通道适配** | 支付宝、微信、Stripe 真实 API 对接,凭据与参数全部入库经 AES-256-GCM 加密 | | **支付核心** | 统一下单 / 关单 / 查单、订单状态机、幂等防重、分布式锁 | | **退款** | 支持原路退回与分账回退(royalty_return) | | **异步通知** | 渠道回调验签 → 订单状态更新,失败指数退避重试 | | **轮询补偿** | 订单状态兜底轮询 + 退避重入队,与异步通知构成双保险 | | **分账引擎** | 分账规则、分账明细、冻结/解冻、分账回退、对账校验 | | **安全合规** | 请求签名验签、接口限流、敏感数据加密、逻辑删除/审计留痕 | | **可观测** | SpringDoc OpenAPI 文档、结构化日志 | --- ## 二、整体架构 ``` ┌──────────────────────────────────────────────────────────────┐ │ 商户业务系统 │ └───────────────────────┬──────────────────────────────────────┘ │ 统一 OpenAPI(REST + 签名 + 限流) ┌───────────────────────▼──────────────────────────────────────┐ │ 斗转星移 网关层 │ │ 支付 API │ 退款 API │ 分账 API │ 回调/通知 API │ │ ┌────────────────────────────────────────────────────┐ │ │ │ 支付核心 (Core Engine) │ │ │ │ 路由策略 · 渠道适配 · 订单状态机 · 幂等 · 补偿 │ │ │ └────┬───────────────┬───────────────┬───────────────┘ │ │ ┌────▼─────┐ ┌──────▼──────┐ ┌─────▼────────┐ │ │ │ 分账引擎 │ │ 通知中心 │ │ 轮询/补偿调度 │ │ │ └──────────┘ └─────────────┘ └──────────────┘ │ └───────────────────────┬──────────────────────────────────────┘ │ 通道 SPI(支付宝 / 微信 / Stripe ...) ┌───────────┼───────────────┐ ┌───▼───┐ ┌────▼────┐ ┌────▼────┐ │ 支付宝 │ │ 微信 │ │ Stripe │ └───────┘ └─────────┘ └─────────┘ ``` ### 分层与包划分 | 包 | 职责 | |---|---| | `com.payroute.gateway` | 商户统一网关入口、签名拦截器、限流拦截器、Body 缓存过滤器 | | `com.payroute.core` | 支付核心:订单实体 / Mapper / Service / MQ / 状态机 | | `com.payroute.route` | 路由引擎:规则 DSL 解释、渠道路由、账号路由、熔断 | | `com.payroute.channel` | 渠道 SPI 抽象(`spi`)+ 支付宝 / 微信 / Stripe 适配器 | | `com.payroute.refund` | 退款核心(原路 / 分账回退) | | `com.payroute.notify` | 异步通知回调入口与重试 | | `com.payroute.poll` | 订单轮询补偿 Worker | | `com.payroute.split` | 分账引擎(规则 / 明细 / 解冻 / 回退 / 对账) | | `com.payroute.admin` | 管理后台:组织 / 商户 / 渠道 / 账号 / 路由规则 / 分账规则 | | `com.payroute.job` | 定时任务(日限额清零、重试扫描等) | | `com.payroute.common` | 公共能力:加密(`crypto`)、签名(`sign`)、工具(`util`)、枚举、异常 | | `com.payroute.config` | 配置类:MyBatis-Plus、Redis、OpenAPI、安全等 | | `com.payroute.model` | 实体、DTO、Mapper 接口 | --- ## 三、技术栈 | 类别 | 选型 | |---|---| | 语言 / 框架 | Java 21 · Spring Boot 4.1.1 | | Web | spring-boot-starter-web · spring-boot-starter-validation | | ORM | MyBatis-Plus 3.5.7(`mybatis-plus-spring-boot3-starter`) | | 数据库 | MySQL 8(驱动 `mysql-connector-j`) | | 迁移 | Flyway(`flyway-core` + `flyway-mysql`) | | 缓存 / 幂等 / 分布式锁 | Redis(spring-boot-starter-data-redis) | | 消息 | Apache RocketMQ(`rocketmq-spring-boot-starter` 2.3.0) | | 文档 | SpringDoc OpenAPI 2.6.0(Swagger UI) | | 工具 | Lombok · Jackson · commons-lang3 | | 构建 | Maven(spring-boot-maven-plugin 打包可执行 jar) | --- ## 四、工程目录结构 ``` starry-shift/ ├── pom.xml ├── PayRoute架构设计文档.md # 完整架构设计(详细版) ├── IMPLEMENTATION_CONTRACTS.md # 实现契约(对齐说明) ├── src/main/ │ ├── java/com/payroute/ │ │ ├── PayRouteApplication.java │ │ ├── admin/ # 管理后台配置模块 │ │ ├── channel/ # 渠道 SPI + 适配器(alipay/wechat/stripe) │ │ ├── common/ # crypto/sign/util/enums/exception │ │ ├── config/ # MyBatis-Plus/Redis/OpenAPI 等配置 │ │ ├── core/ # 支付核心(entity/mapper/service/mq/statemachine) │ │ ├── gateway/ # 统一网关(controller/annotation/interceptor/filter) │ │ ├── job/ # 定时任务 │ │ ├── mapper/ # 通用 Mapper 接口 │ │ ├── model/ # 实体/DTO │ │ ├── notify/ # 异步通知 │ │ ├── poll/ # 订单轮询补偿 │ │ ├── refund/ # 退款 │ │ ├── route/ # 路由引擎 │ │ └── split/ # 分账引擎 │ └── resources/ │ ├── application.yml │ ├── logback-spring.xml │ └── db/migration/ │ └── V1__init_schema.sql # 建表 + 演示种子数据 └── logs/ # 运行时日志(自动生成) ``` --- ## 五、环境要求 | 组件 | 版本 / 要求 | 说明 | |---|---|---| | JDK | 21+ | 编译与运行 | | Maven | 3.8+ | 构建 | | MySQL | 8.0+ | 业务库(utf8mb4) | | Redis | 5.0+ | 幂等 / 缓存 / 分布式锁 / 限流计数 | | RocketMQ | 4.9.x / 5.x | 异步通知 / 轮询 / 分账解耦(name-server 9876) | > 说明:演示与联调可单机运行;Redis / RocketMQ 在功能路径上会被使用(如限流、异步通知重试),请保证可用。 --- ## 六、快速开始 ### 0. 一键起全链路联调(docker-compose,推荐) 基础设施(MySQL / Redis / RocketMQ)已通过 `docker-compose.yml` 一键拉起,应用运行在宿主机,dev profile 已**固化演示主密钥**,无需任何环境变量即可启动: ```bash # 1) 启动依赖中间件(MySQL / Redis / RocketMQ) docker compose up -d # 等待 healthy:docker compose ps # 2) 编译打包 mvn clean package -DskipTests # 3) 直接运行(active=dev,自动使用固化演示密钥) java -jar target/starry-shift.jar # 4) 验证文档与启动日志 # Swagger UI: http://localhost:8080/swagger-ui.html # 日志出现 Started PayRouteApplication 即成功 ``` > 如应用也需跑在 Docker 内,可自行在 `docker-compose.yml` 增加 `app` 服务(构建 `target/starry-shift.jar` 镜像),并通过 `extra_hosts` 复用 `host.docker.internal` 访问 broker。 > > 停止与清理:`docker compose down`(保留数据) / `docker compose down -v`(清空 MySQL/Redis 数据)。 ### 1. 准备 MySQL 库与账号 ```sql CREATE DATABASE payroute CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'payroute'@'%' IDENTIFIED BY 'payroute123'; GRANT ALL PRIVILEGES ON payroute.* TO 'payroute'@'%'; FLUSH PRIVILEGES; ``` > 默认连接参数见 `application.yml`(`jdbc:mysql://127.0.0.1:3306/payroute`,账号 `payroute` / `payroute123`)。 > 建表与演示种子数据由 **Flyway 在应用启动时自动执行**(脚本 `V1__init_schema.sql`),无需手动导入。 ### 2. 准备 Redis 确保 `127.0.0.1:6379` 可用(默认无密码,见 `application.yml` 的 `spring.data.redis`)。 ### 3. 配置 AES-256-GCM 主密钥(必填) 凭据加密依赖一个 **32 字节** 的主密钥,通过环境变量注入(不写在配置文件中): ```bash # 32 字节的 base64(推荐使用随机值,例如用下面的命令生成) export PAYROUTE_MASTER_KEY="$(head -c 32 /dev/urandom | base64 -w0)" # Windows (PowerShell): # $b=New-Object byte[] 32; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b); $env:PAYROUTE_MASTER_KEY=[Convert]::ToBase64String($b) ``` > 密钥长度必须为 32 字节;`cryptoConfig` 在初始化时会校验长度,非 32 字节将启动失败。 ### 4. 编译打包 ```bash mvn clean package -DskipTests # 产物:target/starry-shift.jar ``` ### 5. 运行 ```bash export PAYROUTE_MASTER_KEY="<你的 32 字节 base64 密钥>" java -jar target/starry-shift.jar # 默认激活 dev profile,监听 8080 端口 ``` ### 6. 验证 - 启动日志出现 `Started PayRouteApplication in x.xxx seconds` 即表示上下文装配成功。 - 接口文档(Swagger UI): - OpenAPI JSON: --- ## 七、配置说明 主配置文件 `src/main/resources/application.yml`(关键项): ```yaml server: port: 8080 spring: application: name: payroute profiles: active: dev datasource: # MySQL 业务库 url: jdbc:mysql://127.0.0.1:3306/payroute?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: payroute password: payroute123 data: redis: # Redis(幂等/缓存/锁/限流) host: 127.0.0.1 port: 6379 password: "" database: 0 flyway: # 自动建表 enabled: true locations: classpath:db/migration baseline-on-migrate: true mybatis-plus: # 逻辑删除字段 deleted;主键自增 mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 rocketmq: # 异步通知 / 轮询 / 分账解耦 name-server: 127.0.0.1:9876 producer: group: payroute_producer_group payroute: crypto: master-key-env: PAYROUTE_MASTER_KEY # 主密钥环境变量名 notify: retry-intervals-seconds: [10,30,60,300,900,1800,3600,7200,14400,28800] # 指数退避 max-retry: 10 poll: initial-delay-seconds: 30 backoff-multiplier: 1.5 max-retry: 30 shard-count: 16 signature: default-sign-type: RSA2 route: circuit-breaker-cooldown-seconds: 300 # 路由失败熔断冷却 springdoc: swagger-ui: path: /swagger-ui.html ``` ### 环境变量 | 变量 | 必填 | 说明 | |---|---|---| | `PAYROUTE_MASTER_KEY` | 是 | AES-256-GCM 主密钥,**32 字节**(base64 或 hex),用于凭据加密 | | `JAVA_HOME` | 建议 | 指向 JDK 21 | --- ## 八、数据库与 Flyway 迁移 - 迁移脚本:`src/main/resources/db/migration/V1__init_schema.sql` - 应用启动时 Flyway 自动执行,包含: - 全部业务表(组织 `t_org`、商户 `t_merchant`、渠道 `t_pay_channel`、账号 `t_channel_account`、路由规则 `t_route_rule`、支付订单 `t_pay_order`、退款 `t_refund`、通知 `t_notify_record`、轮询任务 `t_poll_task`、分账 `t_split_rule` / `t_split_receiver` / `t_split_record` 等)。 - **演示种子数据**:平台组织、演示商户、3 个渠道(支付宝当面付 / 微信 JSAPI / Stripe 卡)、渠道参数、渠道账号、渠道路由与账号路由规则、示例分账规则。 - JSON 列(`ext_config` / `credentials` / `conditions` / `actions` / `extra` / `payload` / `receivers` 等)通过 MyBatis-Plus 的 `JacksonTypeHandler` 与实体字段映射。 - 多数字段含 `deleted` 逻辑删除标记(MyBatis-Plus 全局配置自动过滤)。 --- ## 九、API 接口清单 所有响应统一封装为: ```json { "code": 0, "message": "ok", "data": { } } ``` ### 9.1 商户统一网关(`/api/v1`) | 方法 | 路径 | 说明 | 限流(60s) | |---|---|---|---| | POST | `/api/v1/pay/unified` | 统一下单 | 200 | | POST | `/api/v1/pay/close` | 关闭订单(`payrouteOrderNo`) | 100 | | GET | `/api/v1/pay/query` | 查询订单(`payrouteOrderNo`) | 200 | | GET | `/api/v1/pay/methods` | 可用支付方式列表(可按 `merchantNo` / `payMethod` 过滤) | - | | POST | `/api/v1/refund/apply` | 申请退款 | 50 | | GET | `/api/v1/refund/query` | 退款查询(`outRefundNo`) | - | **统一下单请求 `UnifiedPayRequest`:** | 字段 | 类型 | 说明 | |---|---|---| | `merchantNo` | String | 商户号 | | `outTradeNo` | String | 商户侧订单号 | | `amount` | Long | 金额,**单位:分** | | `currency` | String | 币种,默认 `CNY` | | `subject` | String | 订单标题 | | `body` | String | 订单描述 | | `payMethod` | String | 支付方式(ALIPAY / WECHAT / CARD) | | `scene` | String | 场景(BAR / JSAPI / APP ...) | | `payerId` | String | 支付者标识(openid / buyer_id) | | `authCode` | String | 条码支付 auth_code | | `clientIp` | String | 客户端 IP | | `device` | String | 设备 | | `notifyUrl` | String | 异步通知地址 | | `returnUrl` | String | 同步回跳地址 | | `extra` | Map | 扩展参数(JSON) | **下单示例:** ```bash curl -X POST http://localhost:8080/api/v1/pay/unified \ -H 'Content-Type: application/json' \ -d '{ "merchantNo": "M20260001", "outTradeNo": "OUT2026091500001", "amount": 1000, "currency": "CNY", "subject": "测试商品", "payMethod": "ALIPAY", "scene": "BAR", "notifyUrl": "https://merchant.example.com/notify" }' ``` ### 9.2 渠道异步回调(`/notify/{channel}`) | 方法 | 路径 | 说明 | |---|---|---| | POST | `/notify/{channel}` | 渠道异步回调入口(`channel` = 渠道编码,如 `ALIPAY_BAR`) | 处理流程:定位渠道 → 解析回调 → 定位订单 → 凭据验签 → 更新订单支付状态(`confirmPaid`)。 ### 9.3 管理后台(`/admin/*`) **组织 `Org`** | 方法 | 路径 | |---|---| | POST | `/admin/org/create` | | POST | `/admin/org/update` | | POST | `/admin/org/delete/{orgId}` | | GET | `/admin/org/{orgId}` | | GET | `/admin/org/tree` | | GET | `/admin/org/descendants/{orgId}` | | POST | `/admin/org/move`(`orgId`,`newParentId`) | **商户 `Merchant`** | 方法 | 路径 | |---|---| | POST | `/admin/merchant/create` | | POST | `/admin/merchant/update` | | GET | `/admin/merchant/{merchantId}` | | GET | `/admin/merchant/org/{orgId}` | | POST | `/admin/merchant/config/save` | | GET | `/admin/merchant/config/{merchantId}` | | POST | `/admin/merchant/bind`(`merchantId`,`accountId`) | **渠道 `PayChannel`** | 方法 | 路径 | |---|---| | POST | `/admin/channel/create` | | POST | `/admin/channel/update` | | GET | `/admin/channel/{channelId}` | | GET | `/admin/channel/list`(`orgId`) | | POST | `/admin/channel/{channelId}/param` | | GET | `/admin/channel/{channelId}/param` | | POST | `/admin/channel/{channelId}/capability`(`capability`) | | GET | `/admin/channel/{channelId}/capability` | **渠道账号 `ChannelAccount`(凭据脱敏展示,写入加密)** | 方法 | 路径 | |---|---| | POST | `/admin/account/create` | | POST | `/admin/account/update` | | POST | `/admin/account/{accountId}/rotate-key` | | GET | `/admin/account/{accountId}`(脱敏) | | GET | `/admin/account/channel/{channelId}` | | GET | `/admin/account/org/{orgId}` | | POST | `/admin/account/{accountId}/health`(`healthStatus`) | | POST | `/admin/account/{accountId}/param`(`paramKey`,`paramValue`,`encrypted`) | | GET | `/admin/account/{accountId}/param` | **路由规则 `RouteRule`(渠道路由 rule_type=1 / 账号路由 rule_type=2)** | 方法 | 路径 | |---|---| | POST | `/admin/route-rule/create` | | POST | `/admin/route-rule/update` | | POST | `/admin/route-rule/delete/{ruleId}` | | GET | `/admin/route-rule/list`(`ruleType`,`orgId`) | | POST | `/admin/route-rule/dry-run`(`RouteContext`) | **分账规则 `SplitRule`** | 方法 | 路径 | |---|---| | POST | `/admin/split-rule/create` | | POST | `/admin/split-rule/update` | | GET | `/admin/split-rule/{ruleId}` | | GET | `/admin/split-rule/merchant/{merchantId}` | | POST | `/admin/split-rule/receiver` | | GET | `/admin/split-rule/receiver/{merchantId}` | --- ## 十、核心业务流程 ### 10.1 支付下单与路由 1. 商户调用 `POST /api/v1/pay/unified`。 2. 网关层完成**签名校验 → 限流 → 幂等防重**(Redis 锁 / 去重)。 3. 支付核心生成 `PayOrder`(状态机:`INIT → PAYING → PAID/FAILED/CLOSED`)。 4. **路由引擎**两级选择:先按 `rule_type=1` 选渠道,再按 `rule_type=2` 选账号;支持权重 / 成本 / 固定策略,失败 `fallback` 与 `failover`,含熔断冷却。 5. 调用对应渠道适配器 `pay()`,返回渠道支付参数 / 跳转信息。 6. 渠道异步回调 `/notify/{channel}` 验签后 `confirmPaid`,完成状态推进。 ### 10.2 异步通知 - 渠道回调 → `NotifyController` 验签 → 更新订单状态。 - 业务通知通过 RocketMQ 解耦,由 `NotifyWorker` 向商户 `notifyUrl` 推送,失败按 `payroute.notify.retry-intervals-seconds` **指数退避重试**(默认 10 次后转人工)。 ### 10.3 退款 - `POST /api/v1/refund/apply` → `RefundService`,支持**原路退回**与**分账回退(royalty_return)**。 - `GET /api/v1/refund/query` 查询退款状态。 ### 10.4 订单轮询补偿 - `PollWorker` 按分片(`shard-count: 16`)扫描处于中间态(`PAYING` 等)的订单,调用渠道 `query()` 兜底补偿,失败按 `payroute.poll` 配置退避重入队(最多 30 次)。 ### 10.5 分账 - 支付成功后触发分账引擎:依据 `SplitRule.receivers`(JSON)生成 `t_split_record` 明细。 - 支持冻结 / 解冻、分账回退、对账校验(当前为可扩展 stub,便于接入各渠道分账 API)。 --- ## 十一、路由引擎与规则 DSL 路由规则以 JSON 存储于 `t_route_rule` 的 `conditions` / `actions` 列,由路由引擎解析为 DSL 对象(字段类型为 `String`,运行时解释求值): - `conditions`:匹配条件,例如 ```json {"and":[{"field":"amount","op":"gte","value":10000}]} ``` - `actions`:路由动作,例如 ```json {"strategy":"weighted","weighted":{"STRIPE_CARD":100}} {"strategy":"fixed","fixed":["ACC_WECHAT_01"]} ``` 支持策略:`weighted`(权重)、`fixed`(固定)、`cost`(成本优先)等;含 `fallbackRuleId` 兜底与 `priority` 优先级排序。管理后台提供 `/admin/route-rule/dry-run` 用于在线试算。 --- ## 十二、渠道适配器 渠道通过 **SPI**(`com.payroute.channel.spi.ChannelAdapter` + `ChannelAdapterRegistry`)抽象,新增渠道只需实现接口并在注册中心登记: | 渠道 | 适配器 | 说明 | |---|---|---| | 支付宝 | `channel.alipay.AlipayAdapter` | 当面付等,按真实 API 对接 | | 微信 | `channel.wechat.WechatAdapter` | JSAPI 等 | | Stripe | `channel.stripe.StripeAdapter` | 卡支付等 | 适配器运行时从 `t_channel_account` 读取已加密凭据(经 AES 解密),结合 `t_channel_param` / `t_account_param_value` 组装请求,并对回调做验签。 --- ## 十三、安全设计 | 维度 | 实现 | |---|---| | **请求签名** | `SignInterceptor` 对商户请求验签,默认 `RSA2`(见 `payroute.signature.default-sign-type`) | | **接口限流** | `RateLimitInterceptor` + `@RateLimit` 注解,基于 Redis 计数(各接口独立阈值) | | **凭据加密** | `common.crypto` 使用 **AES-256-GCM**,主密钥由 `PAYROUTE_MASTER_KEY` 注入;账号凭据入库加密、展示脱敏(`AccountService.getMasked`) | | **密钥轮转** | `/admin/account/{accountId}/rotate-key` 支持账号密钥轮转 | | **幂等防重** | 下单 / 退款基于 `outTradeNo` / `outRefundNo` + Redis 去重锁 | | **逻辑删除 / 审计** | 全局 `deleted` 逻辑删除;实体含 `createdAt` / `updatedAt` 时间戳 | | **Body 可重读** | `BodyCacheFilter` 缓存请求体,支持拦截器多次读取(验签 + 业务) | --- ## 十四、定时任务 `@EnableScheduling` 开启,关键任务位于 `com.payroute.job`: | 任务 | 调度 | 说明 | |---|---|---| | 日限额清零 | `0 0 0 * * ?` | 重置渠道账号当日已用额度 `used_amount_today` | | 重试扫描 | `fixedDelay=300000`(5 分钟) | 扫描通知 / 轮询重试,触发补偿 | | 其他维护 | `0 30 2 * * ?` | 对账 / 数据维护类任务 | > 说明:各 Worker(`NotifyWorker` / `PollWorker` / `SplitWorker`)在上下文刷新后首次立即执行并访问数据库,因此**运行时需保证 MySQL 与 Redis 可用**。 --- ## 十五、部署与运维 - **单机 jar**:`java -jar target/starry-shift.jar`,依赖外部 MySQL / Redis / RocketMQ。 - **多实例**:应用本身无状态(状态在 MySQL / Redis / RocketMQ),可水平扩展;限流 / 幂等 / 锁依赖 Redis,请确保 Redis 高可用。 - **配置来源**:生产环境建议通过外部 `application.yml` 或环境变量覆盖默认配置(尤其是 `PAYROUTE_MASTER_KEY`、数据库与 Redis 连接)。 - **日志**:`logback-spring.xml` 输出至 `logs/`;`com.payroute` 默认 `debug`,生产可调高阈值。 - **迁移演进**:后续表结构变更以新增 `V2__*.sql` 等 Flyway 脚本管理,避免手动改表。 --- ## 十六、常见问题与排错 ### Q1:启动报 `BeanCreationException` / `sqlSessionFactory` 相关 本工程显式在 `MybatisPlusConfig` 中声明 `SqlSessionFactory` bean,**未依赖 MyBatis-Plus 自动配置**。原因:MyBatis-Plus 3.5.7 自动配置的条件基于 `javax.sql.DataSource`,而 Spring Boot 4 的 `DataSource` 实际亦为 JDK 内置的 `javax.sql.DataSource`(JDBC 命名空间未随 Jakarta 改名),自动配置条件未命中导致 `SqlSessionFactory` 缺失。如改动配置,请保持手动声明。 ### Q2:启动报 `No typehandler found for property xxx` JSON 列对应的实体字段需标注 `@TableField(typeHandler = JacksonTypeHandler.class)` 且实体 `@TableName(autoResultMap = true)`。当前 `extConfig` / `credentials` / `receivers` / `extra` / `payload` / `extParams` / `contactInfo` / `businessLicense` / `payMethods` 均已标注。 ### Q3:启动报 `主密钥长度必须为 32 字节` 环境变量 `PAYROUTE_MASTER_KEY`(或 dev profile 的 `payroute.crypto.dev-master-key`)解码后必须为 **32 字节**(AES-256 要求)。dev 模式已内置演示密钥,无需手动设置;生产请使用随机生成的 32 字节 base64(参见第六章第 3 步)。 ### Q4:`Access denied for user 'payroute'@'localhost'` MySQL 账号 / 密码 / 库名与 `application.yml` 的 `spring.datasource` 不一致。请确认已按第六章创建 `payroute` 库与账号,或修改连接配置。 ### Q5:`Could not open JDBC Connection` / Redis 连接失败 确认 MySQL(3306)、Redis(6379)已启动且网络可达;RocketMQ(9876)在异步通知 / 轮询 / 分账路径上会用到。 ### Q6:如何查看接口文档 启动后访问 `http://localhost:8080/swagger-ui.html`(OpenAPI:`/v3/api-docs`)。 --- ## 十七、相关文档 - `PayRoute架构设计文档.md`:完整架构、数据模型(ER)、流程与时序、安全与高可用、接口清单详版。 - `IMPLEMENTATION_CONTRACTS.md`:实现与设计的对齐说明 / 契约。 - 迁移脚本:`src/main/resources/db/migration/V1__init_schema.sql`。 --- ## 十八、License 本项目以开源方式提供,供中小商户与独立开发者学习与集成使用。具体许可条款以仓库 LICENSE 文件为准。 > **合规提示**:斗转星移(PayRoute)仅做支付通道的抽象与编排,**自身不进行资金清算、不持有支付牌照**。在生产环境接入渠道前,请确保业务方具备相应的支付业务资质与合规要求。