docs: 迁移 spring-ai 通用文档到 rui-docs

从 docs-local 迁移以下文档:

- backend/guides/: AI开发环境配置、Nacos配置、GitNexus指南、OpenCode工作流等

- backend/templates/: Superpowers设计模板、计划模板、审查清单

- backend/config-templates/: 应用配置模板、Nacos配置

- backend/design/: 数据库表结构规划

- backend/specs/: 项目文档治理、MQ统一推送设计

- backend/: 代码分析报告、Feign分析报告、文档治理报告

- frontend/design/: Admin-UI分模块打包设计
This commit is contained in:
2026-06-04 09:31:24 +08:00
parent 2e38c53434
commit 19de7e24ec
36 changed files with 5872 additions and 0 deletions
@@ -0,0 +1,65 @@
# ============================================================================
# 睿核科技 — 通用平台框架 通用 application.yml 模板
# ============================================================================
# 使用方法:
# 复制到新模块的 src/main/resources/application.yml
# 修改 server.port 为模块对应端口
# 本地开发配置见项目根目录 config/application-dev.yml(不提交 git
# ============================================================================
server:
port: XXXX # 模块端口(按规划分配)
shutdown: graceful # 优雅关闭
spring:
application:
name: @artifactId@ # Maven 过滤,自动替换为模块名
profiles:
active: @profiles.active@ # Maven 过滤,默认 dev
lifecycle:
timeout-per-shutdown-phase: 30s # 优雅关闭等待时间
autoconfigure:
exclude:
servlet:
multipart:
max-file-size: 5MB
max-request-size: 10MB
encoding:
charset: UTF-8
enabled: true
force: true
cloud:
openfeign:
circuitbreaker:
enabled: true
nacos:
discovery: # 服务发现(独立环境变量,不依赖 config)
server-addr: ${NACOS_SERVER_ADDR:nacos:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
config: # 配置中心
server-addr: ${NACOS_SERVER_ADDR:nacos:8848}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
file-extension: yaml
import-check:
enabled: true
config:
import:
- optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension:yaml}
- optional:nacos:rui-common.${spring.cloud.nacos.config.file-extension:yaml}
# 无 MyBatis 的模块(gateway)请删除下面这行
- optional:nacos:rui-data.${spring.cloud.nacos.config.file-extension:yaml}
management:
endpoints:
web:
exposure:
include: health
discovery:
enabled: false
@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
通用日志配置,复制到任意模块无需修改
- 自动读取 spring.application.name 作为日志目录名
- 按级别精确分文件:debug / info / warn / error
- 控制台彩色输出,文件不含颜色码
- dev 环境输出 DEBUG,其他环境输出 INFO
-->
<configuration scan="true" scanPeriod="10 seconds">
<!-- 彩色日志转换器 -->
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex" class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx" class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 读取 Spring 应用名称,复制到任意模块无需修改 -->
<springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="app"/>
<property name="LOG_PATH" value="logs/${APP_NAME}"/>
<!-- 文件日志格式:含时间、线程、级别、类名、代码行号、消息 -->
<property name="FILE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%line - %msg%n"/>
<!-- 控制台彩色日志格式(%clr 根据级别自动着色:ERROR 红、WARN 黄、INFO 绿、DEBUG 蓝、TRACE 青) -->
<property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr([%thread]){faint} %clr(%-5level) %clr(%logger{50}:%line){cyan} %clr(-){faint} %msg%n"/>
<!-- ==================== 控制台输出 ==================== -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ==================== DEBUG 日志文件 ==================== -->
<!-- 精确匹配 DEBUG 级别,dev 环境下生效 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/debug.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/debug/log-debug.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>200MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ==================== INFO 日志文件 ==================== -->
<!-- 精确匹配 INFO 级别 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/info.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/info/log-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>200MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ==================== WARN 日志文件 ==================== -->
<!-- 精确匹配 WARN 级别 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/warn.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/warn/log-warn.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>200MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ==================== ERROR 日志文件 ==================== -->
<!-- 精确匹配 ERROR 级别 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/history/error/log-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>200MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- ==================== 特定包日志级别调整 ==================== -->
<logger name="org.apache.catalina.connector.CoyoteAdapter" level="OFF"/>
<logger name="org.apache.seata.config.FileConfiguration" level="OFF"/>
<!-- ==================== 生产环境配置 ==================== -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<!-- ==================== 开发环境配置 ==================== -->
<springProfile name="dev">
<logger name="com.baomidou.mybatisplus" level="DEBUG"/>
<logger name="com.rui" level="DEBUG"/>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<!-- ==================== 其他环境默认配置 ==================== -->
<springProfile name="!prod,!dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
</configuration>
@@ -0,0 +1,7 @@
# rui-auth.yaml — 认证中心模块配置
# Data ID: rui-auth.yaml
# Group: DEFAULT_GROUP
# 服务端口:9301(认证中心,所有服务依赖)
server:
port: 9301
@@ -0,0 +1,47 @@
# rui-common.yaml — 所有模块共享
# Data ID: rui-common.yaml
# Group: DEFAULT_GROUP
# 用途: 全局通用配置(Redis、Jackson、JWT、安全白名单)
spring:
rabbitmq:
host: ${RABBITMQ_HOST:192.168.31.210}
port: ${RABBITMQ_PORT:5672}
username: ${RABBITMQ_USERNAME:vifo}
password: ${RABBITMQ_PASSWORD:!QW3e4r2023}
data:
redis:
host: ${REDIS_HOST:192.168.31.210}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:123456}
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
security:
oauth2:
ignore-urls:
- /entry/**
- /notify/**
- /actuator/**
# MyBatis Plus 全局配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
# Feign 服务提供者配置(默认走聚合启动器,独立模式请在各服务配置中覆盖)
feign:
providers:
user: rui-service-starter # 用户服务:默认指向聚合启动器
system: rui-service-starter # 系统服务:默认指向聚合启动器
auth: rui-auth # 认证中心:保持独立
@@ -0,0 +1,74 @@
# rui-data.yaml — 数据库模块共享
# Data ID: rui-data.yaml
# Group: DEFAULT_GROUP
# 用途: 数据源 + MyBatis Plus 配置(仅 DB 模块导入)
spring:
datasource:
url: jdbc:mysql://${DB_HOST:192.168.31.210}:3306/rui_platform?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:123456}
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
mybatis-plus:
global-config:
db-config:
table-prefix: rui_
id-type: assign_id
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:/mapper/**/*.xml
tenant:
mode: IGNORE
tenant-ignore:
- sys_tenant
- user_credential
- system_oauth2_client
# 系统租户也需限制租户隔离的表(即使是系统租户,也只能查自己租户的数据)
system-tenant-table:
- uc_user
- uc_user_detail
- uc_user_role
- uc_user_dept
- uc_user_post
- uc_user_permission
- uc_user_level
- uc_user_level_log
- sys_menu
- sys_role
- sys_role_menu
- sys_role_dept
- sys_dept
- sys_post
- sys_dict_type
- sys_dict_item
- sys_config
- sys_oper_log
- sys_login_log
seata:
enabled: true
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
data-source-proxy-mode: AT
registry:
type: nacos
nacos:
application: seata-server
server-addr: ${spring.cloud.nacos.config.server-addr}
group : SEATA_GROUP
namespace: cloud
username: ${spring.cloud.nacos.config.username}
password: ${spring.cloud.nacos.config.password}
@@ -0,0 +1,80 @@
# rui-gateway.yaml — 网关路由配置 (Gateway 5.x 格式:server.webflux.routes)
# 说明:默认使用聚合模式(所有业务路由指向 rui-service-starter
# 如需独立微服务模式,取消注释独立路由并注释掉聚合路由
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: rui-auth
uri: lb://rui-auth
predicates:
- Path=/auth/**
filters:
- StripPrefix=0
- id: rui-auth
uri: lb://rui-auth
predicates:
- Path=/oauth2/**
filters:
- StripPrefix=0
- id: rui-service-pay
uri: lb://rui-service-pay
predicates:
- Path=/payment/**
filters:
- StripPrefix=0
# ========== 聚合模式(默认,中小型项目)==========
- id: rui-service-starter
uri: lb://rui-service-starter
predicates:
- Path=/user/**,/system/**
filters:
- StripPrefix=0
- id: rui-cashier
uri: lb://rui-cashier-api
predicates:
- Path=/cashier/**
filters:
- StripPrefix=0
# ========== 独立微服务模式(大型项目)==========
# - id: rui-service-user
# uri: lb://rui-service-user
# predicates:
# - Path=/user/**
# filters:
# - StripPrefix=0
# - id: rui-service-system
# uri: lb://rui-service-system
# predicates:
# - Path=/system/**
# filters:
# - StripPrefix=0
# 灰度发布配置
# 支持按服务配置不同的灰度策略,包括:权重、用户白名单、IP 白名单、强制 Header
grayscale:
# 强制灰度 Header 名称,客户端通过此 Header 强制指定版本
force-header: X-Grayscale-Version
# 灰度规则(Key 为服务名,如 rui-service-user
# 配置示例:
# rules:
# rui-service-user:
# enabled: true
# version: v2
# weight: 10
# user-ids:
# - user001
# ip-ranges:
# - 192.168.1.0/24
management:
endpoints:
web:
exposure:
include: health # 仅暴露 health 端点,其余全部禁止访问
# 服务端口:9300(网关最优先)
server:
port: 9300
@@ -0,0 +1,8 @@
# rui-service-cache.yaml — 缓存服务配置
# Data ID: rui-service-cache.yaml
# Group: DEFAULT_GROUP
# 功能:Redis缓存封装、分布式锁
# 服务端口:9309(缓存服务)
server:
port: 9309
@@ -0,0 +1,8 @@
# rui-service-file.yaml — 文件存储服务配置
# Data ID: rui-service-file.yaml
# Group: DEFAULT_GROUP
# 功能:文件上传、下载、OOS存储、本地存储
# 服务端口:9305(文件服务)
server:
port: 9305
@@ -0,0 +1,8 @@
# rui-service-log.yaml — 日志服务配置
# Data ID: rui-service-log.yaml
# Group: DEFAULT_GROUP
# 功能:日志收集、日志分析、ELK集成
# 服务端口:9310(日志服务)
server:
port: 9310
@@ -0,0 +1,8 @@
# rui-service-monitor.yaml — 监控告警服务配置
# Data ID: rui-service-monitor.yaml
# Group: DEFAULT_GROUP
# 功能:系统监控、性能指标、告警通知
# 服务端口:9311(监控服务)
server:
port: 9311
@@ -0,0 +1,8 @@
# rui-service-msg.yaml — 消息通知服务配置
# Data ID: rui-service-msg.yaml
# Group: DEFAULT_GROUP
# 功能:SMS短信、邮件、站内信、App推送
# 服务端口:9304(消息服务)
server:
port: 9304
@@ -0,0 +1,7 @@
# rui-service-order.yaml — 订单服务配置
# Data ID: rui-service-order.yaml
# Group: DEFAULT_GROUP
# 服务端口:9306(订单服务)
server:
port: 9306
@@ -0,0 +1,60 @@
# rui-service-pay.yaml — 支付服务配置
# Data ID: rui-service-pay.yaml
# Group: DEFAULT_GROUP
# 功能:支付通道、支付回调、对账
# 服务端口:9307(支付服务)
server:
port: 9307
# 支付模块配置
payment:
# 订单超时时间(分钟)
order-timeout: 30
# 分账延迟时间(天)
split-delay-days: 7
# 佣金结算周期
commission-settle-cycle: T+1
# 通知重试次数
notify-max-times: 5
# 通知间隔(秒)
notify-intervals: 15,30,60,300,900
# 渠道配置(示例)
payment:
channels:
alipay:
enabled: true
sandbox: true
app-id: ${ALIPAY_APP_ID:}
private-key: ${ALIPAY_PRIVATE_KEY:}
public-key: ${ALIPAY_PUBLIC_KEY:}
notify-url: http://localhost:9307/payment/notify/alipay
wechat_pay:
enabled: true
sandbox: true
app-id: ${WXPAY_APP_ID:}
mch-id: ${WXPAY_MCH_ID:}
api-v3-key: ${WXPAY_API_V3_KEY:}
notify-url: http://localhost:9307/payment/notify/wechat_pay
# 安全白名单
security:
oauth2:
ignore-urls:
- /payment/entry/**
- /payment/notify/**
# Swagger 文档
springdoc:
swagger-ui:
enabled: true
path: /swagger-ui.html
api-docs:
enabled: true
path: /v3/api-docs
knife4j:
enable: true
setting:
language: zh_cn
@@ -0,0 +1,8 @@
# rui-service-search.yaml — 搜索服务配置
# Data ID: rui-service-search.yaml
# Group: DEFAULT_GROUP
# 功能:Elasticsearch封装、全文检索
# 服务端口:9308(搜索服务)
server:
port: 9308
@@ -0,0 +1,33 @@
# rui-service-system.yaml — 系统服务配置
# Data ID: rui-service-system.yaml
# Group: DEFAULT_GROUP
# 服务端口:9302(系统基础服务)
server:
port: 9302
# 模块管理配置
rui:
modules:
# 全局可用模块列表(系统层面定义有哪些模块可选)
available:
- code: system
name: 系统管理
icon: tabler:settings
- code: user
name: 用户管理
icon: tabler:users
- code: order
name: 订单管理
icon: tabler:shopping-cart
- code: cms
name: 内容管理
icon: tabler:edit
- code: marketing
name: 营销中心
icon: tabler:present
- code: demo
name: 演示中心
icon: tabler:device-desktop
# 默认启用模块(新租户默认开启,逗号分隔)
default-enabled: system,user,demo
@@ -0,0 +1,7 @@
# rui-service-user.yaml — 用户基础信息及等级服务配置
# Data ID: rui-service-user.yaml
# Group: DEFAULT_GROUP
# 服务端口:9303(用户中心服务)
server:
port: 9303
+664
View File
@@ -0,0 +1,664 @@
# 通用平台框架 — 数据库表结构规划
> 基于当前项目分析和通用平台框架最佳实践,列出平台运行所需的全量表结构及归属服务。
> **注意:本规划仅做设计,不做任何代码实施。**
---
## 一、表结构总览
| 服务归属 | 表数量 | 表名前缀 | 说明 |
|---------|--------|---------|------|
| **rui-service-system** | 11 | `sys_` | 系统基础服务(租户/菜单/角色/字典/配置/部门/岗位) |
| **rui-service-user** | 7 | `uc_` | 用户中心服务(用户/详情/等级/角色/部门/岗位/权限) |
| **rui-auth** | 3 | `auth_` | 认证中心(OAuth2客户端/登录日志/操作日志) |
| **rui-gateway** | 2 | `gw_` | 网关服务(路由配置/限流规则) |
| **公共表** | 2 | — | 跨服务使用(文件/消息) |
| **合计** | **25** | | |
> **命名调整建议**:当前 `rui_xxx` 前缀过长,建议简化为服务简称前缀,如 `sys_`、`uc_`、`auth_`、`gw_`,更简洁专业。
---
## 二、rui-service-system 系统基础服务(11张表)
### 2.1 租户管理
```sql
-- sys_tenant — 租户(原 rui_system_tenant 改进)
CREATE TABLE sys_tenant (
id BIGINT NOT NULL,
tenant_code VARCHAR(50) NOT NULL COMMENT '租户编码',
tenant_name VARCHAR(200) NOT NULL COMMENT '租户名称',
tenant_type TINYINT NOT NULL DEFAULT 1 COMMENT '类型 1:企业 2:个人 3:试用',
tenant_level TINYINT NOT NULL DEFAULT 1 COMMENT '等级 1:基础版 2:专业版 3:旗舰版',
logo_url VARCHAR(500) DEFAULT NULL COMMENT 'Logo',
contact_name VARCHAR(100) DEFAULT NULL,
contact_phone VARCHAR(20) DEFAULT NULL,
contact_email VARCHAR(100) DEFAULT NULL,
domains VARCHAR(500) DEFAULT NULL COMMENT '绑定域名(逗号分隔)',
max_user_count INT NOT NULL DEFAULT 100 COMMENT '最大用户数',
expire_time DATETIME DEFAULT NULL COMMENT '过期时间 NULL:永久',
super_admin_id BIGINT DEFAULT NULL COMMENT '超管用户ID',
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_tenant_code (tenant_code),
INDEX idx_status (status)
) COMMENT='租户';
-- sys_tenant_package — 租户套餐(新增)
CREATE TABLE sys_tenant_package (
id BIGINT NOT NULL,
package_code VARCHAR(100) NOT NULL COMMENT '套餐编码',
package_name VARCHAR(200) NOT NULL COMMENT '套餐名称',
max_user_count INT NOT NULL DEFAULT 100,
max_dept_count INT NOT NULL DEFAULT 50,
max_menu_count INT NOT NULL DEFAULT 100,
price DECIMAL(19,4) NOT NULL DEFAULT 0 COMMENT '价格',
duration_days INT NOT NULL DEFAULT 365 COMMENT '时长(天)',
remark VARCHAR(500) DEFAULT NULL,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_package_code (package_code)
) COMMENT='租户套餐';
```
### 2.2 组织架构
```sql
-- sys_dept — 部门(新增)
CREATE TABLE sys_dept (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
parent_id BIGINT NOT NULL DEFAULT 0,
ancestors VARCHAR(500) DEFAULT '' COMMENT '祖级ID列表 如: 0,1,5,',
dept_code VARCHAR(100) NOT NULL COMMENT '部门编码',
dept_name VARCHAR(100) NOT NULL COMMENT '部门名称',
leader_id BIGINT DEFAULT NULL COMMENT '负责人ID',
phone VARCHAR(20) DEFAULT NULL,
email VARCHAR(100) DEFAULT NULL,
sort_no INT NOT NULL DEFAULT 0,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_dept_code (tenant_id, dept_code),
INDEX idx_parent (parent_id),
INDEX idx_tenant (tenant_id)
) COMMENT='部门';
-- sys_post — 岗位(新增)
CREATE TABLE sys_post (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
post_code VARCHAR(100) NOT NULL COMMENT '岗位编码',
post_name VARCHAR(100) NOT NULL COMMENT '岗位名称',
sort_no INT NOT NULL DEFAULT 0,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_post_code (tenant_id, post_code),
INDEX idx_tenant (tenant_id)
) COMMENT='岗位';
```
### 2.3 菜单与权限
```sql
-- sys_menu — 菜单(原 rui_system_menu 改进)
CREATE TABLE sys_menu (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父菜单ID 0:顶级',
ancestors VARCHAR(500) DEFAULT '' COMMENT '祖级ID列表',
menu_code VARCHAR(100) NOT NULL COMMENT '菜单编码(唯一标识)',
menu_name VARCHAR(100) NOT NULL COMMENT '菜单名称',
menu_type TINYINT NOT NULL DEFAULT 1 COMMENT '类型 1:目录 2:菜单 3:按钮',
icon VARCHAR(100) DEFAULT NULL COMMENT '图标',
path VARCHAR(200) DEFAULT NULL COMMENT '路由路径',
component VARCHAR(200) DEFAULT NULL COMMENT '组件路径',
permission VARCHAR(200) DEFAULT NULL COMMENT '权限标识 如: user:list',
is_external TINYINT NOT NULL DEFAULT 0 COMMENT '是否外链 0:否 1:是',
target VARCHAR(20) DEFAULT '_self' COMMENT '打开方式 _self/_blank',
keep_alive TINYINT NOT NULL DEFAULT 0 COMMENT '是否缓存 0:否 1:是',
visible TINYINT NOT NULL DEFAULT 1 COMMENT '是否显示 0:隐藏 1:显示',
status TINYINT NOT NULL DEFAULT 1,
sort_no INT NOT NULL DEFAULT 0,
remark VARCHAR(500) DEFAULT NULL,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_menu_code (tenant_id, menu_code),
INDEX idx_parent (parent_id),
INDEX idx_ancestors (ancestors(100)),
INDEX idx_tenant (tenant_id)
) COMMENT='菜单';
```
### 2.4 角色管理
```sql
-- sys_role — 角色(原 rui_system_role 改进)
CREATE TABLE sys_role (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
parent_id BIGINT NOT NULL DEFAULT 0,
ancestors VARCHAR(500) DEFAULT '' COMMENT '祖级ID列表',
role_code VARCHAR(100) NOT NULL COMMENT '角色编码',
role_name VARCHAR(100) NOT NULL COMMENT '角色名称',
role_type TINYINT NOT NULL DEFAULT 1 COMMENT '类型 1:系统角色 2:租户角色 3:自定义',
data_scope TINYINT NOT NULL DEFAULT 1 COMMENT '数据范围 1:全部 2:本部门 3:本部门及子部门 4:仅本人 5:自定义',
description VARCHAR(500) DEFAULT NULL,
sort_no INT NOT NULL DEFAULT 0,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_role_code (tenant_id, role_code),
INDEX idx_parent (parent_id),
INDEX idx_tenant (tenant_id)
) COMMENT='角色';
-- sys_role_menu — 角色菜单关联(原 rui_system_role_menu 改进)
CREATE TABLE sys_role_menu (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
role_id BIGINT NOT NULL,
menu_id BIGINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_role_menu (tenant_id, role_id, menu_id),
INDEX idx_role (role_id),
INDEX idx_menu (menu_id),
INDEX idx_tenant (tenant_id)
) COMMENT='角色菜单关联';
```
### 2.5 数据字典
```sql
-- sys_dict_type — 字典类型(新增,拆分原字典表)
CREATE TABLE sys_dict_type (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
dict_code VARCHAR(100) NOT NULL COMMENT '字典编码(唯一标识)',
dict_name VARCHAR(200) NOT NULL COMMENT '字典名称',
description VARCHAR(500) DEFAULT NULL,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_dict_code (tenant_id, dict_code),
INDEX idx_tenant (tenant_id)
) COMMENT='字典类型';
-- sys_dict_item — 字典项(新增,拆分原字典表)
CREATE TABLE sys_dict_item (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
dict_type_id BIGINT NOT NULL COMMENT '字典类型ID',
item_code VARCHAR(100) NOT NULL COMMENT '项编码',
item_label VARCHAR(200) NOT NULL COMMENT '项标签',
item_value VARCHAR(200) NOT NULL COMMENT '项值',
sort_no INT NOT NULL DEFAULT 0,
remark VARCHAR(500) DEFAULT NULL,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_dict_type_code (tenant_id, dict_type_id, item_code),
INDEX idx_dict_type (dict_type_id),
INDEX idx_tenant (tenant_id)
) COMMENT='字典项';
```
### 2.6 系统配置
```sql
-- sys_config — 系统配置(原 rui_system_config 改进)
CREATE TABLE sys_config (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
config_group VARCHAR(100) NOT NULL DEFAULT 'default' COMMENT '配置分组(oss/sms/email',
config_key VARCHAR(200) NOT NULL COMMENT '配置键',
config_value VARCHAR(2000) NOT NULL COMMENT '配置值',
config_type VARCHAR(20) NOT NULL DEFAULT 'STRING' COMMENT '值类型 STRING/JSON/NUMBER/BOOLEAN/ENCRYPTED',
is_system TINYINT NOT NULL DEFAULT 0 COMMENT '是否系统级 0:租户 1:系统',
is_encrypted TINYINT NOT NULL DEFAULT 0 COMMENT '是否加密 0:明文 1:密文',
description VARCHAR(500) DEFAULT NULL,
sort_no INT NOT NULL DEFAULT 0,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_config_key (tenant_id, config_key),
INDEX idx_group (config_group),
INDEX idx_tenant (tenant_id)
) COMMENT='系统配置';
```
---
## 三、rui-service-user 用户中心服务(7张表)
### 3.1 用户主体
```sql
-- uc_user — 用户主表(原 rui_user_credential + rui_user_info 合并改进)
CREATE TABLE uc_user (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
username VARCHAR(100) NOT NULL COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码(BCrypt加密)',
user_type TINYINT NOT NULL DEFAULT 1 COMMENT '用户类型 1:普通用户 2:管理员 3:系统用户',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态 0:禁用 1:启用',
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_username (tenant_id, username),
INDEX idx_tenant (tenant_id)
) COMMENT='用户';
-- uc_user_detail — 用户详情表(原 rui_user_info 拆分)
CREATE TABLE uc_user_detail (
id BIGINT NOT NULL,
user_id BIGINT NOT NULL COMMENT '用户ID',
tenant_id BIGINT NOT NULL DEFAULT 0,
nickname VARCHAR(100) DEFAULT NULL COMMENT '昵称',
real_name VARCHAR(100) DEFAULT NULL COMMENT '真实姓名',
email VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
phone VARCHAR(20) DEFAULT NULL COMMENT '手机号',
avatar VARCHAR(1000) DEFAULT NULL COMMENT '头像URL',
gender TINYINT DEFAULT 0 COMMENT '性别 0:未知 1:男 2:女',
birthday DATE DEFAULT NULL,
id_card VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
address VARCHAR(500) DEFAULT NULL COMMENT '地址',
extra JSON DEFAULT NULL COMMENT '扩展字段(JSON格式)',
remark VARCHAR(500) DEFAULT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_id (user_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户详情';
```
### 3.2 用户等级
```sql
-- uc_user_level — 用户等级定义(原 rui_user_level 改进)
CREATE TABLE uc_user_level (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
level_code VARCHAR(100) NOT NULL COMMENT '等级编码',
level_name VARCHAR(100) NOT NULL COMMENT '等级名称',
level_no INT NOT NULL DEFAULT 1 COMMENT '等级序号(数字越大等级越高)',
min_score INT NOT NULL DEFAULT 0 COMMENT '最低积分要求',
icon VARCHAR(500) DEFAULT NULL COMMENT '等级图标',
benefits JSON DEFAULT NULL COMMENT '权益配置(JSON',
upgrade_type TINYINT NOT NULL DEFAULT 1 COMMENT '升级方式 1:自动 2:手动审核',
expire_days INT DEFAULT 0 COMMENT '有效期(天) 0:永久',
status TINYINT NOT NULL DEFAULT 1,
sort_no INT NOT NULL DEFAULT 0,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_level_code (tenant_id, level_code),
INDEX idx_tenant (tenant_id)
) COMMENT='用户等级';
-- uc_user_level_log — 用户等级变更记录(原 rui_user_level_log 改进)
CREATE TABLE uc_user_level_log (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL COMMENT '用户ID',
change_type TINYINT NOT NULL DEFAULT 1 COMMENT '变更类型 1:升级 2:降级 3:手动调整 4:过期',
from_level_id BIGINT DEFAULT NULL COMMENT '原等级ID NULL:初始',
to_level_id BIGINT NOT NULL COMMENT '新等级ID',
score_before INT DEFAULT NULL COMMENT '变更前积分',
score_after INT DEFAULT NULL COMMENT '变更后积分',
reason VARCHAR(500) DEFAULT NULL COMMENT '变更原因',
operator_id BIGINT DEFAULT NULL COMMENT '操作人ID',
operator_type TINYINT NOT NULL DEFAULT 1 COMMENT '操作人类型 1:系统 2:管理员 3:用户',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_user (user_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户等级变更记录';
```
### 3.3 用户关联
```sql
-- uc_user_role — 用户角色关联(原 rui_user_role 改进)
CREATE TABLE uc_user_role (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_role (tenant_id, user_id, role_id),
INDEX idx_user (user_id),
INDEX idx_role (role_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户角色关联';
-- uc_user_dept — 用户部门关联(新增)
CREATE TABLE uc_user_dept (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL,
dept_id BIGINT NOT NULL,
is_main TINYINT NOT NULL DEFAULT 1 COMMENT '是否主部门 0:否 1:是',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_dept (tenant_id, user_id, dept_id),
INDEX idx_user (user_id),
INDEX idx_dept (dept_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户部门关联';
-- uc_user_post — 用户岗位关联(新增)
CREATE TABLE uc_user_post (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL,
post_id BIGINT NOT NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_post (tenant_id, user_id, post_id),
INDEX idx_user (user_id),
INDEX idx_post (post_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户岗位关联';
-- uc_user_permission — 用户权限关联(新增,支持直接授权)
CREATE TABLE uc_user_permission (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL,
permission VARCHAR(200) NOT NULL COMMENT '权限标识 如: user:list',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_user_permission (tenant_id, user_id, permission),
INDEX idx_user (user_id),
INDEX idx_tenant (tenant_id)
) COMMENT='用户权限关联(直接授权)';
```
---
## 四、rui-auth 认证中心(3张表)
### 4.1 OAuth2 客户端
```sql
-- auth_oauth2_client — OAuth2客户端(原 rui_system_oauth2_client 改进)
CREATE TABLE auth_oauth2_client (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
client_id VARCHAR(100) NOT NULL COMMENT '客户端ID',
client_secret VARCHAR(255) DEFAULT NULL COMMENT '客户端密钥',
client_name VARCHAR(200) NOT NULL COMMENT '客户端名称',
client_type TINYINT NOT NULL DEFAULT 1 COMMENT '客户端类型 1:Web 2:App 3:小程序',
logo_url VARCHAR(500) DEFAULT NULL COMMENT 'Logo',
description VARCHAR(500) DEFAULT NULL,
authentication_methods VARCHAR(500) NOT NULL COMMENT '认证方式',
grant_types VARCHAR(500) NOT NULL COMMENT '授权类型',
redirect_uris TEXT DEFAULT NULL COMMENT '回调地址(逗号分隔)',
scopes VARCHAR(500) NOT NULL COMMENT '授权范围',
access_token_ttl INT NOT NULL DEFAULT 7200 COMMENT '访问令牌有效期(秒)',
refresh_token_ttl INT NOT NULL DEFAULT 604800 COMMENT '刷新令牌有效期(秒)',
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_client_id (tenant_id, client_id),
INDEX idx_tenant (tenant_id)
) COMMENT='OAuth2客户端';
```
### 4.2 日志审计
```sql
-- auth_login_log — 登录日志(新增)
CREATE TABLE auth_login_log (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT DEFAULT NULL,
username VARCHAR(100) DEFAULT NULL,
login_type TINYINT NOT NULL DEFAULT 1 COMMENT '登录类型 1:密码 2:短信 3:微信 4:支付宝',
client_id VARCHAR(100) DEFAULT NULL COMMENT '客户端ID',
ip VARCHAR(128) DEFAULT NULL,
location VARCHAR(255) DEFAULT NULL,
browser VARCHAR(200) DEFAULT NULL,
os VARCHAR(200) DEFAULT NULL,
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态 0:失败 1:成功',
msg VARCHAR(500) DEFAULT NULL COMMENT '消息',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_user (user_id),
INDEX idx_tenant (tenant_id),
INDEX idx_created (created_at)
) COMMENT='登录日志';
-- auth_oper_log — 操作日志(新增)
CREATE TABLE auth_oper_log (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
oper_type TINYINT NOT NULL DEFAULT 1 COMMENT '操作类型 1:新增 2:修改 3:删除 4:查询 5:导出 6:登录 7:登出',
title VARCHAR(200) NOT NULL COMMENT '操作模块',
method VARCHAR(500) DEFAULT NULL COMMENT '请求方法',
request_url VARCHAR(500) DEFAULT NULL COMMENT '请求URL',
request_method VARCHAR(10) DEFAULT NULL COMMENT '请求方式 GET/POST/PUT/DELETE',
request_params TEXT DEFAULT NULL COMMENT '请求参数',
response_data TEXT DEFAULT NULL COMMENT '响应数据',
user_id BIGINT DEFAULT NULL COMMENT '操作用户ID',
user_name VARCHAR(100) DEFAULT NULL COMMENT '操作用户名',
oper_ip VARCHAR(128) DEFAULT NULL COMMENT '操作IP',
oper_location VARCHAR(255) DEFAULT NULL COMMENT '操作地点',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态 0:失败 1:成功',
error_msg TEXT DEFAULT NULL COMMENT '错误消息',
cost_time BIGINT DEFAULT 0 COMMENT '耗时(ms)',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_user (user_id),
INDEX idx_tenant (tenant_id),
INDEX idx_created (created_at)
) COMMENT='操作日志';
```
---
## 五、rui-gateway 网关服务(2张表)
```sql
-- gw_route — 路由配置(新增)
CREATE TABLE gw_route (
id BIGINT NOT NULL,
route_id VARCHAR(100) NOT NULL COMMENT '路由ID',
route_name VARCHAR(200) NOT NULL COMMENT '路由名称',
uri VARCHAR(500) NOT NULL COMMENT '目标URI',
predicates VARCHAR(1000) NOT NULL COMMENT '断言条件(JSON',
filters VARCHAR(1000) DEFAULT NULL COMMENT '过滤器(JSON',
sort_no INT NOT NULL DEFAULT 0,
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_route_id (route_id)
) COMMENT='网关路由配置';
-- gw_rate_limit — 限流规则(新增)
CREATE TABLE gw_rate_limit (
id BIGINT NOT NULL,
route_id VARCHAR(100) NOT NULL COMMENT '路由ID',
limit_type TINYINT NOT NULL DEFAULT 1 COMMENT '限流类型 1:IP 2:用户 3:接口',
limit_key VARCHAR(200) NOT NULL COMMENT '限流键',
limit_count INT NOT NULL DEFAULT 100 COMMENT '限流次数',
limit_window INT NOT NULL DEFAULT 60 COMMENT '时间窗口(秒)',
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_route (route_id)
) COMMENT='网关限流规则';
```
---
## 六、公共表(跨服务使用,2张表)
```sql
-- pub_file — 文件记录(新增)
CREATE TABLE pub_file (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
file_name VARCHAR(255) NOT NULL COMMENT '原始文件名',
file_url VARCHAR(1000) NOT NULL COMMENT '文件访问URL',
file_path VARCHAR(1000) NOT NULL COMMENT '文件存储路径',
file_size BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(字节)',
file_type VARCHAR(100) DEFAULT NULL COMMENT '文件类型',
storage_type TINYINT NOT NULL DEFAULT 1 COMMENT '存储类型 1:本地 2:OSS 3:MinIO',
module VARCHAR(100) DEFAULT NULL COMMENT '所属模块',
biz_id BIGINT DEFAULT NULL COMMENT '业务ID',
status TINYINT NOT NULL DEFAULT 1,
deleted TINYINT NOT NULL DEFAULT 0,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_tenant (tenant_id),
INDEX idx_module_biz (module, biz_id)
) COMMENT='文件记录';
-- pub_message — 消息记录(新增)
CREATE TABLE pub_message (
id BIGINT NOT NULL,
tenant_id BIGINT NOT NULL DEFAULT 0,
msg_type TINYINT NOT NULL DEFAULT 1 COMMENT '消息类型 1:短信 2:邮件 3:站内信 4:App推送',
template_code VARCHAR(100) DEFAULT NULL COMMENT '模板编码',
sender VARCHAR(200) DEFAULT NULL COMMENT '发送方',
receiver VARCHAR(500) NOT NULL COMMENT '接收方',
subject VARCHAR(500) DEFAULT NULL COMMENT '主题',
content TEXT NOT NULL COMMENT '内容',
params JSON DEFAULT NULL COMMENT '模板参数(JSON',
send_status TINYINT NOT NULL DEFAULT 0 COMMENT '发送状态 0:待发送 1:发送中 2:成功 3:失败',
retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数',
send_time DATETIME DEFAULT NULL COMMENT '发送时间',
result_msg VARCHAR(500) DEFAULT NULL COMMENT '发送结果',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
INDEX idx_tenant (tenant_id),
INDEX idx_status (send_status),
INDEX idx_created (created_at)
) COMMENT='消息记录';
```
---
## 七、表结构变更对照(当前 → 规划)
| 当前表名 | 规划表名 | 归属服务 | 变更类型 |
|---------|---------|---------|---------|
| rui_system_oauth2_client | auth_oauth2_client | rui-auth | 🔴 迁移 + 重构 |
| rui_system_tenant | sys_tenant | rui-service-system | 🟡 重构 |
| rui_user_credential | uc_user | rui-service-user | 🔴 合并拆分 |
| rui_user_info | uc_user_detail | rui-service-user | 🔴 合并拆分 |
| rui_user_level | uc_user_level | rui-service-user | 🟡 重构 |
| rui_user_level_log | uc_user_level_log | rui-service-user | 🟡 重构 |
| rui_user_role | uc_user_role | rui-service-user | 🟡 重构 |
| rui_system_menu | sys_menu | rui-service-system | 🟡 重构 |
| rui_system_role | sys_role | rui-service-system | 🟡 重构 |
| rui_system_role_menu | sys_role_menu | rui-service-system | 🟡 重构 |
| rui_system_dict | sys_dict_type + sys_dict_item | rui-service-system | 🔴 拆分 |
| rui_system_config | sys_config | rui-service-system | 🟡 重构 |
| — | sys_tenant_package | rui-service-system | 🟢 新增 |
| — | sys_dept | rui-service-system | 🟢 新增 |
| — | sys_post | rui-service-system | 🟢 新增 |
| — | uc_user_dept | rui-service-user | 🟢 新增 |
| — | uc_user_post | rui-service-user | 🟢 新增 |
| — | uc_user_permission | rui-service-user | 🟢 新增 |
| — | auth_login_log | rui-auth | 🟢 新增 |
| — | auth_oper_log | rui-auth | 🟢 新增 |
| — | gw_route | rui-gateway | 🟢 新增 |
| — | gw_rate_limit | rui-gateway | 🟢 新增 |
| — | pub_file | 公共 | 🟢 新增 |
| — | pub_message | 公共 | 🟢 新增 |
---
## 八、表前缀规则(新增)
| 前缀 | 归属服务 | 说明 |
|------|---------|------|
| `sys_` | rui-service-system | 系统基础表(租户/部门/岗位/菜单/角色/字典/配置) |
| `uc_` | rui-service-user | 用户中心表(用户/详情/等级/角色/部门/岗位关联) |
| `auth_` | rui-auth | 认证授权表(OAuth2客户端/登录日志/操作日志) |
| `gw_` | rui-gateway | 网关表(路由/限流) |
| `pub_` | 公共 | 跨服务公共表(文件/消息) |
| `biz_` | 业务服务 | 业务应用模块表(如 biz_order, biz_pay |
---
## 九、Entity 设计规则(新增)
### 9.1 必须继承 BaseEntity
```java
@Data
@EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
// 业务字段
}
```
### 9.2 @TableName 命名规则
```java
// ✅ 正确:不包含前缀,与全局配置 table-prefix 配合
@TableName(value = "user", keepGlobalPrefix = true)
// 实际表名: rui_user(当前)或 uc_user(建议调整后)
// ❌ 错误:硬编码前缀,或前缀与配置不一致
@TableName(value = "auth_user", keepGlobalPrefix = true)
// 实际表名: rui_auth_user(与 SQL 不一致)
```
### 9.3 主键策略统一
```java
// ✅ 统一使用 ASSIGN_ID(雪花算法)
@TableId(type = IdType.ASSIGN_ID)
private Long id;
// ❌ 不要混用 AUTO
@TableId(type = IdType.AUTO) // 错误!
```
---
> **文档版本**: v1.0
> **创建日期**: 2026-05-28
> **适用范围**: spring-ai 项目数据库设计
> **状态**: 仅规划,未实施
@@ -0,0 +1,388 @@
# AI 开发环境配置手册
> **适用对象**: 业务模块开发者(支付、收银台等)
> **版本**: v1.0
> **更新日期**: 2026-06-03
---
## 目录
1. [概述](#一概述)
2. [环境准备](#二环境准备)
3. [仓库克隆与配置](#三仓库克隆与配置)
4. [GitNexus 索引配置](#四gitnexus-索引配置)
5. [AI 开发工作流](#五ai-开发工作流)
6. [常见问题](#六常见问题)
---
## 一、概述
### 1.1 背景
项目已拆分为独立仓库:
| 仓库 | 地址 | 用途 | 负责人 |
|------|------|------|--------|
| `rui-framework` | `git@gitee.com:pigeon/rui-framework.git` | 框架(backend | 框架维护者 |
| `rui-payment` | `git@gitee.com:pigeon/rui-payment.git` | 支付模块 | 员工A |
| `rui-cashier` | `git@gitee.com:pigeon/rui-cashier.git` | 收银台模块 | 员工B |
业务开发者**只需要**维护自己的业务仓库,框架依赖通过 Maven 仓库自动下载。
### 1.2 为什么需要本地 clone 框架仓库?
AIOpenCode)基于 GitNexus 知识图谱工作:
- **业务代码索引**:理解业务逻辑、生成业务代码
- **框架代码索引**:理解框架 API(如 `AuthUtil``BizException`
框架代码以 **jar 包** 形式通过 Maven 引入,AI 无法直接解析 jar 中的 class 文件。因此需要在本地 clone 框架仓库作为**只读参考**,供 AI 索引和查询。
---
## 二、环境准备
### 2.1 基础工具
| 工具 | 最低版本 | 验证命令 | 说明 |
|------|---------|---------|------|
| JDK | 21 | `java -version` | 必须 |
| Maven | 3.9 | `mvn -version` | 必须 |
| Git | 2.40 | `git --version` | 必须 |
| Node.js | 18 | `node --version` | GitNexus 需要 |
### 2.2 配置 Maven 仓库
确保 `~/.m2/settings.xml` 已配置公司 Nexus 仓库认证(向运维获取):
```xml
<settings>
<servers>
<server>
<id>releases</id>
<username>your-username</username>
<password>your-password</password>
</server>
<server>
<id>snapshots</id>
<username>your-username</username>
<password>your-password</password>
</server>
</servers>
</settings>
```
> **注意**:框架 `rui-parent` 和 `rui-common-*` 已发布到 Nexus,首次编译时会自动下载。
---
## 三、仓库克隆与配置
### 3.1 目录结构
```
~/work/
├── rui-framework/ # 框架仓库(只读参考)
│ ├── backend/
│ └── .gitnexus/ # 框架代码索引
└── rui-payment/ # 业务仓库(主要工作区)
├── pom.xml
├── rui-payment-api/
└── .gitnexus/ # 业务代码索引
```
### 3.2 克隆仓库
**步骤1:克隆框架仓库(所有员工都需要)**
```bash
git clone git@gitee.com:pigeon/rui-framework.git ~/work/rui-framework
```
**步骤2:克隆业务仓库**
```bash
# 员工A(支付)
git clone git@gitee.com:pigeon/rui-payment.git ~/work/rui-payment
# 员工B(收银台)
git clone git@gitee.com:pigeon/rui-cashier.git ~/work/rui-cashier
```
### 3.3 验证编译
```bash
cd ~/work/rui-payment
mvn clean compile -DskipTests
```
预期输出:`BUILD SUCCESS`
如果报错找不到 `rui-parent`,联系框架维护者确认已发布到 Nexus。
---
## 四、GitNexus 索引配置
### 4.1 什么是 GitNexus
GitNexus 是 AI 的代码智能引擎,通过索引代码库构建知识图谱,帮助 AI:
- 理解代码结构(类、方法、调用关系)
- 分析修改影响(改 A 会波及 B、C)
- 安全导航(不重命名、不遗漏引用)
### 4.2 索引框架仓库
```bash
cd ~/work/rui-framework
npx gitnexus analyze
```
预期输出:
```
Repository indexed successfully
7,504 nodes | 15,350 edges | 268 clusters | 300 flows
```
### 4.3 索引业务仓库
```bash
cd ~/work/rui-payment
npx gitnexus analyze
```
预期输出:
```
Repository indexed successfully
1,601 nodes | 3,921 edges | 63 clusters | 131 flows
```
### 4.4 验证索引
```bash
npx gitnexus list
```
预期输出:
```
Indexed Repositories (2)
spring-ai
Path: ~/work/rui-framework
Stats: 7504 symbols, 15350 edges
rui-payment
Path: ~/work/rui-payment
Stats: 1601 symbols, 3921 edges
```
### 4.5 索引维护
| 场景 | 操作 |
|------|------|
| 业务代码修改后 | `cd ~/work/rui-payment && npx gitnexus analyze` |
| 框架升级后 | `cd ~/work/rui-framework && git pull && npx gitnexus analyze` |
| 索引损坏 | `npx gitnexus clean && npx gitnexus analyze` |
| 检查状态 | `npx gitnexus status` |
---
## 五、AI 开发工作流
### 5.1 启动 AI
在**业务仓库**目录下启动 IDE/OpenCode
```bash
cd ~/work/rui-payment
# 启动 IDE 或 OpenCode
```
AI 默认使用当前目录的索引(`rui-payment`)。
### 5.2 开发业务代码(默认模式)
AI 自动基于 `rui-payment` 索引工作:
```
用户:帮我写一个订单查询接口
AI:基于 rui-payment 索引分析...
生成 OrderController、OrderService、OrderMapper
```
### 5.3 查询框架 API(跨仓库模式)
当需要理解框架源码时,在对话中指定 `repo` 参数:
**查询类定义:**
```
gitnexus_context({
name: "AuthUtil",
repo: "spring-ai"
})
```
**搜索功能实现:**
```
gitnexus_query({
query: "分布式锁 Redisson",
repo: "spring-ai"
})
```
**分析修改影响:**
```
gitnexus_impact({
target: "BaseController",
repo: "spring-ai",
direction: "upstream"
})
```
**查看执行流程:**
```
gitnexus_query({
query: "用户登录认证流程",
repo: "spring-ai"
})
```
### 5.4 常用查询对照表
| 我想查... | 命令 | 仓库 |
|-----------|------|------|
| `AuthUtil.getUserId()` 怎么用 | `gitnexus_context({name:"AuthUtil", repo:"spring-ai"})` | framework |
| 业务异常怎么抛 | `gitnexus_query({query:"BizException", repo:"spring-ai"})` | framework |
| 分布式锁怎么加 | `gitnexus_query({query:"Redisson分布式锁", repo:"spring-ai"})` | framework |
| 支付订单状态流转 | `gitnexus_query({query:"订单状态", repo:"rui-payment"})` | payment |
| 修改订单会影响哪里 | `gitnexus_impact({target:"OrderService", direction:"upstream"})` | payment |
| 收银台缓存策略 | `gitnexus_query({query:"缓存 CacheKeys", repo:"rui-cashier"})` | cashier |
### 5.5 工作流示例
**场景:开发一个支付退款接口**
```
1. 员工A在 ~/work/rui-payment 中启动 AI
2. 员工:帮我写退款接口
AI基于 rui-payment 索引分析业务代码...
3. 员工:退款时需要校验用户权限吗?
AI:建议调用 AuthUtil.getUserId()...
4. 员工:AuthUtil 有哪些方法?
【AI执行】gitnexus_context({name:"AuthUtil", repo:"spring-ai"})
AIAuthUtil 提供 getUserId()、getTenantId()、getUser()...
5. 员工:退款金额用 BigDecimal 吗?
【AI执行】gitnexus_query({query:"金额计算 BigDecimal", repo:"spring-ai"})
AI:框架规范要求金额使用 DECIMAL(19,4)Java 对应 BigDecimal...
6. AI 生成完整代码
```
---
## 六、常见问题
### Q1AI 提示 "Index is stale"
**原因**:代码已修改但索引未更新。
**解决**
```bash
cd ~/work/你的仓库
npx gitnexus analyze
```
### Q2:查询框架代码时提示 "Repo not found"
**原因**:框架仓库未索引或索引名称不对。
**解决**
```bash
# 查看所有索引
npx gitnexus list
# 确认 spring-ai 存在
# 如果不存在,重新索引框架仓库
cd ~/work/rui-framework
npx gitnexus analyze
```
### Q3:编译时找不到 rui-parent
**原因**:框架未发布到 Nexus,或 Maven 仓库配置错误。
**解决**
1. 确认 `~/.m2/settings.xml` 已配置 Nexus 认证
2. 联系框架维护者确认已执行 `mvn clean deploy`
3. 临时方案:本地 clone framework 后执行 `mvn clean install -DskipTests`
### Q4:框架升级后 AI 给出的 API 已过时
**原因**:框架索引未更新。
**解决**
```bash
cd ~/work/rui-framework
git pull origin main
npx gitnexus analyze
```
### Q5:发现框架代码有 Bug
**禁止**:直接修改 `~/work/rui-framework` 代码。
**正确做法**
1. 在业务仓库记录:`docs/框架问题记录.md`
2. 通知框架维护者
3. 等待框架修复并发布新版本
### Q6:AI 回答中引用了不存在的框架类
**原因**:业务仓库依赖的框架版本与本地索引的框架版本不一致。
**解决**
1. 检查 `pom.xml` 中 parent version(如 `1.0.0`
2. 确认 `~/work/rui-framework` 的代码版本一致
3. 如果不一致,更新 framework clone 并重新索引
---
## 附录
### A. 各仓库索引名称
| 仓库路径 | 索引名称 | 说明 |
|----------|----------|------|
| `~/work/rui-framework` | `spring-ai` | 基于 pom.xml artifactId |
| `~/work/rui-payment` | `rui-payment` | 基于目录名 |
| `~/work/rui-cashier` | `rui-cashier` | 基于目录名 |
### B. 快速命令速查
```bash
# 克隆所有仓库(新员工一次性执行)
git clone git@gitee.com:pigeon/rui-framework.git ~/work/rui-framework
git clone git@gitee.com:pigeon/rui-payment.git ~/work/rui-payment
# 初始化索引(一次性执行)
cd ~/work/rui-framework && npx gitnexus analyze
cd ~/work/rui-payment && npx gitnexus analyze
# 日常开发
mvn clean install -DskipTests
# 更新索引
npx gitnexus analyze
```
### C. 相关文档
| 文档 | 路径 |
|------|------|
| GitNexus 使用指南 | `docs/gitnexus-guide.md` |
| 环境搭建指南 | `docs/environment-setup.md` |
| 项目规范 | `AGENTS.md` |
---
> **提示**:本手册随项目演进持续更新。如有问题,联系框架维护者或技术负责人。
+120
View File
@@ -0,0 +1,120 @@
# Nacos 配置管理规范
## 1. 配置分类
| 配置类型 | 文件位置 | 是否推送 Nacos | 说明 |
|---------|---------|--------------|------|
| **本地开发配置** | `config/application-dev.yml` | ❌ 不推送 | 本地环境专属,已加入 `.gitignore` |
| **Nacos 配置文件** | `docs/nacos/*.yaml` | ✅ **必须推送** | 所有服务的 Nacos 配置源文件 |
| **应用模板** | `docs/application-template.yml` | ❌ 不推送 | 新建模块的模板,不直接推送 |
## 2. 核心规则
### 规则 1:修改必须推送
**除 `config/application-dev.yml` 外,任何对 `docs/nacos/*.yaml` 的修改必须推送至 Nacos 服务器。**
> ⚠️ **禁止行为**:只修改本地文件不推送。这会导致:
> - 本地测试正常,线上环境异常
> - 多人协作时配置不同步
> - 生产环境使用旧配置,引发故障
### 规则 2:统一推送脚本
使用根目录 `push-nacos-config.sh` 统一推送:
```bash
# 推送所有配置(推荐,确保所有配置同步)
bash push-nacos-config.sh
# 只推送单个配置(快速修复时)
bash push-nacos-config.sh rui-common.yaml
```
### 规则 3:推送前检查清单
推送前请确认:
- [ ] 配置文件语法正确(YAML 格式)
- [ ] 敏感信息使用 `${}` 环境变量注入,不硬编码
- [ ] 端口配置与 `项目实施规范.md` 一致
- [ ] 修改内容已 git commit
### 规则 4:推送后验证
推送后必须验证配置是否生效:
```bash
# 1. 查看 Nacos 控制台确认配置已更新
# 2. 重启对应服务使配置生效
# 3. 检查服务日志确认配置加载成功
```
## 3. 配置文件说明
### 3.1 公共配置(rui-common.yaml
| 配置项 | 说明 | 示例 |
|--------|------|------|
| `spring.data.redis` | Redis 连接配置 | host、port、password |
| `spring.jackson` | JSON 序列化配置 | date-format、time-zone |
| `security.oauth2.ignore-urls` | 免认证 URL 白名单 | `/entry/**``/actuator/**` |
| `feign.providers` | Feign 服务名映射 | user、system、auth |
### 3.2 数据配置(rui-data.yaml
| 配置项 | 说明 |
|--------|------|
| `spring.datasource` | MySQL + Druid 连接池配置 |
| `mybatis-plus` | MyBatis Plus 全局配置 |
### 3.3 网关配置(rui-gateway.yaml
| 配置项 | 说明 |
|--------|------|
| `spring.cloud.gateway.routes` | 路由规则 |
| `grayscale` | 灰度发布规则 |
### 3.4 服务专属配置(rui-service-*.yaml
| 配置项 | 说明 |
|--------|------|
| `server.port` | 服务端口 |
## 4. 命名空间与分组
| 环境 | 命名空间 | Group |
|------|---------|-------|
| 开发环境 | `rui-dev` | `DEFAULT_GROUP` |
| 测试环境 | `rui-test` | `DEFAULT_GROUP` |
| 生产环境 | `rui-prod` | `DEFAULT_GROUP` |
> 推送脚本默认使用 `rui-dev`,生产环境推送需手动指定命名空间。
## 5. 常见问题
### Q1: 为什么修改了本地配置但服务没变化?
> 因为 Spring Cloud 应用启动时会从 Nacos 拉取配置,**本地修改不影响运行中的服务**。必须推送至 Nacos 后重启服务才能生效。
### Q2: 可以同时修改多个配置吗?
> 可以,但建议每次只修改一个配置文件,避免推送时出错难以排查。
### Q3: 推送失败怎么办?
> 检查以下几点:
> 1. Nacos 服务器是否可访问(`http://192.168.31.210:8848`
> 2. 用户名密码是否正确(默认 nacos/nacos
> 3. 命名空间是否存在(rui-dev)
> 4. YAML 格式是否正确(缩进、特殊字符等)
### Q4: 如何回滚配置?
> Nacos 控制台支持配置历史版本回滚,或手动将旧配置内容重新推送。
## 6. 最佳实践
1. **先修改本地文件****测试验证****git commit****推送 Nacos****重启服务**
2. 多人协作时,推送前先看一眼 Nacos 控制台的当前配置,避免覆盖他人修改
3. 生产环境配置修改建议先修改测试环境验证,再同步到生产
4. 定期备份 Nacos 配置(导出为文件存档)
@@ -0,0 +1,413 @@
# Resilience4j ThreadPoolBulkhead 租户上下文跨线程传播问题排查指南
> **适用场景**Spring Cloud + OpenFeign + Resilience4jThreadPoolBulkhead+ TransmittableThreadLocalTTL
---
## 1. 问题现象
### 1.1 典型日志特征
```
# HTTP 线程正确设置了租户上下文
[nio-9301-exec-3] GlobalContextFilter : 租户上下文已设置: tenantId=5
# Feign 调用线程(线程池线程)却读取到错误的租户 ID
[pool-5-thread-1] OAuthRequestInterceptor : Feign 透传租户 ID: tenantId=4
# 后续请求无论 X-Tenant-Id 是多少,线程池线程始终返回第一次的 tenantId=4
[nio-9301-exec-4] GlobalContextFilter : 租户上下文已设置: tenantId=51
[pool-5-thread-1] OAuthRequestInterceptor : Feign 透传租户 ID: tenantId=4 ← 仍然是 4
```
### 1.2 核心特征
| 现象 | 说明 |
|------|------|
| HTTP 线程上下文正确 | `TenantContextHolder.getTenantId()` 在 Controller/Filter 中返回正确值 |
| 线程池线程上下文错误 | Feign 拦截器或 Service 中读取到旧值或 `null` |
| 旧值具有"粘性" | 线程池线程复用后,始终残留第一次被创建时的上下文值 |
| 与请求头不一致 | 请求头 `X-Tenant-Id` 变化,但业务线程读取的值不变 |
---
## 2. 问题根因
### 2.1 架构背景
本项目使用以下技术栈:
- **租户上下文**`TenantContextHolder` 基于 `TransmittableThreadLocal`TTL)实现
- **服务间调用**OpenFeign + Spring Cloud LoadBalancer
- **熔断隔离**Spring Cloud Circuit Breaker + Resilience4j `ThreadPoolBulkhead`
- **线程池隔离目的**:限制并发数,防止故障扩散
### 2.2 线程切换链路(关键!)
当 Feign 调用触发 Circuit Breaker + ThreadPoolBulkhead 时,一次请求会经历 **三层线程**
```
┌─────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ HTTP 线程 │ │ ThreadPoolBulkhead 线程 │ │ CircuitBreakerFactory │
│ [nio-9301-exec] │ ──▶ │ [bulkhead-xxx-thread] │ ──▶ │ ExecutorService 线程 │
│ │ │ │ │ [pool-N-thread-M] │
└─────────────────┘ └─────────────────────────┘ └─────────────────────────┘
│ │ │
│ ① TTL 自动透传 │ ② ContextPropagator 恢复 │ ③ ??? 上下文丢失
│ (原生 ThreadLocal │ Resilience4j 官方机制) │
│ 不跨线程池) │ │
│ │ │
tenantId=5 tenantId=5 ✓ tenantId=4 ✗
```
### 2.3 为什么 ContextPropagator 不够?
Resilience4j 提供了 `ContextPropagator` 接口,官方设计目的是在 **ThreadPoolBulkhead 线程池** 内透传上下文:
- `retrieve()`:在调用方线程捕获上下文值
- `copy()`:在线程池线程恢复上下文值
- `clear()`:在线程池线程清理上下文值
**但 Spring Cloud Circuit Breaker 内部还有一层线程池!**
查看 `Resilience4JCircuitBreaker.run()` 源码:
```java
if (executorService != null) {
// ① 先把任务提交到工厂自己的 ExecutorServicenewCachedThreadPool
Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get);
// ② 再用 ThreadPoolBulkhead 包装 Future 等待逻辑
Callable<T> bulkheadCall = bulkheadProvider.decorateCallable(..., timeLimitedCall);
...
}
```
**执行流程变成了:**
1. HTTP 线程提交任务 → `ThreadPoolBulkhead` 线程池
2. `ThreadPoolBulkhead` 线程执行 `ContextPropagator.copy()` → 恢复 `tenantId=5`
3. `ThreadPoolBulkhead` 线程调用 `executorService.submit(toRun::get)` → 提交到 **第二个线程池**
4. `executorService` 线程(`pool-5-thread-1`)执行 Feign 调用
5. `executorService` 线程 **没有** 经过 `ContextPropagator` 恢复,其 `ThreadLocal``null`
6. 但由于 `TransmittableThreadLocal` 继承 `InheritableThreadLocal`,线程创建时可能继承了父线程的值,且 **永不清理**,导致旧值残留
### 2.4 为什么旧值有"粘性"
`Resilience4JCircuitBreakerFactory` 默认使用 `Executors.newCachedThreadPool()`
- 线程创建时继承父线程(`ThreadPoolBulkhead` 线程)的 `InheritableThreadLocal`
- 线程被缓存复用,永不销毁(空闲 60 秒)
- **没有人清理** `executorService` 线程的 `ThreadLocal`
- 因此该线程永远携带第一次被创建时的 `tenantId`
---
## 3. 排查思路(按优先级)
### Step 1:确认问题范围
检查日志中 Feign 调用所在的线程名:
```
# 如果是 ThreadPoolBulkhead 线程,命名类似:
[bulkhead-xxx-1]
# 如果是 Spring 默认线程池,命名类似:
[pool-5-thread-1]
```
如果看到 `[pool-N-thread-M]`,说明问题在 **第二层线程切换**
### Step 2:确认 ContextPropagator 是否生效
`TenantContextPropagator``retrieve()` / `copy()` / `clear()` 方法中加日志:
```java
@Override
public Supplier<Optional<TenantContextSnapshot>> retrieve() {
return () -> {
Long tenantId = TenantContextHolder.getTenantId();
log.info("[ContextPropagator] retrieve: tenantId={} in thread={}",
tenantId, Thread.currentThread().getName());
...
};
}
```
- 如果 `retrieve()` 日志不打印 → `ContextPropagator` 未被注册到 ThreadPoolBulkheadConfig
- 如果 `copy()` 打印的线程名是 `[pool-N-thread-M]``ContextPropagator` 被用在了错误的线程池上(本不应出现)
### Step 3:确认是否存在多层线程池
`OAuthRequestInterceptor` 中加日志:
```java
Long tenantId = TenantContextHolder.getTenantId();
log.info("[Feign] 当前线程={}, tenantId={}", Thread.currentThread().getName(), tenantId);
```
对比 HTTP 线程的 `tenantId` 和 Feign 线程的 `tenantId`
| HTTP 线程 | Feign 线程 | 结论 |
|-----------|-----------|------|
| 5 | 5 | 正常 |
| 5 | null | 上下文完全丢失 |
| 5 | 4 | 旧值残留(本文档描述的问题) |
| 5 | 51(上一次的值)| 线程复用且未清理 |
### Step 4:检查 ThreadPoolBulkheadConfig 配置
断点或日志打印 `ThreadPoolBulkheadConfig.getContextPropagator()`
```java
ThreadPoolBulkheadConfig config = threadPoolBulkheadRegistry.getDefaultConfig();
log.info("配置中的 ContextPropagator: {}", config.getContextPropagator());
```
- 如果为空列表 → 配置未生效
- 如果有 `TenantContextPropagator` → 配置已生效,但只能解决第一层线程切换
---
## 4. 完整修复方案
### 4.1 方案概览
需要 **三个层面的修复** 协同工作:
| 层面 | 修复目标 | 组件 |
|------|---------|------|
| 第一层 | HTTP 线程 → ThreadPoolBulkhead 线程 | `TenantContextPropagator` + `TenantContextThreadPoolBulkheadConfigCustomizer` |
| 第二层 | ThreadPoolBulkhead 线程 → ExecutorService 线程 | `TtlExecutors` + `TtlResilience4JCircuitBreakerFactoryCustomizer` |
| 注册层 | Feign 客户端被 Spring 正确扫描 | `META-INF/spring.factories` 注册 Feign 接口 |
### 4.2 第一层:ThreadPoolBulkhead 内上下文传播
**组件**`TenantContextPropagator`(已存在)+ `TenantContextThreadPoolBulkheadConfigCustomizer`(新增 BeanPostProcessor
**原理**
- Resilience4j `ThreadPoolBulkhead` 内部使用 `ContextPropagator.decorateSupplier()` 包装任务
- 在任务提交时 `retrieve()` 捕获上下文,在线程池线程执行前 `copy()` 恢复,执行后 `clear()` 清理
**实现要点**
- `TenantContextThreadPoolBulkheadConfigCustomizer` 实现 `BeanPostProcessor`
-`ThreadPoolBulkheadRegistry` 初始化后,通过反射修改其默认配置,注入 `TenantContextPropagator`
- 不能使用 `AbstractRegistry.addConfiguration("default", config)`,因为该方法禁止修改 `"default"`
- 必须直接修改内部的 `configurations` Map
```java
public class TenantContextThreadPoolBulkheadConfigCustomizer implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ThreadPoolBulkheadRegistry registry) {
injectContextPropagator(registry);
}
return bean;
}
@SuppressWarnings("unchecked")
private void injectContextPropagator(ThreadPoolBulkheadRegistry registry) {
try {
Field configurationsField = AbstractRegistry.class.getDeclaredField("configurations");
configurationsField.setAccessible(true);
Map<String, ThreadPoolBulkheadConfig> configurations =
(Map<String, ThreadPoolBulkheadConfig>) configurationsField.get(registry);
ThreadPoolBulkheadConfig defaultConfig = configurations.get("default");
if (defaultConfig == null || hasTenantPropagator(defaultConfig)) {
return;
}
ThreadPoolBulkheadConfig newDefaultConfig = ThreadPoolBulkheadConfig.from(defaultConfig)
.contextPropagator(TenantContextPropagator.class)
.build();
configurations.put("default", newDefaultConfig);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException("无法修改 ThreadPoolBulkheadRegistry 的默认配置", e);
}
}
}
```
**注册为 Spring Bean**(注意用 `static` 方法避免 BeanPostProcessor 警告):
```java
@Bean
@ConditionalOnMissingBean
public static TenantContextThreadPoolBulkheadConfigCustomizer tenantContextThreadPoolBulkheadConfigCustomizer() {
return new TenantContextThreadPoolBulkheadConfigCustomizer();
}
```
### 4.3 第二层:CircuitBreakerFactory ExecutorService 上下文传播
**组件**`TtlResilience4JCircuitBreakerFactoryCustomizer`(新增 Customizer
**原理**
- 使用 Alibaba `TtlExecutors` 包装 `ExecutorService`
- `TtlExecutors``submit()` 时自动捕获当前线程的 `TransmittableThreadLocal`
- 在目标线程执行前自动恢复,执行后自动清理
- 支持线程池复用场景,无旧值残留
**实现**
```java
public class TtlResilience4JCircuitBreakerFactoryCustomizer
implements Customizer<Resilience4JCircuitBreakerFactory> {
@Override
public void customize(Resilience4JCircuitBreakerFactory factory) {
factory.configureExecutorService(
TtlExecutors.getTtlExecutorService(Executors.newCachedThreadPool())
);
factory.configureGroupExecutorService(
group -> TtlExecutors.getTtlExecutorService(Executors.newCachedThreadPool())
);
}
}
```
**注册为 Spring Bean**
```java
@Bean
@ConditionalOnMissingBean
public static TtlResilience4JCircuitBreakerFactoryCustomizer ttlResilience4JCircuitBreakerFactoryCustomizer() {
return new TtlResilience4JCircuitBreakerFactoryCustomizer();
}
```
### 4.4 第三层:Feign 客户端注册
**组件**`META-INF/spring.factories`
**原理**
- 本项目使用自定义的 `CustomFeignClientsRegistrar` 注册 Feign 客户端
- `CustomFeignClientsRegistrar``SpringFactoriesLoader.loadFactoryNames(CloudFeignAutoConfiguration.class, classLoader)` 加载 Feign 接口类名
- 如果 Feign 接口未在 `spring.factories` 中注册,Spring 容器中不会出现该 Bean,导致 `NoSuchBeanDefinitionException`
**实现**
在定义 Feign 接口的模块(如 `rui-common-security`)新增 `META-INF/spring.factories`
```properties
com.rui.common.feign.CloudFeignAutoConfiguration=\
com.rui.common.security.feign.TokenManageFeign
```
### 4.5 模块依赖关系
确保 `rui-common-security` 添加 `rui-common-feign` 依赖:
```xml
<dependency>
<groupId>com.rui</groupId>
<artifactId>rui-common-feign</artifactId>
</dependency>
```
否则 `spring.factories` 中引用的 `CloudFeignAutoConfiguration` 类在编译期不可见。
---
## 5. 验证方法
### 5.1 本地验证
1. 启动 `rui-auth``rui-service-system`
2. 发送第一次请求:`X-Tenant-Id: 5`
3. 观察 Feign 调用日志,确认 `tenantId=5`
4. 发送第二次请求:`X-Tenant-Id: 51`
5. 观察 Feign 调用日志,确认 `tenantId=51`(不是 5
6. 发送第三次请求:`X-Tenant-Id: 3`
7. 观察 Feign 调用日志,确认 `tenantId=3`(不是 5 也不是 51
### 5.2 关键日志断言
```java
// 断言:HTTP 线程和 Feign 线程的 tenantId 必须一致
assertEquals("HTTP 线程和 Feign 线程的租户 ID 必须一致",
httpTenantId, feignTenantId);
// 断言:每次请求的 tenantId 必须不同(如果请求头不同)
assertNotEquals("线程池线程不应残留旧租户 ID",
previousTenantId, currentTenantId);
```
### 5.3 断点验证
在以下位置打断点,单步跟踪:
1. `TenantContextPropagator.retrieve()` — 确认每次请求捕获的值不同
2. `TenantContextPropagator.copy()` — 确认在线程池线程恢复的值正确
3. `TtlExecutors` 内部 — 确认 `executorService` 线程恢复的值正确
4. `OAuthRequestInterceptor` — 确认最终 Feign 调用时的值正确
---
## 6. 常见问题 FAQ
### Q1:为什么 YAML 中配置 `resilience4j.thread-pool-bulkhead.configs.default.contextPropagators` 不生效?
**A**:该配置依赖 Spring Boot `ConfigurationProperties` 绑定 `Class[]` 类型。虽然 `CommonThreadPoolBulkheadConfigurationProperties` 支持该属性,但:
1. Spring Cloud Circuit Breaker 动态创建的 Bulkhead 实例名不确定
2. `CompositeCustomizer` 按实例名精确匹配,不存在通配符机制
3. 因此编程式注入(`BeanPostProcessor`)更可靠
### Q2:只用 `TtlExecutors` 不用 `ContextPropagator` 可以吗?
**A**:不可以。`TtlExecutors` 只能透传标准 `ThreadPoolExecutor` 的任务提交。Resilience4j 的 `ThreadPoolBulkhead` 内部使用自己的 `ThreadPoolExecutor`,不经过 `TtlExecutors`。因此第一层切换(HTTP → ThreadPoolBulkhead)必须由 `ContextPropagator` 处理。
### Q3:为什么 `TenantContextHolder` 使用 `TransmittableThreadLocal` 而不是普通 `ThreadLocal`
**A**:因为项目中存在 `@Async` 异步任务、Feign 线程池切换等场景。`TransmittableThreadLocal` 配合 `TtlExecutors` 可以在线程池间自动透传上下文,而普通 `ThreadLocal` 只能在线程父子间继承(且对线程池无效)。
### Q4:如果以后引入其他线程池(如 `@Async`),是否也会遇到同样问题?
**A**:是的。任何使用线程池的地方,如果任务提交方线程有 `ThreadLocal` 上下文,而执行方线程需要读取该上下文,都必须使用以下方案之一:
-`TtlExecutors` 包装线程池(推荐)
- 手动在任务提交前捕获上下文,在任务执行前恢复(类似 `ContextPropagator` 原理)
- 使用 Project Reactor 的 `Context` + `Hooks.onEachOperator`(响应式场景)
**最佳实践**:所有业务线程池统一通过 `TtlExecutors` 包装。
### Q5:如果关闭 ThreadPoolBulkhead,改用 SemaphoreBulkhead,能否避免此问题?
**A**:可以。SemaphoreBulkhead 在同一线程内执行,不存在线程切换。但会牺牲线程隔离的故障保护能力。
配置方式:
```yaml
spring:
cloud:
circuitbreaker:
resilience4j:
enableSemaphoreDefaultBulkhead: true
```
---
## 7. 后续维护建议
1. **新增 Feign 客户端时**:务必在所在模块的 `META-INF/spring.factories` 中注册
2. **新增线程池时**:优先使用 `TtlExecutors.getTtlExecutorService()` 包装
3. **新增 ThreadLocal 上下文时**:考虑是否需要配套 `ContextPropagator`
4. **日志规范**:在上下文切换关键点(Filter、Interceptor、线程池任务)打印 `tenantId` + `threadName`,便于快速定位问题
5. **自动化测试**:编写并发测试,模拟多租户同时请求,断言各线程的 `tenantId` 与请求头一致
---
## 8. 相关代码文件
| 文件 | 作用 |
|------|------|
| `rui-common-core/holder/TenantContextHolder.java` | 租户上下文持有者(TransmittableThreadLocal |
| `rui-common-security/feign/TokenManageFeign.java` | Feign 客户端示例 |
| `rui-common-security/feign/OAuthRequestInterceptor.java` | Feign 请求拦截器(透传租户 ID) |
| `rui-common-feign/propagator/TenantContextPropagator.java` | Resilience4j ContextPropagator 实现 |
| `rui-common-feign/config/TenantContextThreadPoolBulkheadConfigCustomizer.java` | 注入 ContextPropagator 到 ThreadPoolBulkheadRegistry |
| `rui-common-feign/config/TtlResilience4JCircuitBreakerFactoryCustomizer.java` | 用 TtlExecutors 包装 CircuitBreakerFactory 线程池 |
| `rui-common-feign/CloudFeignAutoConfiguration.java` | 注册上述 Bean |
| `rui-common-security/META-INF/spring.factories` | 注册 Feign 客户端 |
+181
View File
@@ -0,0 +1,181 @@
# 环境搭建指南
> **适用范围**: 新加入的开发者
> **预计耗时**: 30-60 分钟
---
## 一、必要工具安装
### 1.1 JDK 21
```bash
# macOS (使用 Homebrew)
brew install openjdk@21
# 验证
java -version
# Expected: openjdk version "21"
```
### 1.2 Maven 3.9+
```bash
# macOS
brew install maven
# 验证
mvn -version
# Expected: Apache Maven 3.9.x
```
### 1.3 MySQL 8.0
```bash
# macOS
brew install mysql@8.0
brew services start mysql@8.0
# 验证
mysql --version
# Expected: mysql Ver 8.0.x
```
### 1.4 Node.js 18+ 和 pnpm
```bash
# macOS
brew install node@18
npm install -g pnpm
# 验证
node --version
pnpm --version
```
### 1.5 Git
```bash
# macOS
brew install git
# 验证
git --version
```
---
## 二、项目初始化
### 2.1 克隆项目
```bash
git clone <repository-url>
cd spring-ai
```
### 2.2 配置本地开发环境
```bash
# 创建本地配置文件
cp backend/config/application-dev.yml.example backend/config/application-dev.yml
# 编辑配置(使用你的数据库连接信息)
# vim backend/config/application-dev.yml
```
配置示例:
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/rui_platform?useUnicode=true&characterEncoding=utf8
username: root
password: your_password
```
### 2.3 初始化数据库
```bash
# 创建数据库
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS rui_platform CHARACTER SET utf8mb4;"
# 执行初始化脚本
mysql -u root -p rui_platform < docs/init-database.sql
```
### 2.4 编译项目
```bash
cd backend
mvn clean install -DskipTests
```
Expected: `BUILD SUCCESS`
---
## 三、IDE 配置
### 3.1 IntelliJ IDEA
1. 打开项目(选择 backend/pom.xml
2. 启用 Annotation Processing
- Settings → Build → Annotation Processors
- 勾选 "Enable annotation processing"
3. 配置代码风格:
- Settings → Editor → Code Style → Java
- Import Scheme → Project
### 3.2 VS Code(前端)
1. 安装推荐插件:
- ESLint
- Prettier
- Vue Language Features
2. 配置自动格式化:
```json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
```
---
## 四、验证清单
完成以上步骤后,请确认:
- [ ] `java -version` 显示 JDK 21
- [ ] `mvn -version` 显示 Maven 3.9+
- [ ] `mysql --version` 显示 MySQL 8.0+
- [ ] `backend/mvn clean install -DskipTests` 执行成功
- [ ] 数据库 `rui_platform` 已创建
- [ ] IntelliJ IDEA 已配置 Annotation Processing
---
## 五、常见问题
### Q1: Maven 编译失败
**可能原因**: JDK 版本不对
**解决**: 确认 `JAVA_HOME` 指向 JDK 21
```bash
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
```
### Q2: 数据库连接失败
**可能原因**: MySQL 未启动或配置错误
**解决**:
```bash
brew services start mysql
# 检查 application-dev.yml 中的连接信息
```
### Q3: Lombok 注解不生效
**可能原因**: Annotation Processing 未启用
**解决**: 按 3.1 节启用
+52
View File
@@ -0,0 +1,52 @@
# GitNexus — Code Intelligence 使用指南
> **项目索引**: spring-ai (2690 symbols, 5387 relationships, 218 execution flows)
## 基本概念
GitNexus 是一个代码智能工具,通过索引代码库构建知识图谱,帮助开发者理解代码、评估影响、安全导航。
## 核心原则
### Always Do
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
### Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## 资源速查
| Resource | Use for |
|----------|---------|
| `gitnexus://repo/spring-ai/context` | Codebase overview, check index freshness |
| `gitnexus://repo/spring-ai/clusters` | All functional areas |
| `gitnexus://repo/spring-ai/processes` | All execution flows |
| `gitnexus://repo/spring-ai/process/{name}` | Step-by-step execution trace |
## 技能参考
| 场景 | 技能文件 |
|------|---------|
| 架构理解 / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| 影响分析 / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Bug 追踪 / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| 重构 / "Rename this function" | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| 工具参考 | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| CLI 命令 | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
## 索引维护
如果 GitNexus 工具提示索引过期,执行:
```bash
npx gitnexus analyze
```
+322
View File
@@ -0,0 +1,322 @@
# OpenCode 多仓库操作指南
> **版本**: v1.0
> **创建日期**: 2026-06-04
> **适用**: rui 项目前后端分离开发团队
---
## 一、项目结构概览
rui 项目采用**多仓库**架构:
```
~/rhkj/
├── spring-ai/ # 后端仓库(Java/Spring
│ ├── backend/ # 基础框架
│ ├── app/ # 应用模块
│ └── docs/ # 文档
└── rui-frontend/ # 前端仓库(Vue/Node.js
├── admin-ui/ # 管理后台
├── cashier-mobile/ # 收银移动端
└── customer-mobile/ # 顾客端
```
**原则**:一个 OpenCode 会话只处理一个仓库。
---
## 二、启动 OpenCode 的正确姿势
### 2.1 后端开发(spring-ai
```bash
# 1. 进入后端目录
cd /Users/zhangsheng/rhkj/spring-ai
# 2. 启动 OpenCode(命令行方式)
opencode
# 3. 会话启动后,明确告知角色
```
**启动时输入**(粘贴到 OpenCode 对话框):
```
你现在进入【后端开发模式】。
工作目录:/Users/zhangsheng/rhkj/spring-ai
负责范围:backend/ 和 app/ 目录下的 Java 代码
技术栈:Spring Boot 4.x、Spring Cloud、MyBatis Plus、JDK 21
规则:
1. 只能修改 backend/ 和 app/ 下的代码
2. 发现前端需求时,提醒用户创建 Gitee Issue
3. 编码规范参考 docs/AGENTS.md
当前任务:【在这里描述你的具体任务】
```
---
### 2.2 前端开发(rui-frontend
```bash
# 1. 进入前端目录
cd /Users/zhangsheng/rhkj/rui-frontend
# 2. 启动 OpenCode
opencode
# 3. 会话启动后,明确告知角色
```
**启动时输入**(粘贴到 OpenCode 对话框):
```
你现在进入【前端开发模式】。
工作目录:/Users/zhangsheng/rhkj/rui-frontend
负责范围:admin-ui/、cashier-mobile/、customer-mobile/
技术栈:Vue 3、TypeScript、Element Plus、Vite、pnpm
规则:
1. 只能修改前端项目下的代码
2. 需要后端接口时,在 spring-ai 仓库创建 Gitee Issue
3. 编码规范参考 AGENTS.md
4. 使用 pnpm workspace 管理多项目
当前任务:【在这里描述你的具体任务,如:开发用户管理页面】
```
---
### 2.3 框架开发(仅修改 backend/
```
你现在进入【框架开发模式】。
工作目录:/Users/zhangsheng/rhkj/spring-ai
负责范围:仅 backend/ 目录
角色:基础框架维护者
规则:
1. 只能修改 backend/ 下的代码
2. 不修改任何 app/ 目录下的业务代码
3. 保持框架的通用性和向后兼容性
4. 修改公共接口时,评估对 app/ 的影响
当前任务:【描述框架任务】
```
---
## 三、切换工作流的正确方式
### ❌ 错误示范
在一个 OpenCode 会话中:
```
用户:帮我修改后端接口
AI:好的,已修改 backend/xxx.java
用户:再帮我改一下前端页面
AI:好的,已修改 admin-ui/xxx.vue ← 错误!上下文已污染
```
### ✅ 正确示范
**场景 1:同一仓库内切换任务**
如果任务相关(如修改后端接口 + 对应单元测试),可以在同一会话中完成。
如果任务不相关(如用户管理 + 订单管理),建议:
```
用户:/new
AI:已创建新会话
用户:【输入新的任务描述】
```
**场景 2:跨仓库协作**
```
# 后端仓库会话
用户:开发用户查询接口
AI:已完成,接口路径:GET /user/admin/list
# 需要前端对接时
用户:需要前端开发列表页面
AI:请创建新会话处理前端任务。
在 rui-frontend 目录启动 OpenCode,输入前端开发模式提示词。
接口文档:http://localhost:8080/doc.html#/user/list
# 用户切换到前端仓库,新建会话
用户:/new
【进入 rui-frontend 目录,启动新 OpenCode 会话】
用户:根据接口文档开发用户列表页面
AI:好的,开始开发前端页面...
```
---
## 四、跨仓库通信机制
### 4.1 前端需要后端接口
**前端会话**中:
```
我需要后端提供用户批量导入接口,参数是 Excel 文件
```
OpenCode 应该回答:
```
请在 spring-ai 仓库创建 Issue,使用模板:api_request.md
Issue 内容:
标题:[API-REQ] 用户模块需要批量导入接口
内容:...
创建后,在 backend/ 开发会话中处理该 Issue
```
### 4.2 后端接口完成通知前端
**后端会话**中:
```
接口已实现,Swagger 地址:/doc.html#/user/import
请通知前端团队进行对接
```
OpenCode 应该:
```
已在 Issue 中回复前端团队。
前端可以在 rui-frontend 仓库的新会话中进行对接开发。
```
---
## 五、目录访问限制检查
启动会话后,可以让 OpenCode 自我检查:
```
请确认你的工作范围:
1. 可以修改哪些目录?
2. 不能修改哪些目录?
3. 如果需要跨仓库协作,应该怎么做?
```
期望回答:
```
1. 可修改:backend/、app/(后端模式)或 admin-ui/(前端模式)
2. 不可修改:frontend/(后端模式)或 backend/(前端模式)
3. 跨仓库协作:通过 Gitee Issue 进行
```
---
## 六、常见问题
### Q1: 我不小心在错误目录启动了 OpenCode,怎么办?
**答**
1. 保存当前对话(如有重要信息)
2. 关闭当前 OpenCode 窗口
3. 切换到正确目录重新启动
4. 使用 `/new` 创建新会话
### Q2: 一个功能需要同时修改前后端,怎么操作?
**答**
1. **方法 A(推荐)**:先在一个仓库完成,提交后切换到另一个仓库
- 在 spring-ai 开发接口 → 提交 PR
- 在 rui-frontend 开发页面 → 提交 PR
2. **方法 B(并行)**:两个 OpenCode 窗口同时工作
- 窗口 1spring-ai 目录,开发后端
- 窗口 2rui-frontend 目录,开发前端
3. **不要**:在一个会话中同时修改两个仓库
### Q3: OpenCode 能记住跨仓库的上下文吗?
**答**:不能。每个 OpenCode 会话是独立的:
- 不同目录 = 不同上下文
- 即使同一个目录,`/new` 后也是全新上下文
- 需要人工传递关键信息(如接口文档链接)
### Q4: 怎么快速查看当前在哪个仓库?
**答**:在 OpenCode 中输入:
```
请告诉我当前工作目录和可修改范围
```
### Q5: 可以用同一个 OpenCode 窗口切换目录吗?
**答**:不建议。OpenCode 启动时会锁定工作目录。如果需要切换:
1. 关闭当前窗口
2. `cd` 到新目录
3. 重新启动 OpenCode
---
## 七、快捷键和命令速查
| 操作 | 命令 |
|------|------|
| 创建新会话 | `/new` |
| 查看当前目录 | `pwd` |
| 查看文件树 | `tree -L 2` |
| 查看 Git 状态 | `git status` |
| 切换分支 | `git checkout branch-name` |
---
## 八、最佳实践
1. **明确角色**:启动时明确告知 OpenCode 当前角色和范围
2. **单一职责**:一个会话只做一件事(一个功能/一个 Bug)
3. **及时提交**:完成一个功能后立即 `git commit`,不要积压
4. **Issue 驱动**:跨仓库需求通过 Issue 追踪,不要口头传递
5. **文档优先**:复杂功能先写设计文档,再编码
6. **定期 /new**:对话超过 20-30 轮后,新建会话保持上下文清晰
---
## 九、模板库
### 启动模板
保存以下模板,启动时直接粘贴:
**后端启动模板**
```markdown
你现在进入【后端开发模式】。
工作目录:/Users/zhangsheng/rhkj/spring-ai
技术栈:Spring Boot 4.x、JDK 21、MyBatis Plus
规则:只能修改 backend/ 和 app/ 目录
当前任务:【填写】
```
**前端启动模板**
```markdown
你现在进入【前端开发模式】。
工作目录:/Users/zhangsheng/rhkj/rui-frontend
技术栈:Vue 3、TypeScript、Element Plus、Vite
规则:只能修改前端项目目录
当前任务:【填写】
```
---
## 十、相关文档
- [跨团队协作规范](./cross-team-workflow.md)
- [后端项目规范](../AGENTS.md)
- [前端项目规范](../../rui-frontend/AGENTS.md)
- [Gitea 自建 Git 服务器](./self-hosted-git-server.md)
---
> **提示**:本文档是活文档,根据团队实践持续更新。如有建议请提交 PR。
@@ -0,0 +1,459 @@
# rui-common-core 使用手册
> **文件名**`rui-common-core使用手册.md`
> **存放位置**`docs/rui-common-core使用手册.md`
>
> **文档定位**:本文档是 `rui-common-core` 模块的**唯一权威参考**,记录所有工具类、注解、事件、DTO 的功能与用法。
>
> **使用规则**
> 1. **开发前先查本文档**:使用任何通用工具/注解/常量前,优先查阅本文档确认是否存在可用实现
> 2. **新增即更新**:向 `rui-common-core` 模块新增/修改任何类、方法时,**必须同步更新本文档**
> 3. **禁止重复造轮子**:本文档中已有的工具,禁止在业务模块中重新实现
---
## 1. 模块概述
`rui-common-core` 是睿核通用平台框架的**核心基础模块**,为所有上层模块提供通用的工具类、常量、异常、上下文持有器、注解、事件、DTO 等基础能力。
> **定位**:无业务依赖,无 Spring 依赖(除 SpringUtil、LoginEvent 外),可被任意模块引用。
---
## 2. 功能清单
### 2.1 上下文持有器(holder
| 类名 | 功能 | 说明 |
|------|------|------|
| `TenantContextHolder` | 租户上下文 | 线程隔离的租户 ID 存储,支持父子线程传递 |
| `LocaleContextHolder` | 本地化上下文 | 线程隔离的语言环境存储 |
### 2.2 工具类(util
| 类名 | 功能 | 依赖 |
|------|------|------|
| `ServletUtil` | Servlet 请求工具 | 获取 IP、参数、Header、请求体等 |
| `SpringUtil` | Spring 上下文工具 | 获取 Bean、配置、Environment、判断环境 |
| `JsonUtil` | JSON 工具 | 基于 Fastjson2,对象与 JSON 互转 |
| `DateUtil` | 日期时间工具 | 基于 JDK 8 java.time,格式化、解析、计算 |
| `IdWorker` | 雪花算法 ID 生成器 | 分布式唯一 ID,支持趋势递增 |
| `BeanUtil` | Bean 拷贝工具 | 基于 Spring BeanUtils,支持列表拷贝 |
| `EncryptUtil` | 加密工具 | MD5、SHA-256、AES、Base64 |
| `ValidateUtil` | 校验工具 | 手机号、邮箱、身份证、URL、密码等校验 |
| `StringUtil` | 字符串扩展工具 | 驼峰/下划线转换、脱敏、截取等 |
| `ThreadUtil` | 线程工具 | 线程池创建、优雅关闭、命名线程工厂 |
| `FileUtil` | 文件工具 | 读写、复制、移动、目录操作、大小格式化 |
| `UserNoGenerator` | 用户编号生成器 | 生成 U0001 格式编号,自动跳过保留靓号 |
### 2.3 异常(exception
| 类名 | 功能 |
|------|------|
| `BizException` | 业务异常,统一业务错误抛出 |
### 2.4 常量(constants
| 类名 | 功能 |
|------|------|
| `SecurityConstant` | 安全相关常量(Token 前缀、Header 名称等) |
### 2.5 模型(model
| 类名 | 功能 |
|------|------|
| `PageResult` | 统一分页结果封装 |
### 2.6 结果封装(result
| 类名 | 功能 |
|------|------|
| `Result` | 统一 API 返回结果(code、msg、data |
| `ResultCode` | 统一错误码枚举 |
### 2.7 注解(annotation
| 类名 | 功能 | 目标位置 |
|------|------|---------|
| `DataScope` | 数据权限注解,自动拼接数据范围 SQL | Service 方法 |
### 2.8 事件(event
| 类名 | 功能 | 说明 |
|------|------|------|
| `LoginEvent` | 登录事件 | 登录成功/失败时发布,供监听器记录日志 |
### 2.9 DTOdto
| 类名 | 功能 | 说明 |
|------|------|------|
| `OperLogDTO` | 操作日志 DTO | 跨服务传输操作日志数据 |
### 2.10 拦截器上下文(interceptor
| 类名 | 功能 | 说明 |
|------|------|------|
| `DataScopeContext` | 数据权限上下文 | ThreadLocal 存储数据范围信息,需手动清理 |
---
## 3. 工具类详细说明
### 3.1 IdWorker(雪花算法)
```java
// 生成唯一 ID
long id = IdWorker.nextIdLong();
String idStr = IdWorker.nextIdStr();
// 从 ID 中提取信息
long timestamp = IdWorker.extractTimestamp(id);
long workerId = IdWorker.extractWorkerId(id);
```
### 3.2 DateUtil(日期时间)
```java
// 获取当前时间
String now = DateUtil.now(); // 2024-01-01 12:00:00
String today = DateUtil.today(); // 2024-01-01
// 格式化与解析
String str = DateUtil.format(LocalDateTime.now());
LocalDateTime dt = DateUtil.parse("2024-01-01 12:00:00");
// 计算
LocalDateTime tomorrow = DateUtil.plusDays(dt, 1);
long days = DateUtil.betweenDays(start, end);
```
### 3.3 JsonUtilJSON 处理)
```java
// 对象转 JSON
String json = JsonUtil.toJsonString(user);
// JSON 转对象
User user = JsonUtil.parseObject(json, User.class);
List<User> list = JsonUtil.parseList(json, User.class);
Map<String, Object> map = JsonUtil.parseMap(json);
```
### 3.4 EncryptUtil(加密)
```java
// MD5
String md5 = EncryptUtil.md5("password");
// SHA-256
String sha256 = EncryptUtil.sha256("password");
// AES 对称加密
String encrypt = EncryptUtil.aesEncrypt("content", "1234567890123456");
String decrypt = EncryptUtil.aesDecrypt(encrypt, "1234567890123456");
// Base64
String base64 = EncryptUtil.base64Encode("content");
```
### 3.5 ValidateUtil(校验)
```java
// 常用校验
boolean isMobile = ValidateUtil.isMobile("13800138000");
boolean isEmail = ValidateUtil.isEmail("test@example.com");
boolean isIdCard = ValidateUtil.isIdCard("110101199001011234");
boolean isUrl = ValidateUtil.isUrl("https://example.com");
boolean isIpv4 = ValidateUtil.isIpv4("192.168.1.1");
// 密码强度
boolean validPwd = ValidateUtil.isValidPassword("Abc123456");
```
### 3.6 SpringUtilSpring 上下文)
```java
// 获取 Bean
UserService service = SpringUtil.getBean(UserService.class);
// 获取配置
String value = SpringUtil.getProperty("server.port");
// 判断环境
boolean isDev = SpringUtil.isDev();
boolean isProd = SpringUtil.isProd();
```
### 3.7 BeanUtilBean 拷贝)
```java
// 对象拷贝
UserDTO dto = BeanUtil.copyProperties(user, UserDTO.class);
// 列表拷贝
List<UserDTO> dtoList = BeanUtil.copyList(userList, UserDTO.class);
// 忽略 null 值拷贝(用于更新)
BeanUtil.copyNonNullProperties(source, target);
```
### 3.8 StringUtil(字符串扩展)
```java
// 命名转换
String camel = StringUtil.underlineToCamel("user_name"); // userName
String underline = StringUtil.camelToUnderline("userName"); // user_name
// 脱敏
String mobile = StringUtil.desensitizeMobile("13800138000"); // 138****8000
String email = StringUtil.desensitizeEmail("test@example.com"); // t***@example.com
// 其他
String truncated = StringUtil.truncate("很长的字符串", 5); // 很长的字...
```
### 3.9 ThreadUtil(线程池)
```java
// 创建线程池
ExecutorService pool = ThreadUtil.newFixedPool(4, "my-pool");
ScheduledExecutorService scheduled = ThreadUtil.newScheduledPool(2, "schedule");
// 推荐:自定义线程池
ThreadPoolExecutor executor = ThreadUtil.newThreadPool(
4, 8, 60, 100, "business"
);
// 优雅关闭
ThreadUtil.shutdown(pool, 30);
```
### 3.10 FileUtil(文件操作)
```java
// 读写
String content = FileUtil.readString("/path/file.txt");
FileUtil.writeString("/path/file.txt", "content");
FileUtil.appendString("/path/file.txt", "append");
// 目录
FileUtil.createDir("/path/dir");
FileUtil.delete("/path/file.txt");
// 信息
boolean exists = FileUtil.exists("/path/file.txt");
long size = FileUtil.size("/path/file.txt");
String sizeStr = FileUtil.formatSize(1024 * 1024); // 1.00 MB
```
### 3.11 UserNoGenerator(用户编号生成器)
```java
// 生成格式化的用户编号(U0001、U0002...
String userNo = UserNoGenerator.format(1); // U0001
// 判断是否为保留靓号(豹子号、连号、含666/888/999
boolean reserved = UserNoGenerator.isReserved("U1111"); // true
// 生成下一个可用编号(自动跳过保留号)
String next = UserNoGenerator.generate(1); // U0001(如果 U0001 是靓号则自动跳过)
```
---
## 4. 注解使用说明
### 4.1 @DataScope(数据权限)
标记在 Service 方法上,由 MyBatis Plus 拦截器自动拼接数据范围 SQL。
```java
@DataScope(deptField = "dept_id", userField = "create_by")
public List<User> list() {
return baseMapper.selectList();
}
```
**参数说明**
- `deptField`:部门字段名,默认 `"dept_id"`
- `userField`:用户字段名,默认 `"create_by"`
**数据范围类型**(由 `DataScopeContext` 设置):
- `1`:全部数据
- `2`:本部门数据
- `3`:本部门及子部门数据
- `4`:仅本人数据
- `5`:自定义数据
---
## 5. 事件使用说明
### 5.1 LoginEvent(登录事件)
登录成功或失败时发布,供其他模块监听记录日志。
```java
// 发布事件
applicationEventPublisher.publishEvent(
new LoginEvent(this, userId, username, 1, clientId, ip,
location, browser, os, 1, "登录成功")
);
// 监听事件
@Component
public class LoginEventListener {
@EventListener
public void onLogin(LoginEvent event) {
// 记录登录日志
}
}
```
---
## 6. DTO 使用说明
### 6.1 OperLogDTO(操作日志 DTO
用于跨服务传输操作日志数据。
```java
OperLogDTO log = new OperLogDTO();
log.setTitle("用户管理");
log.setOperType(2); // 修改
log.setOperTypeName("修改用户");
log.setRequestUrl("/user/admin/user");
log.setRequestMethod("PUT");
log.setUserId(userId);
log.setStatus(1); // 成功
```
---
## 7. 上下文使用说明
### 7.1 TenantContextHolder(租户上下文)
```java
// 设置租户ID
TenantContextHolder.setTenantId(100L);
// 获取租户ID
Long tenantId = TenantContextHolder.getTenantId();
// 清理(必须在线程结束时调用)
TenantContextHolder.clear();
```
### 7.2 DataScopeContext(数据权限上下文)
```java
// 设置数据范围
DataScopeContext.setDataScope(2); // 本部门
DataScopeContext.setUserId(userId);
DataScopeContext.setDeptId(deptId);
// 获取数据范围
Integer scope = DataScopeContext.getDataScope();
// 清理(必须在线程结束时调用,防止内存泄漏)
DataScopeContext.clear();
```
---
## 8. 使用方式
### 8.1 Maven 依赖
```xml
<dependency>
<groupId>com.rui</groupId>
<artifactId>rui-common-core</artifactId>
<version>${revision}</version>
</dependency>
```
### 8.2 在业务模块中使用
```java
import com.rui.common.core.util.*;
@Service
public class UserService {
public void createUser(User user) {
// 生成唯一 ID
user.setId(IdWorker.nextIdLong());
// 生成用户编号
user.setUserNo(UserNoGenerator.generate(seq));
// 日期处理
user.setCreatedAt(DateUtil.nowDateTime());
// 密码加密
user.setPassword(EncryptUtil.md5(user.getPassword()));
// 保存...
}
}
```
---
## 9. 规范说明
### 9.1 工具类设计原则
1. **无状态**:所有工具类均为无状态静态方法,线程安全
2. **不可实例化**:通过 private 构造器防止实例化
3. **异常处理**:内部捕获并转换为 RuntimeException,避免污染业务代码
4. **空安全**:所有方法对 null 参数有处理,避免 NPE
### 9.2 新增规范
如需向本模块新增类,请遵循:
1. **包名规范**
- 工具类:`com.rui.common.core.util`
- 注解:`com.rui.common.core.annotation`
- 事件:`com.rui.common.core.event`
- DTO`com.rui.common.core.dto`
- 上下文:`com.rui.common.core.holder` / `interceptor`
- 常量:`com.rui.common.core.constants`
- 异常:`com.rui.common.core.exception`
2. **命名规范**
- 工具类:以 `Util` 结尾,如 `XxxUtil`
- 注解:以功能命名,如 `@DataScope`
- 事件:以 `Event` 结尾,如 `XxxEvent`
- DTO:以 `DTO` 结尾,如 `XxxDTO`
3. **方法规范**:均为 public static(工具类)
4. **文档规范**:类注释和方法注释使用中文
5. **测试规范**:建议补充单元测试
6. **文档同步**:**新增/修改后必须同步更新本文档**
---
## 10. 文档维护说明
### 10.1 何时更新本文档
| 场景 | 操作 |
|------|------|
| 新增工具类/注解/事件/DTO | 在功能清单中添加条目,在详细说明中添加使用示例 |
| 修改现有类的方法签名 | 同步更新对应详细说明中的代码示例 |
| 删除类或方法 | 从文档中移除对应条目,并在版本历史中注明 |
| 发现文档与代码不一致 | 以代码为准,修正文档 |
### 10.2 版本历史
| 日期 | 版本 | 变更内容 |
|------|------|---------|
| 2024-01 | 1.0 | 初始版本,包含基础工具类 |
| 2024-06 | 1.1 | 新增 IdWorker、BeanUtil、ThreadUtil |
| 2026-05 | 1.2 | 补充完整工具类集合,新增文档 |
| 2026-06 | 1.3 | 新增 UserNoGenerator、DataScope 注解、LoginEvent、OperLogDTO、DataScopeContext;补充文档使用说明 |
+480
View File
@@ -0,0 +1,480 @@
# 自建 Git 服务器方案:Gitea
> **版本**: v1.0
> **创建日期**: 2026-06-04
> **适用场景**: 替代 Gitee,实现完整的 Git + CI/CD 私有化部署
---
## 一、为什么选择 Gitea
### 1.1 对比分析
| 特性 | Gitee | GitLab CE | Gitea | Gogs |
|------|-------|-----------|-------|------|
| **开源免费** | 部分功能收费 | ✅ 社区版免费 | ✅ 完全开源 | ✅ 完全开源 |
| **资源占用** | 云端,无需部署 | 4GB+ 内存 | **128MB 内存** | 64MB 内存 |
| **CI/CD** | 收费 | ✅ 内置 | ✅ Gitea Actions | ❌ 需搭配 Drone |
| **中文支持** | ✅ 原生 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
| **Issue 模板** | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
| **代码审查** | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
| **部署难度** | 无需部署 | 复杂 | **简单** | 简单 |
| **GitHub Actions 兼容** | ❌ | ❌ | ✅ 兼容 | ❌ |
### 1.2 Gitea 优势
-**轻量级**:单二进制文件,内置 SQLite,无需额外数据库
-**低资源**:128MB 内存即可运行,适合低配服务器
-**CI/CD 内置**Gitea Actions 完全兼容 GitHub Actions 语法
-**易迁移**:支持从 Gitee/GitHub 导入仓库
-**Webhook 丰富**:支持钉钉、企业微信、Slack 等通知
-**权限管理**:组织、团队、仓库级权限控制
---
## 二、部署方案
### 方案 ADocker 部署(推荐)
适合:有 Docker 环境的服务器
```yaml
# docker-compose.yml
version: "3"
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__server__DOMAIN=git.vifo.cc
- GITEA__server__ROOT_URL=https://git.vifo.cc
- GITEA__server__SSH_DOMAIN=git.vifo.cc
- GITEA__actions__ENABLED=true
restart: always
networks:
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
# 可选:Gitea Actions Runner(执行 CI/CD 任务)
runner:
image: gitea/act_runner:latest
container_name: gitea-runner
environment:
- GITEA_INSTANCE_URL=https://git.vifo.cc
- GITEA_RUNNER_REGISTRATION_TOKEN=your-token
- GITEA_RUNNER_NAME=default-runner
restart: always
networks:
- gitea
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./runner:/data
depends_on:
- gitea
networks:
gitea:
external: false
```
**启动命令**
```bash
# 创建目录
mkdir -p ~/gitea && cd ~/gitea
# 创建 docker-compose.yml(粘贴上方内容)
nano docker-compose.yml
# 启动
docker-compose up -d
# 查看日志
docker-compose logs -f gitea
```
**初始化配置**
1. 访问 `http://服务器IP:3000`
2. 填写管理员账号(首次访问会自动跳转到安装页面)
3. 基础 URL 设置为你的域名(如 `https://git.vifo.cc`
4. 数据库选择 SQLite(轻量级)或 MySQL(生产环境)
---
### 方案 B:二进制部署
适合:没有 Docker 环境的裸机
```bash
# 1. 下载二进制(Linux AMD64
wget -O gitea https://dl.gitea.com/gitea/latest/gitea-latest-linux-amd64
chmod +x gitea
# 2. 创建用户(不要使用 root 运行)
sudo useradd -r -m -s /bin/bash git
# 3. 创建工作目录
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea/
sudo chmod -R 750 /var/lib/gitea/
# 4. 移动到系统目录
sudo mv gitea /usr/local/bin/
# 5. 创建 Systemd 服务
sudo tee /etc/systemd/system/gitea.service > /dev/null <<EOF
[Unit]
Description=Gitea
After=network.target
[Service]
User=git
Group=git
WorkingDirectory=/var/lib/gitea
ExecStart=/usr/local/bin/gitea web --config /var/lib/gitea/custom/conf/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
EOF
# 6. 启动服务
sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea
# 7. 查看状态
sudo systemctl status gitea
```
---
### 方案 C:一键安装脚本(最简单)
```bash
# 下载官方安装脚本
curl -s https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/install.sh | bash
# 或者使用 snapUbuntu/Debian
sudo snap install gitea
```
---
## 三、Nginx 反向代理 + HTTPS
### 3.1 Nginx 配置
```nginx
# /etc/nginx/conf.d/gitea.conf
server {
listen 80;
server_name git.vifo.cc;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name git.vifo.cc;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
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;
}
}
```
### 3.2 申请免费 SSL 证书(Let's Encrypt
```bash
# 安装 certbot
sudo apt install certbot python3-certbot-nginx
# 申请证书
sudo certbot --nginx -d git.vifo.cc
# 自动续期测试
sudo certbot renew --dry-run
```
---
## 四、从 Gitee 迁移到 Gitea
### 4.1 迁移单个仓库
```bash
# 1. 在 Gitea 创建空仓库(如 rui-frontend
# 2. 本地克隆 Gitee 仓库
git clone --mirror https://gitee.com/rui/rui-frontend.git
# 3. 推送到 Gitea
cd rui-frontend.git
git remote add gitea https://git.vifo.cc/rui/rui-frontend.git
git push gitea --mirror
```
### 4.2 批量迁移(所有仓库)
```bash
#!/bin/bash
# migrate.sh
GITEA_URL="https://git.vifo.cc"
GITEA_TOKEN="your-token"
GITEA_ORG="rui"
REPOS=("spring-ai" "rui-frontend" "rui-payment")
for repo in "${REPOS[@]}"; do
echo "迁移: $repo"
# 在 Gitea 创建仓库
curl -X POST "$GITEA_URL/api/v1/orgs/$GITEA_ORG/repos" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"$repo\", \"private\": true}"
# 克隆并推送
git clone --mirror "https://gitee.com/rui/$repo.git" "/tmp/$repo"
cd "/tmp/$repo"
git remote add gitea "$GITEA_URL/$GITEA_ORG/$repo.git"
git push gitea --mirror
cd ..
done
```
---
## 五、配置 CI/CDGitea Actions
### 5.1 启用 Actions
`app.ini` 中配置:
```ini
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = github
```
### 5.2 注册 Runner
```bash
# 获取注册令牌(在 Gitea 管理后台 → Actions → Runners → 创建新 Runner
# 然后执行:
docker exec -it gitea-runner act_runner register \
--instance https://git.vifo.cc \
--token YOUR_TOKEN \
--name default-runner \
--labels ubuntu-latest:docker://node:18
```
### 5.3 创建前端 CI/CD 工作流
```yaml
# rui-frontend/.gitea/workflows/build.yml
name: Build and Deploy
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Build admin-ui
run: pnpm build:admin
- name: Deploy to server
if: github.ref == 'refs/heads/main'
run: |
# 部署脚本
echo "部署到生产环境"
```
### 5.4 创建后端 CI/CD 工作流
```yaml
# spring-ai/.gitea/workflows/build.yml
name: Build and Test
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: cd backend && mvn clean install -DskipTests
- name: Run tests
run: cd backend && mvn test
```
---
## 六、Gitea 常用配置
### 6.1 配置 Issue 模板
与 Gitee 类似,创建 `.gitea/issue_templates/` 目录:
```
.gitea/issue_templates/
├── api_request.md
├── framework_bug.md
└── cross_team_task.md
```
**注意**Gitea 的 Issue 模板语法与 Gitee 兼容。
### 6.2 配置 Webhook(通知钉钉/企业微信)
在仓库设置 → Webhooks 中添加:
```
URL: https://oapi.dingtalk.com/robot/send?access_token=xxx
触发事件: Push, Pull Request, Issue
```
### 6.3 禁用公开注册(私有化)
```ini
# app.ini
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
```
---
## 七、备份策略
### 7.1 自动备份脚本
```bash
#!/bin/bash
# backup.sh
BACKUP_DIR="/backup/gitea"
DATE=$(date +%Y%m%d)
# 备份 Gitea 数据
tar czf "$BACKUP_DIR/gitea-$DATE.tar.gz" /var/lib/gitea
# 保留最近 7 天的备份
find "$BACKUP_DIR" -name "gitea-*.tar.gz" -mtime +7 -delete
```
添加到 crontab
```bash
# 每天凌晨 2 点备份
0 2 * * * /path/to/backup.sh
```
---
## 八、常见问题
### Q1: Gitea 和 GitLab 怎么选?
- **Gitea**:轻量、简单、资源占用低,适合小团队(< 50 人)
- **GitLab**:功能强大、生态丰富,适合大团队、需要复杂 CI/CD 流水线
### Q2: 需要多少服务器资源?
| 规模 | CPU | 内存 | 磁盘 | 推荐 |
|------|-----|------|------|------|
| 小团队 (< 10人) | 1 核 | 1GB | 20GB | 阿里云/腾讯云 入门配置 |
| 中等团队 (10-50人) | 2 核 | 2GB | 50GB | 阿里云 2C2G |
| 大团队 (50+人) | 4 核 | 4GB | 100GB | 阿里云 4C4G |
### Q3: 可以从 Gitea 迁移回 Gitee/GitHub 吗?
可以。Gitea 支持导出仓库,也可以直接推送回其他 Git 平台。
### Q4: Gitea Actions 和 GitHub Actions 完全兼容吗?
大部分常用 action 兼容。如果某个 action 不兼容,可以自己写 shell 脚本替代。
---
## 九、部署检查清单
- [ ] 准备一台 Linux 服务器(1C1G 起步)
- [ ] 安装 Docker(推荐)或下载 Gitea 二进制
- [ ] 配置域名和 DNS 解析
- [ ] 配置 Nginx 反向代理 + HTTPS
- [ ] 初始化 Gitea 并创建管理员账号
- [ ] 创建组织(如 `rui`
- [ ] 从 Gitee 迁移仓库
- [ ] 配置 Gitea Actions Runner
- [ ] 创建 CI/CD 工作流文件
- [ ] 配置 Webhook 通知
- [ ] 设置备份策略
---
## 十、相关文档
- [Gitea 官方文档](https://docs.gitea.com/)
- [Gitea Actions 文档](https://docs.gitea.com/usage/actions/overview)
- [OpenCode 多仓库操作指南](../docs/opencode-workflow.md)
---
> **提示**:如果不方便自己部署服务器,也可以考虑 **Gitea Cloud**(官方托管版)或继续使用 Gitee 免费版(仅代码托管,CI/CD 用其他方案如 Jenkins)。
+242
View File
@@ -0,0 +1,242 @@
# 灰度发布(金丝雀发布)
> 网关层灰度发布解决方案,支持多种灰度策略,实现平滑的服务升级。
## 概述
灰度发布(Canary Release)是一种渐进式发布策略,通过将小部分流量先路由到新版本,验证无误后再逐步扩大流量,最终完成全量发布。本方案在网关层实现,对业务服务无侵入。
## 核心特性
| 特性 | 说明 |
|------|------|
| **多策略支持** | 权重、用户白名单、IP 白名单、强制 Header |
| **多服务独立配置** | 每个服务可配置独立的灰度规则 |
| **无侵入** | 业务服务无需改动,仅通过元数据标记版本 |
| **优先级控制** | 多种策略按优先级执行,确保灰度准确性 |
## 灰度策略(按优先级排序)
### 1. 强制 Header(最高优先级)
客户端通过指定 Header 强制访问特定版本,用于测试验证。
```http
GET /user/api/info HTTP/1.1
Host: api.example.com
X-Grayscale-Version: v2
```
### 2. 用户白名单
特定用户 ID 强制走灰度版本,通常用于内部测试账号。
**识别方式**:通过 `X-User-Id` Header 识别用户身份。
### 3. IP 白名单
特定 IP 或 IP 段的请求走灰度版本,支持 CIDR 格式。
**示例**
- `192.168.1.0/24` - 内网网段
- `10.0.0.5` - 单个 IP
### 4. 权重比例(最低优先级)
按比例分配流量,适用于全量灰度场景。
**示例**`weight: 10` 表示 10% 的请求走灰度版本。
## 后端服务配置
### 1. 标记灰度实例
在服务的 `application.yml` 中通过 Nacos 元数据标记版本:
```yaml
spring:
cloud:
nacos:
discovery:
metadata:
gray.version: v2 # 标记为灰度版本 v2
```
### 2. 部署多个版本
同时部署稳定版本和灰度版本:
```
服务实例列表
├── rui-service-user:v1 (稳定版本,无 gray.version 或 gray.version=v1)
├── rui-service-user:v1 (稳定版本)
└── rui-service-user:v2 (灰度版本,gray.version=v2)
```
## 网关配置
在 Nacos 的 `rui-gateway.yaml` 中配置灰度规则:
```yaml
grayscale:
# 强制灰度 Header 名称
force-header: X-Grayscale-Version
# 灰度规则
rules:
# rui-service-user 服务的灰度规则
rui-service-user:
enabled: true # 启用灰度
version: v2 # 灰度版本标识(与实例元数据对应)
weight: 10 # 10% 流量走灰度
user-ids: # 特定用户走灰度
- user001
- user002
ip-ranges: # 特定 IP 走灰度
- 192.168.1.0/24
```
### 配置项说明
| 配置项 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `enabled` | boolean | 是 | 是否启用灰度 |
| `version` | string | 否 | 灰度版本标识,默认 `gray` |
| `weight` | int | 否 | 灰度流量权重(0-100),默认 0 |
| `user-ids` | list | 否 | 用户白名单列表 |
| `ip-ranges` | list | 否 | IP 白名单列表,支持 CIDR |
## 使用场景示例
### 场景一:内部测试
仅需内部测试人员访问新版本:
```yaml
grayscale:
rules:
rui-service-user:
enabled: true
version: v2
user-ids:
- tester001
- tester002
```
### 场景二:按比例灰度
对全量用户按比例灰度:
```yaml
grayscale:
rules:
rui-service-user:
enabled: true
version: v2
weight: 5 # 先 5%,逐步提高到 10%、20%、50%、100%
```
### 场景三:内部 IP 灰度
公司内部员工先行体验:
```yaml
grayscale:
rules:
rui-service-user:
enabled: true
version: v2
ip-ranges:
- 192.168.0.0/16 # 公司内网
- 10.0.0.0/8 # VPN 网段
```
### 场景四:组合策略
多种策略同时生效(按优先级匹配):
```yaml
grayscale:
rules:
rui-service-user:
enabled: true
version: v2
weight: 10
user-ids:
- vip001 # VIP 用户优先体验
ip-ranges:
- 192.168.1.0/24 # 办公室网络
```
**匹配逻辑**
1. 强制 Header > 2. 用户白名单 > 3. IP 白名单 > 4. 权重
## 验证灰度是否生效
### 1. 查看网关日志
开启 DEBUG 级别日志,查看实例选择:
```
选择灰度实例: rui-service-user:v2 [v2]
选择稳定实例: rui-service-user:v1
```
### 2. 通过 Header 验证
在响应头中添加版本标识:
```java
// 后端服务在响应中添加版本信息
response.setHeader("X-Server-Version", version);
```
### 3. 使用强制 Header 测试
```bash
curl -H "X-Grayscale-Version: v2" https://api.example.com/user/api/info
```
## 最佳实践
### 1. 灰度前准备
- [ ] 新版本已通过测试环境验证
- [ ] 监控和告警已配置
- [ ] 回滚方案已准备
### 2. 灰度流程
1. **Phase 1**:内部测试(用户白名单)
2. **Phase 2**:办公网灰度(IP 白名单)
3. **Phase 3**:小流量灰度(weight=1%
4. **Phase 4**:逐步扩大(5% → 10% → 20% → 50% → 100%
5. **Phase 5**:全量发布,下线旧版本
### 3. 监控指标
- 错误率对比(灰度 vs 稳定)
- 响应时间对比
- 业务指标波动
### 4. 快速回滚
发现问题时立即关闭灰度:
```yaml
grayscale:
rules:
rui-service-user:
enabled: false # 关闭灰度,全部流量回稳定版本
```
## 注意事项
1. **服务发现延迟**:Nacos 服务列表更新可能有延迟(默认 5-10 秒)
2. **数据兼容性**:确保新版本与旧版本数据库兼容
3. **接口兼容性**:灰度期间避免破坏性接口变更
4. **会话一致性**:有状态服务需考虑会话粘滞或共享
## 相关文档
- [Spring Cloud Gateway 文档](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/)
- [Spring Cloud LoadBalancer 文档](https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer)
@@ -0,0 +1,302 @@
# 聚合启动器(rui-service-starter)使用文档
## 1. 什么是聚合启动器
`rui-service-starter` 是将多个业务微服务合并为一个 Spring Boot 应用启动的**轻量化部署方案**。
| 模块 | 说明 |
|------|------|
| `rui-service-system` | 系统管理服务(菜单、角色、部门、字典、租户等) |
| `rui-service-user` | 用户基础服务(用户、等级、权限等) |
> **认证中心(rui-auth)和网关(rui-gateway)保持独立**,不参与聚合。
---
## 2. 适用场景
| 场景 | 推荐模式 |
|------|---------|
| 中小型项目、团队规模 < 10 人 | ✅ **聚合模式**(节省资源、简化部署) |
| 大型项目、多团队并行开发 | 独立微服务模式(服务隔离、独立发布) |
| 从单体向微服务过渡 | ✅ **聚合模式**(先聚合后拆分) |
| 本地开发调试 | ✅ **聚合模式**(一键启动所有业务) |
**聚合模式优势:**
- 减少 JVM 内存占用(节省 500MB+
- 减少 Nacos 注册中心压力
- 一次打包、一次部署、一次启动
- 本地开发只需启动 3 个服务(gateway + auth + starter
---
## 3. 架构说明
### 3.1 独立微服务模式(默认)
```
┌─────────────┐ ┌─────────────┐ ┌─────────────────┐
│ Gateway │────▶│ rui-auth │────▶│ rui-service-xxx │
│ :9300 │ │ :9301 │ │ :9302~930N │
└─────────────┘ └─────────────┘ └─────────────────┘
┌──────────┴──────────┐
▼ ▼
rui-service-system rui-service-user
:9302 :9303
```
### 3.2 聚合模式
```
┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐
│ Gateway │────▶│ rui-auth │────▶│ rui-service-starter │
│ :9300 │ │ :9301 │ │ :9399 │
└─────────────┘ └─────────────┘ └─────────────────────────┘
┌──────────┴──────────┐
▼ ▼
[system 模块] [user 模块]
共享 JVM + 端口
```
---
## 4. 快速启动
### 4.1 编译
```bash
cd backend
# 编译聚合启动器(自动编译依赖模块)
mvn clean install -pl rui-service/rui-service-starter -am -DskipTests
```
### 4.2 本地开发启动
```bash
# 方式一:IDE 直接运行 StarterApplication.java
# 方式二:命令行启动
java -jar rui-service/rui-service-starter/target/rui-service-starter-1.0.0-SNAPSHOT.jar
```
### 4.3 生产环境启动
```bash
# 指定环境变量(也可在 Nacos 中配置)
java -jar rui-service-starter-1.0.0-SNAPSHOT.jar \
--NACOS_SERVER_ADDR=127.0.0.1:8848 \
--spring.profiles.active=prod
```
---
## 5. 端口与服务名
| 服务 | 端口 | Nacos 服务名 | 说明 |
|------|------|-------------|------|
| rui-gateway | 9300 | rui-gateway | 网关(保持独立) |
| rui-auth | 9301 | rui-auth | 认证中心(保持独立) |
| rui-service-system | 9302 | rui-service-system | 系统服务(独立模式) |
| rui-service-user | 9303 | rui-service-user | 用户服务(独立模式) |
| **rui-service-starter** | **9399** | **rui-service-starter** | **聚合启动器(替代 system + user** |
---
## 6. 网关路由配置
Nacos `rui-gateway.yaml` 中已默认使用聚合模式,将 `/system/**``/user/**` 统一路由到 `rui-service-starter`
```yaml
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: rui-auth
uri: lb://rui-auth
predicates:
- Path=/auth/**
filters:
- StripPrefix=0
# ========== 聚合模式(默认,中小型项目)==========
- id: rui-service-starter
uri: lb://rui-service-starter
predicates:
- Path=/user/**,/system/**
filters:
- StripPrefix=0
# ========== 独立微服务模式(大型项目)==========
# - id: rui-service-user
# uri: lb://rui-service-user
# predicates:
# - Path=/user/**
# filters:
# - StripPrefix=0
# - id: rui-service-system
# uri: lb://rui-service-system
# predicates:
# - Path=/system/**
# filters:
# - StripPrefix=0
```
> **提示**:如需切换到独立模式,取消注释独立路由并注释掉聚合路由即可。
---
## 7. Feign 调用说明
### 7.1 聚合模式下的 Feign 行为
| Feign 接口 | 目标服务 | 默认指向 | 说明 |
|-----------|---------|---------|------|
| `UserAuthFeign` | `rui-service-user` | `rui-service-starter` | 通过 Nacos 路由到聚合启动器 |
| `SystemClientFeign` | `rui-service-system` | `rui-service-starter` | 通过 Nacos 路由到聚合启动器 |
| `TokenManageFeign` | `rui-auth` | `rui-auth` | 认证中心保持独立 |
### 7.2 配置原理
FeignClient 的 `value` 属性使用 `${feign.providers.xxx}` 变量,默认指向聚合启动器:
```java
@FeignClient(contextId = "userAuthFeign",
value = "${feign.providers.user:rui-service-starter}", // 默认指向聚合启动器
path = "/user/inner")
```
**Nacos `rui-common.yaml` 中的公共配置:**
```yaml
feign:
providers:
user: rui-service-starter # 用户服务:默认指向聚合启动器
system: rui-service-starter # 系统服务:默认指向聚合启动器
auth: rui-auth # 认证中心:保持独立
```
**切换独立模式**:在对应服务的 Nacos 配置中覆盖:
```yaml
feign:
providers:
user: rui-service-user # 改回独立用户服务
system: rui-service-system # 改回独立系统服务
```
---
## 8. Nacos 配置建议
### 8.1 配置中心
聚合启动器启动时会加载以下 Nacos 配置:
| 配置文件 | 说明 |
|---------|------|
| `rui-service-starter.yaml` | 聚合服务专属配置(可选) |
| `rui-common.yaml` | 公共配置(日志、线程池等) |
| `rui-data.yaml` | 数据源配置(MySQL、Redis 等) |
### 8.2 配置继承
聚合模式下,`rui-service-starter.yaml` 可以覆盖 `rui-service-system.yaml``rui-service-user.yaml` 中的冲突配置。
建议将**业务无关的基础配置**放到 `rui-common.yaml`**数据库连接等环境配置**放到 `rui-data.yaml`
---
## 9. 两种模式切换指南
### 9.1 从独立模式切换到聚合模式
1. **停止** `rui-service-user``rui-service-system`
2. **启动** `rui-service-starter`
3. **修改网关路由**(见 6.1
4. **完成**
### 9.2 从聚合模式切换到独立模式
1. **停止** `rui-service-starter`
2. **启动** `rui-service-system`(端口 9302)和 `rui-service-user`(端口 9303
3. **修改网关路由**(见 6.2
4. **完成**
### 9.3 代码层面注意事项
- 聚合模式下,**所有业务代码无需修改**
- 两个服务的 `@RestController` 路由前缀不同(`/system/**``/user/**`),天然无冲突
- Mapper 扫描范围 `com.rui.**.mapper` 已覆盖两个模块
---
## 10. 常见问题
### Q1: 聚合启动器内存占用多少?
> 约 400~600MBJVM Heap),比同时启动 user + system(约 800MB+)节省 30%~50%。
### Q2: 可以再加其他服务吗?
> 可以。在 `rui-service-starter/pom.xml` 中增加依赖即可:
> ```xml
> <dependency>
> <groupId>com.rui</groupId>
> <artifactId>rui-service-order</artifactId>
> <version>${revision}</version>
> </dependency>
> ```
> 同时确保新服务的 Controller 路由前缀不与现有服务冲突。
### Q3: 聚合模式下事务跨服务吗?
> 同一 JVM 内,system 和 user 的 Service 互相调用时,**Spring 本地事务仍然有效**。但建议保持服务边界清晰,避免过度耦合。
### Q4: 日志怎么区分是哪个模块的?
> 日志文件统一输出到 `logs/rui-service-starter/`,通过日志内容中的类名(`com.rui.service.system.xxx` / `com.rui.service.user.xxx`)区分来源模块。
### Q5: 健康检查端点是什么?
> `GET http://localhost:9399/actuator/health`
### Q6: 聚合模式和独立模式可以同时运行吗?
> **不建议**。会导致 Nacos 中同时存在 `rui-service-starter` 和 `rui-service-user/system`Feign 调用可能出现负载均衡到错误实例的情况。
---
## 11. 本地开发推荐启动顺序
聚合模式下,本地开发只需启动 3 个服务:
```bash
# 1. 启动 Nacos(如果本地运行)
sh startup.sh -m standalone
# 2. 启动 Redis(如果本地运行)
redis-server
# 3. 启动 MySQL(如果本地运行)
# 4. 启动 rui-auth(认证中心)
java -jar rui-auth/target/rui-auth-*.jar
# 5. 启动 rui-gateway(网关)
java -jar rui-gateway/target/rui-gateway-*.jar
# 6. 启动 rui-service-starter(聚合业务服务)
java -jar rui-service/rui-service-starter/target/rui-service-starter-*.jar
```
> 相比独立模式(需要启动 5+ 个服务),开发效率大幅提升。
---
## 12. 文档更新记录
| 日期 | 版本 | 说明 |
|------|------|------|
| 2026-05-30 | 1.0 | 初始版本,聚合 system + user |
+21
View File
@@ -0,0 +1,21 @@
# rui-common-feign 分析报告
## 模块功能
Feign 客户端增强模块,自动为所有 Feign 请求注入租户/代理链等请求头。
## 核心类
| 类 | 作用 |
|------|------|
| `CloudEnableFeignClients` | 替代 `@EnableFeignClients`,同时导入自定义 Registrar |
| `CustomFeignClientsRegistrar` | 重写 Feign 客户端注册逻辑,注入 Tenant Header 和代理链 |
| `CloudFeignAutoConfiguration` | 自动配置,注册 Actuator 端点 |
| `FeignClientEndpoint` | `/actuator/feignClients` 查看所有 Feign 客户端 |
## 优化点
1. 三个 `CustomFeignClientsRegistrar` 实际只用一个,删除冗余 `2.java``MyFeignClientsRegistrar`
2. `CloudFeignAutoConfiguration` 缺少 `@AutoConfigureAfter` 正确的顺序
3. 添加 `AutoConfiguration.imports` 注册
4. 静态内部类过多,简化
@@ -0,0 +1,587 @@
# 项目文档治理与 Superpowers 流程规范化
> **设计日期**: 2026-06-02
> **版本**: v1.0
> **状态**: 已批准(待实施)
> **目标**: 建立完整的项目文档体系,将 Superpowers 工作流融入项目规范
---
## 一、项目背景
### 1.1 现状分析
当前项目(睿核科技 - rui)是一个基于 Spring Cloud 的微服务通用平台框架,已开发支付模块(rui-payment)等业务模块。项目文档和代码规范主要维护在 `AGENTS.md` 中。
### 1.2 存在的问题
通过对现有 `AGENTS.md`(668 行)和项目文档体系的分析,发现以下 **7 类问题**
| 问题类型 | 具体表现 | 影响 |
|---------|---------|------|
| **结构混乱** | 规范、指南、规则混杂,无清晰层级 | 查找信息困难,新成员上手慢 |
| **缺少导航** | 无目录、无文档地图 | 无法快速定位需要的规范 |
| **Superpowers 缺失** | 完全没有工作流说明 | 团队无法按标准流程协作 |
| **格式错误** | 多处 `**` 标记不匹配、表格格式问题 | 阅读体验差,可能误解 |
| **内容缺失** | 无环境搭建指南、无模块创建指引、无代码审查规范 | 新人无法自助上手 |
| **职责越界** | GitNexus 工具配置与项目规范混在一起 | 文档职责不清晰 |
| **信息孤岛** | 未引用 `docs/` 下的其他文档 | 文档之间无关联 |
### 1.3 目标定义
1. **建立标准**:重构 AGENTS.md,使其成为项目的"宪法"
2. **查漏补缺**:系统检查现有文档和代码,生成分类问题清单
3. **融入流程**:将 Superpowers 工作流融入项目规范
4. **持续可用**:建立可复用的模板和检查清单
---
## 二、整体工作流设计
采用 **三阶段流水线**,每阶段都有明确的输入、输出和验收标准:
```
┌─────────────────────────────────────────────────────────────────┐
│ 阶段一:重构 AGENTS.md(建立标准) │
│ 输入:现有 AGENTS.md + 项目文档体系 │
│ 输出:新版 AGENTS.md(项目宪法) │
│ 验收:文档结构清晰、规范完整、Superpowers 流程融入 │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 阶段二:全面查漏补缺(基于标准) │
│ 输入:新版 AGENTS.md + 现有项目所有文档和代码 │
│ 输出:分类问题清单(高/中/低优先级) │
│ 验收:检查覆盖率 100%、问题可追踪、有优先级 │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 阶段三:修复与规范化(建立流程) │
│ 输入:问题清单 │
│ 输出:修复后的文档/代码 + Superpowers 工作流模板 + 改进报告 │
│ 验收:高优先级问题全部修复、流程可运行、报告完整 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 三、阶段一:重构 AGENTS.md
### 3.1 现有文档诊断
**格式问题清单**
- 第 173 行:`SecurityUtils` 后的 `**` 未闭合
- 第 176-178 行:多处 `**` 标记不匹配
- 表格格式在部分终端渲染异常
**内容缺失清单**
- 缺少项目级 README.md
- 缺少开发环境搭建指南
- 缺少 Superpowers 工作流说明
- 缺少代码审查规范
- 缺少模块创建标准流程
**结构问题清单**
- 无文档地图/目录
- GitNexus 配置与项目规范混杂
- 章节间无逻辑递进关系
### 3.2 新版结构设计
```
AGENTS.md(项目宪法 - 所有开发者必读)
├── 1. 文档地图(新增)← 60秒了解项目文档体系
├── 2. 项目概览(精简)← 技术栈、仓库结构
├── 3. 环境准备(新增)← 开发环境、初始化步骤
├── 4. Superpowers 工作流(新增)← 四阶段开发流程
├── 5. 编码规范(合并优化)← 所有代码规范集中
├── 6. 基础设施速查(优化)← 工具类、注解、复用原则
├── 7. 模块开发指南(新增)← 如何创建标准业务模块
├── 8. 运维规范(合并)← Git、数据库、Nacos、构建
├── 9. 协作规范(优化)← 对话管理、工作边界
└── 10. 附录(新增)← 错误码、数据类型、文档索引
```
### 3.3 章节详细设计
#### 第 1 章:文档地图(新增)
目标:让新成员在 60 秒内了解项目文档体系
内容:
- 文档体系全景图
- 快速导航(按角色:新手/开发者/架构师)
- 文档更新规则(谁维护、何时更新)
#### 第 2 章:项目概览(精简现有内容)
保留:
- 项目名称、类型
- 精简版仓库结构(突出 app/ 和 backend/
- 技术栈表格
移除:
- 过详细的技术说明(迁移到专门文档)
#### 第 3 章:环境准备(新增)
内容:
- 开发环境要求(JDK 21、Maven、MySQL、Node.js 等)
- 项目初始化步骤(clone → 配置 → 构建 → 运行)
- IDE 配置建议(IntelliJ IDEA 插件、代码风格)
- 验证清单(如何确认环境就绪)
#### 第 4 章:Superpowers 工作流(新增)
核心内容,四阶段流程:
**Phase 1: Brainstorming(头脑风暴)**
- 目标:明确需求、确定方案
- 输入:用户原始需求
- 输出:批准的设计方向
- 检查清单:
- [ ] 是否已探索项目上下文?
- [ ] 是否已提出澄清问题?
- [ ] 是否已对比 2-3 种方案?
- [ ] 用户是否已确认方向?
**Phase 2: Spec Writing(规格编写)**
- 目标:编写详细设计文档
- 输入:批准的设计方向
- 输出:设计文档(保存到 `docs/superpowers/specs/`
- 检查清单:
- [ ] 是否包含背景与目标?
- [ ] 是否包含详细设计?
- [ ] 是否包含验收标准?
- [ ] 是否已完成自我审查?
- [ ] 用户是否已审查批准?
**Phase 3: Plan Writing(计划编写)**
- 目标:编写可执行的实施计划
- 输入:批准的设计文档
- 输出:实施计划(保存到 `docs/superpowers/plans/`
- 检查清单:
- [ ] 是否已分解为具体任务?
- [ ] 每个任务是否有明确的验收标准?
- [ ] 是否有风险评估?
- [ ] 用户是否已审查批准?
**Phase 4: Implementation(实施执行)**
- 目标:按计划执行任务
- 输入:实施计划
- 输出:代码 + 文档更新
- 检查清单:
- [ ] 是否按任务逐个执行?
- [ ] 每个任务是否已完成验证?
- [ ] 是否已更新实施跟踪文档?
- [ ] 是否已完成最终审查?
#### 第 5 章:编码规范(合并优化现有章节)
合并内容:
- 基础编码规范(Lombok、命名、注释)
- 模块 Bean 注入规范
- Mapper 规范
- Controller 规范
- URL 路由规范
- 异常处理规范
- 日志规范
- 测试规范
优化点:
- 统一用表格展示"正确 vs 错误"示例
- 增加常见错误模式说明
- 增加检查工具建议(如 Checkstyle 规则)
#### 第 6 章:基础设施速查(优化)
优化内容:
- 工具类速查表(增加使用场景列)
- 注解速查表(增加参数说明)
- 复用原则(增加反例说明)
#### 第 7 章:模块开发指南(新增)
内容:
- 何时需要新建模块
- 模块命名规范
- 模块标准结构(common/core/provider/api/task
- 创建步骤清单
- AutoConfiguration 配置
- 模块间依赖规则
#### 第 8 章:运维规范(合并现有章节)
合并内容:
- Git 提交规范
- 数据库脚本执行规则
- Nacos 配置管理规则
- 构建与发布规范
- 前端构建规则
#### 第 9 章:协作规范(优化现有章节)
优化内容:
- OpenCode `/new` 使用指南(增加决策树)
- 工作边界规则(增加流程图)
- 框架问题处理流程(增加模板)
#### 第 10 章:附录(新增)
内容:
- 错误码分配表(完整区间划分)
- 数据类型对照表(MySQL ↔ Java ↔ JDBC
- 相关文档索引(docs/ 下所有文档的导航)
- 术语表
### 3.4 职责分离
**GitNexus 配置迁移**
将 AGENTS.md 中的 GitNexus 部分(第 626-668 行)迁移到独立文档:`docs/gitnexus-guide.md`
原因:
- AGENTS.md 是"项目规范"GitNexus 是"工具配置"
- 职责分离后,AGENTS.md 更聚焦
- GitNexus 指南可以独立更新
---
## 四、阶段二:全面查漏补缺
### 4.1 检查策略
基于新版 AGENTS.md 的 10 个章节,设计 **4 维度检查清单**
#### 维度一:文档体系完整性(检查 `docs/` 目录)
| 检查项 | 检查内容 | 优先级 | 当前状态 |
|--------|---------|--------|---------|
| 1.1 | 是否存在 `README.md` 项目总览 | 🔴 高 | ❌ 缺失 |
| 1.2 | 每个业务模块是否有独立设计文档 | 🔴 高 | ⚠️ 仅支付模块有 |
| 1.3 | 是否存在环境搭建指南 | 🔴 高 | ❌ 缺失 |
| 1.4 | 是否存在数据库变更记录 | 🟡 中 | ❌ 缺失 |
| 1.5 | API 文档是否完整 | 🟡 中 | 待确认 |
| 1.6 | 文档之间是否有交叉引用 | 🟡 中 | ❌ 无引用 |
| 1.7 | 实施跟踪文档是否最新 | 🟢 低 | ⚠️ 支付模块显示完成 |
#### 维度二:AGENTS.md 规范落地(检查代码库)
| 检查项 | 检查内容 | 优先级 | 检查方式 |
|--------|---------|--------|---------|
| 2.1 | 所有 Entity 是否继承 BaseEntity | 🔴 高 | 代码扫描 |
| 2.2 | 是否使用 Lombok | 🔴 高 | 代码扫描 |
| 2.3 | Service 是否使用构造器注入 | 🔴 高 | 代码扫描 |
| 2.4 | Mapper SQL 是否使用 `#prefix#` | 🔴 高 | 正则匹配 |
| 2.5 | Controller 是否按规范分类 | 🟡 中 | 目录检查 |
| 2.6 | 标准 CRUD 是否继承 BaseController | 🟡 中 | 代码扫描 |
| 2.7 | 异常处理是否统一用 BizException | 🟡 中 | 代码扫描 |
| 2.8 | Git 提交是否符合规范格式 | 🟢 低 | 提交历史检查 |
#### 维度三:项目结构与配置
| 检查项 | 检查内容 | 优先级 | 当前状态 |
|--------|---------|--------|---------|
| 3.1 | 模块命名是否符合规范 | 🔴 高 | ✅ 符合 |
| 3.2 | 每个模块是否有 AutoConfiguration | 🔴 高 | 待确认 |
| 3.3 | `application-dev.yml` 是否在 `.gitignore` | 🔴 高 | ✅ 符合 |
| 3.4 | Nacos 配置是否已推送 | 🟡 中 | 待确认 |
| 3.5 | 数据库脚本是否已执行 | 🟡 中 | 待确认 |
#### 维度四:Superpowers 流程就绪度
| 检查项 | 检查内容 | 优先级 | 当前状态 |
|--------|---------|--------|---------|
| 4.1 | 是否存在 `docs/superpowers/` 目录 | 🔴 高 | ❌ 缺失 |
| 4.2 | 是否存在设计文档模板 | 🔴 高 | ❌ 缺失 |
| 4.3 | 是否存在实施计划模板 | 🔴 高 | ❌ 缺失 |
| 4.4 | 是否存在代码审查清单 | 🟡 中 | ❌ 缺失 |
### 4.2 输出格式
每个问题按以下格式记录:
```markdown
### 🔴 HIGH-001: 缺少 README.md
| 属性 | 内容 |
|------|------|
| **检查项** | 文档体系完整性 - 1.1 |
| **问题描述** | 项目根目录缺少 README.md,新成员无法快速了解项目 |
| **影响范围** | 所有新加入的开发者 |
| **建议修复** | 创建 README.md,包含项目简介、技术栈、快速开始、文档索引 |
| **参考标准** | AGENTS.md 第 1 章 |
| **优先级** | 🔴 高 |
```
### 4.3 问题分类与优先级
**优先级定义**
| 优先级 | 定义 | 修复时限 |
|--------|------|---------|
| 🔴 高 | 阻碍开发或违反核心规范 | 立即修复 |
| 🟡 中 | 影响效率或存在风险 | 1 周内修复 |
| 🟢 低 | 优化建议 | 下次迭代处理 |
---
## 五、阶段三:修复与规范化
### 5.1 修复计划(按优先级)
#### 🔴 高优先级(必须立即修复)
| 序号 | 任务 | 输出物 | 验收标准 |
|------|------|--------|---------|
| H1 | 创建 README.md | `/README.md` | 包含项目简介、技术栈、快速开始、文档索引 |
| H2 | 重构 AGENTS.md | `/AGENTS.md` | 10 章结构完整、格式正确、无遗漏 |
| H3 | 创建 Superpowers 目录结构 | `docs/superpowers/` | 目录存在且结构正确 |
| H4 | 创建设计文档模板 | `docs/superpowers/templates/design-template.md` | 包含所有必需章节 |
| H5 | 创建实施计划模板 | `docs/superpowers/templates/plan-template.md` | 包含任务分解和验收标准 |
| H6 | 迁移 GitNexus 指南 | `docs/gitnexus-guide.md` | 内容完整、AGENTS.md 中已移除 |
#### 🟡 中优先级(建议 1 周内修复)
| 序号 | 任务 | 输出物 | 验收标准 |
|------|------|--------|---------|
| M1 | 补充环境搭建指南 | `docs/environment-setup.md` | 步骤可执行、有验证方法 |
| M2 | 创建代码审查清单 | `docs/superpowers/templates/review-checklist.md` | 覆盖主要规范点 |
| M3 | 检查模块 AutoConfiguration | 代码修复 | 每个可复用模块都有 |
| M4 | 检查 Nacos 配置同步 | 配置确认 | 所有配置已推送 |
#### 🟢 低优先级(可选优化)
| 序号 | 任务 | 输出物 | 验收标准 |
|------|------|--------|---------|
| L1 | 补充数据库变更记录 | `docs/database-changelog.md` | 记录所有表结构变更 |
| L2 | 统一文档交叉引用 | 各文档更新 | 相关文档间有链接 |
| L3 | 检查测试覆盖率 | 报告 | 核心业务覆盖 ≥ 80% |
### 5.2 Superpowers 工作流模板
#### 模板一:设计文档模板
```markdown
# <模块/功能名称> 设计文档
> **设计日期**: YYYY-MM-DD
> **版本**: v1.0
> **状态**: 设计中/已批准
> **目标**: <一句话描述目标>
---
## 一、背景与目标
### 1.1 现状分析
<描述当前现状、存在的问题>
### 1.2 目标定义
<明确本次设计的目标,建议 3-5 条>
---
## 二、详细设计
### 2.1 整体架构
<架构图、流程图>
### 2.2 核心组件
<各组件的职责、接口>
### 2.3 数据流
<数据如何流转>
### 2.4 接口设计
<API 定义>
### 2.5 数据库设计
<表结构、索引>
### 2.6 错误处理
<异常场景、错误码>
---
## 三、验收标准
- [ ] <可验证的验收条件 1>
- [ ] <可验证的验收条件 2>
- [ ] <可验证的验收条件 3>
---
## 四、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| <风险 1> | 高/中/低 | <措施> |
```
#### 模板二:实施计划模板
```markdown
# <模块/功能名称> 实施计划
> **计划日期**: YYYY-MM-DD
> **版本**: v1.0
> **状态**: 待执行/执行中/已完成
> **关联设计**: <链接到设计文档>
---
## 一、任务清单
### Phase 1: <阶段名称>
| 序号 | 任务 | 负责人 | 预估工时 | 状态 | 验证方式 |
|------|------|--------|---------|------|---------|
| 1.1 | <具体任务> | <负责人> | <工时> | ⬜ | <如何验证> |
---
## 二、进度跟踪
| 日期 | 完成任务 | 遇到的问题 | 解决方案 |
|------|---------|-----------|---------|
| YYYY-MM-DD | <任务> | <问题> | <方案> |
---
## 三、完成总结
- **实际工时**: <X 小时>
- **偏差分析**: <与预估的差异及原因>
- **经验教训**: <可复用的经验>
```
#### 模板三:代码审查清单
```markdown
# 代码审查清单
## 基础规范
- [ ] 使用 Lombok,无手写 getter/setter
- [ ] Service 使用构造器注入
- [ ] Entity 继承 BaseEntity
- [ ] 类名不加 Rui 前缀(除非冲突)
## Mapper 规范
- [ ] SQL 使用 `#prefix#` 占位符
- [ ] 无硬编码表前缀
## Controller 规范
- [ ] 标准 CRUD 继承 BaseController
- [ ] URL 路径符合规范
- [ ] 使用正确注解(@Inner@AuthIgnore 等)
## 异常与日志
- [ ] 使用 BizException 而非 RuntimeException
- [ ] 返回 Result.ok()/Result.fail()
- [ ] 日志无敏感信息
## 测试
- [ ] 核心逻辑有单元测试
- [ ] 测试命名符合规范
```
### 5.3 改进报告模板
```markdown
# 项目文档治理改进报告
> **报告日期**: YYYY-MM-DD
> **治理范围**: 项目文档体系 + AGENTS.md + Superpowers 流程
> **执行人**: <执行者>
---
## 一、检查统计
| 维度 | 检查项数 | 发现问题 | 已修复 | 待修复 |
|------|---------|---------|--------|--------|
| 文档体系完整性 | X | Y | Z | W |
| 代码规范落地 | X | Y | Z | W |
| 项目结构与配置 | X | Y | Z | W |
| Superpowers 就绪度 | X | Y | Z | W |
| **合计** | **X** | **Y** | **Z** | **W** |
## 二、问题分布
<图表或表格展示问题分布>
## 三、关键改进
1. <改进 1:重构 AGENTS.md>
2. <改进 2:建立 Superpowers 流程>
3. <改进 3...>
## 四、后续建议
1. <建议 1>
2. <建议 2>
```
---
## 六、验收标准
### 6.1 阶段验收标准
| 阶段 | 验收标准 | 验证方式 |
|------|---------|---------|
| 阶段一 | AGENTS.md 结构清晰、内容完整、格式正确 | 人工审查 |
| 阶段二 | 检查覆盖率 100%、问题清单完整 | 逐项核对 |
| 阶段三 | 高优先级问题全部修复、模板可用 | 实际使用验证 |
### 6.2 最终验收标准
- [ ] 新成员可在 30 分钟内通过 AGENTS.md 了解项目规范
- [ ] Superpowers 四阶段流程可在项目中完整运行
- [ ] 所有文档间有交叉引用,无信息孤岛
- [ ] 代码规范有自动化检查手段(或明确的手动检查清单)
---
## 七、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 重构 AGENTS.md 期间,团队仍在开发新功能 | 中 | 使用分支管理,重构完成后再合并 |
| 检查清单不够全面,遗漏问题 | 中 | 基于 AGENTS.md 逐条设计检查项,确保覆盖 |
| 团队成员不习惯 Superpowers 流程 | 低 | 提供培训和模板,逐步推广 |
| 文档更新后无人维护 | 中 | 明确文档负责人,纳入代码审查 |
---
## 八、附录
### 8.1 术语表
| 术语 | 说明 |
|------|------|
| Superpowers | OpenCode 的规范化工作流框架 |
| Brainstorming | 头脑风暴阶段,明确需求和方案 |
| Spec | 设计文档/规格说明书 |
| AGENTS.md | 项目规范文档(项目宪法) |
### 8.2 相关文档
| 文档 | 路径 | 说明 |
|------|------|------|
| AGENTS.md | `/AGENTS.md` | 项目规范(待重构) |
| 支付模块设计 | `docs/支付模块架构设计.md` | 示例设计文档 |
| 支付模块跟踪 | `docs/支付模块实施跟踪.md` | 示例跟踪文档 |
### 8.3 参考标准
- 本设计文档本身遵循 Superpowers 流程编写
- 模板设计参考了行业最佳实践
---
> **文档结束**
> 下一步:进入实施计划阶段(由 Superpowers Plan Writer 执行)
@@ -0,0 +1,236 @@
# MQ 统一推送入口设计文档
> **设计日期**: 2026-06-04
> **版本**: v1.0
> **状态**: 已批准
> **目标**: 创建统一消息队列推送入口 MqClient,封装多 Provider 路由、Action 注入、异常兜底,对标 spring-rui MqDefaultClient
---
## 一、背景与目标
### 1.1 现状分析
当前项目已有基础 MQ 能力:
- `MqService` 接口:提供 `send()` 方法,面向业务开发者直接使用
- `Message<T>` 模型:含 id, topic, payload, headers, timestamp, retryCount
- `RedisMqService` / `RabbitMqService`:分别基于 Redis Pub/Sub 和 RabbitMQ 的实现
- `MqTopic` 注解:用于方法级消息订阅
**存在的问题**
1. 缺乏统一推送门面:业务代码需要直接注入 `MqService` 并选择实现,无法自动按 Provider 路由
2. 无 Provider 抽象:无法在多环境(开发用 Redis、生产用 RabbitMQ)间平滑切换
3. 无 Action 语义:消息缺乏"添加/删除/更新"等业务动作标识
4. 异常处理分散:各实现自行处理异常,缺乏统一兜底
### 1.2 目标定义
1. 创建 `MqClient` 门面类,作为业务层唯一推送入口
2. 引入 `MqPublisher` Provider 接口,实现多 MQ 后端自动路由
3. 扩展 `Message` 模型,支持 action、provider、exchange、delay 等高级字段
4. 保持现有 `MqService` 接口不变,确保向后兼容
5. 所有推送操作统一异常捕获和日志记录
---
## 二、详细设计
### 2.1 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 业务层 (Business) │
│ MqClient.publish(...) │
└──────────────────────────┬──────────────────────────────────┘
┌──────────────────────────▼──────────────────────────────────┐
│ MqClient (门面/统一入口) │
│ · 自动从 Spring 容器获取所有 MqPublisher 实现 │
│ · 按 support(MqProvider) 过滤匹配的 Publisher │
│ · 自动注入 action 到 payload │
│ · 统一 try-catch + 日志 │
└──────────────────────────┬──────────────────────────────────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ RedisMqPublisher │ │ RabbitMqPublisher│ │ FutureProvider │
│ (Redis实现) │ │ (RabbitMQ实现) │ │ (可扩展) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 2.2 核心组件
| 组件 | 职责 | 位置 |
|------|------|------|
| `MqClient` | 统一推送门面,业务层唯一入口 | `rui-common-mq` |
| `MqPublisher` | Provider 能力接口,定义 support + publish | `rui-common-mq` |
| `MqProvider` | Provider 枚举(RABBITMQ, REDIS | `rui-common-mq` |
| `MqProperties` | 配置属性(默认 provider、topic prefix | `rui-common-mq` |
| `MqAction` | 消息动作枚举(ADDED, DELETED 等) | `rui-common-mq` |
| `Message<T>` | 扩展后的消息模型 | `rui-common-mq` |
### 2.3 数据流
```
启动阶段 (一次)
Spring 注入 List<MqPublisher> → 遍历注册到 EnumMap
publisherMap = { RABBITMQ: RabbitMqPublisher, REDIS: RedisMqPublisher }
运行时 (每次 publish)
MqClient.publish(String topic, MqAction action, T payload)
├─ 1. 构造 Message<T>
├─ 2. 若 action != NONE,将 action 序列化后注入 payload
├─ 3. 若 message.provider == null,使用 MqProperties 默认值
├─ 4. publisherMap.get(provider) → O(1) 直接取到 Publisher
├─ 5. 调用 publisher.publish(message)
└─ 6. catch Exception → log.error,不抛异常
```
### 2.4 接口设计
**MqClient(门面类)**
采用**构造器注入 + 启动预构建 EnumMap**,避免运行时遍历:
```java
@Component
@Slf4j
public class MqClient {
private final MqProperties properties;
private final EnumMap<MqProvider, MqPublisher> publisherMap = new EnumMap<>(MqProvider.class);
public MqClient(MqProperties properties, List<MqPublisher> publishers) {
this.properties = properties;
for (MqPublisher p : publishers) {
publisherMap.put(p.getProvider(), p); // O(1) 注册
}
}
/** 使用默认 Provider 推送 */
public <T> void publish(String topic, T payload);
/** 使用默认 Provider 推送,带 Action */
public <T> void publish(String topic, MqAction action, T payload);
/** 指定 Provider 推送 */
public <T> void publish(MqProvider provider, String topic, T payload);
/** 指定 Provider 推送,带 Action */
public <T> void publish(MqProvider provider, String topic, MqAction action, T payload);
/** 完整 Message 推送 */
public <T> void publish(Message<T> message);
private MqPublisher resolve(MqProvider provider) {
return publisherMap.get(provider); // O(1) 查找
}
}
```
**MqPublisherProvider 接口)**
```java
public interface MqPublisher {
/** 返回该 Publisher 支持的 Provider 类型 */
MqProvider getProvider();
/** 基础推送 */
<T> void publish(String topic, T payload);
/** 完整消息推送 */
<T> void publish(Message<T> message);
}
```
**MqProvider(枚举)**
```java
public enum MqProvider {
RABBITMQ,
REDIS
}
```
**MqProperties(配置)**
```java
@ConfigurationProperties(prefix = "mq")
@Data
public class MqProperties {
private MqProvider provider = MqProvider.RABBITMQ;
private String prefix = "rui";
public String enTopic(String topic) { ... }
public String deTopic(String topic) { ... }
}
```
**MqAction(动作枚举,精简版)**
```java
public enum MqAction {
NONE, ADDED, DELETED, UPDATED, CREATED,
CANCEL, ENABLED, SUCCESSFUL, FAILURE
}
```
### 2.5 扩展现有实现
**RedisMqService** 调整:
- 实现 `MqPublisher` 接口
- `support(MqProvider.REDIS)` 返回 true
- `publish()` 复用现有 `send()` 逻辑
**RabbitMqService** 调整:
- 实现 `MqPublisher` 接口
- `support(MqProvider.RABBITMQ)` 返回 true
- `publish()` 复用现有 `send()` 逻辑
### 2.6 错误处理
- **异常策略**`MqClient` 所有 publish 方法统一 try-catch,记录 error 日志,不抛异常给业务层
- **Provider 不匹配**:若找不到支持该 Provider 的 Publisher,记录 warn 日志
- **Payload 为空**:自动创建空 JSON 对象 `{}`
---
## 三、验收标准
- [ ] `MqClient` 可正常注入并调用 `publish()` 发送消息
- [ ] 通过配置 `mq.provider=redis` 可自动切换到 Redis 实现
- [ ] `publish(topic, MqAction.ADDED, payload)` 发送的消息包含 action 字段
- [ ] 发送异常时不抛异常,仅记录日志
- [ ] 现有 `MqService.send()` 调用不受影响,向后兼容
- [ ] 项目可正常编译通过
---
## 四、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 现有 `MqService` 被多处使用 | 中 | 不修改 `MqService`,新建 `MqPublisher` 接口 |
| `Message<T>` 扩展后序列化兼容性 | 低 | 新增字段均为可空,不影响现有序列化 |
| SpringUtil 在静态方法中可能未初始化 | 低 | MqClient 通过构造器注入,非静态调用 |
---
## 五、对标分析
| 维度 | spring-rui (MqDefaultClient) | 本设计 (MqClient) |
|------|------------------------------|-------------------|
| 门面类名 | `MqDefaultClient` | `MqClient`(更简洁) |
| Provider 接口 | `MqService`(含 support/publish | `MqPublisher`(不冲突现有 `MqService` |
| 配置类名 | `MqAutoConfiguration` | `MqProperties`(更语义化) |
| JSON 框架 | fastjson2 | Jackson(适配本项目) |
| Provider 支持 | MQTT, RABBITMQ, REDIS | RABBITMQ, REDISMQTT 暂不需要) |
| Action 枚举 | `Actions`80+ 项) | `MqAction`(精简 9 项) |
| 包名 | `org.rui.common.mq` | `com.rui.common.mq` |
+278
View File
@@ -0,0 +1,278 @@
# spring-rui 代码分析报告
> 分析日期:2026-05-26
> 目标:分析 `~/rhkj/spring-rui`(排除 `app/` 目录),对比 {root},识别可复用模式和缺失能力。
---
## 一、项目架构对比
| 维度 | spring-rui | {root}(本项目) |
|------|-----------|-------------------|
| 包名 | `org.rui` | `com.rui` |
| 模块组织 | 单体 `rui-common` 内含 15 子模块 | 平铺 4 子模块 `core/mybatis/security/web` |
| ORM | MyBatis Plus + 自定义类型处理器 | MyBatis Plus |
| 响应模型 | `Result<T>` extends `JSONObject` | `R<T>` POJO |
| JSON 框架 | **fastjson2**(核心依赖) | Jackson |
| 多租户 | 配置驱动(TABLE/IGNORE/NONE 三种模式) | 硬编码忽略表列表 |
| 缓存 | Redis Manager(发布/订阅) | 无 |
---
## 二、spring-rui 各模块分析
### 2.1 rui-common-core
| 文件 | 说明 | {root} 状态 |
|------|------|---------------|
| `Result.java` | 响应模型,extends `JSONObject`,含 `error`/`message`/`code`/`data`/`rows`/`results`,支持链式调用 | 已有 `R<T>`,功能较弱 |
| `ResultResponse.java` | 简单响应 holder | 已有 `ResultCode` |
| `ResponseStatus.java` | 枚举:SUCCESSFUL/UNAUTHORIZED/FORBIDDEN/NOT_FOUND/VALIDATE_FAILED 等 | 可参考补全 |
| `TenantContextHolder.java` | 租户上下文 ThreadLocal | 已有 `TenantContextHolder` |
| `TenantBroker.java` | 支持在指定租户下执行代码 | 无,可选 |
| `IdWorker.java` | ID 生成器 | 无 |
| `JacksonConfig.java` | Jackson 全局配置 | 可参考 |
| `KeyStrResolver.java` / `TenantKeyStrResolver.java` | 缓存 Key 解析(含租户隔离) | 无,可复用 |
| `SpringUtil.java` | Spring 上下文工具 | 无 |
| `ServletUtil.java` | Servlet 请求工具(提取所有参数为 Map) | 无 |
| `JsonUtil.java` | JSON 合并/赋值工具(`JsonUtil.assign()` | 无 |
| `AmountUtil.java` / `NumberUtil.java` / `RandomUtil.java` | 金额/数字/随机工具 | 无 |
| `IPSeekerUtil.java` / `IpUtil.java` | IP 归属地查询 | 无 |
| `StringUtil.java` | 字符串增强工具 | 已有 hutool |
| `LocalDateTimeUtil.java` | 时间工具 | 无 |
| `Validate.java` / `BaseRegex.java` / `NumericUtil.java` | 正则/校验工具 | 无 |
| `HttpClient.java` | HTTP 客户端封装 | 无 |
| `CertHelper.java` / `CertUtil.java` / `RsaUtil.java` / `AesUtil.java` / `MD5Util.java` / `ShaUtil.java` / `SignatureUtil.java` | 加密/证书工具链 | 无 |
| `DelayQueueManager.java` / `QueueTask.java` | 延迟队列 | 无 |
| `Scheduled.java` / `Task.java` / `TaskConfiguration.java` / `TaskFactory.java` | 定时任务调度 | 无 |
| `ProxyProperties.java` / `ProxyInterceptor.java` / `ProxyProperty.java` | 代理链支持 | 无(我们有 proxy chain ThreadLocal |
| `LocaleContextHolder.java` / `LocaleUtil.java` | 国际化支持 | 无 |
| `FileUtil.java` / `MimeUtil.java` / `ZipUtil.java` / `GzipUtil.java` | 文件/压缩工具 | 无 |
| `AntPathMatcher.java` / `PathMatcher.java` | 路径匹配 | 无 |
| `AccountProperty.java` / `AppProperty.java` / `MqttProperty.java` / `WsProperty.java` | 配置属性类 | 无 |
### 2.2 rui-common-data
| 文件 | 说明 | {root} 状态 |
|------|------|---------------|
| `MybatisPlusConfiguration.java` | MP 插件配置(分页 + 多租户 + 防全表更新) | 已有,缺 `BlockAttackInnerInterceptor` |
| `MybatisProperties.java` | MP 配置属性(表前缀、租户模式、忽略表列表) | 无 |
| **`PageService.java`** | **通用查询构建器**(类型字段 + 排序 + 分页 + 返回 Result | **2026-05-26 已移植** |
| `PageOptions.java` | 查询选项(下拉框过滤) | 无,可选 |
| `TenantHandler.java` | 配置驱动多租户(TABLE/IGNORE/NONE 三种模式) | 可参考优化 |
| `BaseMultiTableInnerInterceptor.java` | 多表拦截器 | 无 |
| `TableInterceptor.java` | 表前缀替换(`#prefix#` 占位符) | 无 |
| `AbstractObjectTypeHandler.java` / `JsonObjectTypeHandler.java` / `JsonArrayTypeHandler.java` / `StringArrayTypeHandler.java` / `LongTypeHandler.java` / `IntegerTypeHandler.java` / `BigDecimalArrayTypeHandler.java` / `ArrayTypeHandler.java` | 类型处理器 | 无 |
### 2.3 rui-common-security
| 文件 | 说明 | {root} 状态 |
|------|------|---------------|
| `ResourceServerAutoConfiguration.java` / `ResourceServerConfiguration.java` | OAuth2 资源服务器配置 | 无(不同安全架构) |
| `AuthUtil.java` / `UserUtil.java` | 认证用户工具 | 部分(`SecurityUtils` |
| `OAuthBearerTokenResolver.java` | Token 解析器 | 无 |
| `RedisOAuth2AuthorizationService.java` | Redis 授权存储 | 无 |
| `CustomOAuth2FeignRequestInterceptor.java` | Feign Token 传递 | 无 |
| `OAuthUserDetailsService.java` | 用户详情服务接口 | 无 |
| `DefaultOAuthUserDetailsServiceImpl.java` | 默认实现 | 无 |
| `WeixinOAuthUserDetailsServiceImpl.java` | 微信登录实现 | 无 |
| `RestTemplateUserDetailsServiceImpl.java` | RestTemplate 远程调用实现 | 无 |
| `PermissionService.java` | 权限校验 Bean`@perm.isSystemTenant()` | 无 |
| `AuthIgnore.java` / `Inner.java` | 注解 | 无 |
| `SecurityInnerAspect.java` | 内部调用切面 | 无 |
| `FeignClientConfiguration.java` / `FeignConfiguration.java` | Feign 配置 | 无 |
| `RemoteUserService.java` / `RemoteClientService.java` | Feign 远程调用接口 | 无 |
| `GlobalExceptionHandler.java` | 全局异常处理 | 已有 |
### 2.4 rui-service-system(参考 CRUD 模式)
**重点分析 — CRUD 标准模式:**
```
Controller
├── GET /{module}/list → PageService 分页列表
├── GET /{module}/{id} → doGet: service.getById(id)
├── POST /{module} → doAdd: service.doAdd(bean)
├── PUT /{module}/{id} → doUpdate: service.doUpdate(bean)
├── DELETE /{module}/remove/{id} → doRemove: 物理删除
└── DELETE /{module}/{id} → doDelete: 软删除
Service (extends ServiceImpl<Mapper, Entity>)
├── doAdd(bean) → bean.setDefaultValue(); this.save(bean)
├── doUpdate(bean) → bean.setUpdatedAt(); this.updateById(bean)
├── doRemove(id) → this.removeById(id)
└── doDelete(id) → baseMapper.doDelete(id) // SQL: UPDATE SET deleted=!deleted
Mapper (extends BaseMapper<Entity>)
└── @Update("UPDATE #prefix#table SET deleted=!deleted WHERE id=#{id}")
Integer doDelete(Serializable id);
```
**模式要点:**
- `doRemove` = 物理删除,`doDelete` = 软删除(toggle deleted 标记)
- 所有 Service 方法返回 `Result<?>` 而非实体
- Mapper 中使用 `#prefix#` 占位符,由 `TableInterceptor` 自动替换为实际表前缀
- Entity 实现 `setDefaultValue()` 方法初始化字段
### 2.5 rui-service-users(同上,CRUD 模式验证)
与 rui-service-system 完全一致的 CRUD 模式。每个模块有:
- `model/` — 实体类(`implements Serializable`
- `mapper/` — MyBatis Plus Mapper
- `service/` — 接口
- `service/impl/` — 实现(`extends ServiceImpl` + `doAdd/doUpdate/doRemove/doDelete`
- `controller/` — 接口(`extends BaseController` + CRUD 端点)
### 2.6 rui-gateway
| 文件 | 说明 | {root} 状态 |
|------|------|---------------|
| `GrayLoadBalancer.java` | 灰度负载均衡 | 无 |
| `GrayReactiveLoadBalancerClientFilter.java` | 灰度过滤器 | 无 |
| `GlobalExceptionHandler.java` | 网关异常处理 | 无 |
### 2.7 rui-oauth2
| 文件 | 说明 | {root} 状态 |
|------|------|---------------|
| `OauthApplication.java` | 启动类(无额外配置) | 已有 AuthApplication |
---
## 三、已移植到 {root} 的内容
### 2026-05-26 批量移植
| 文件 | 位置 | 对应 spring-rui |
|------|------|----------------|
| `PageResult.java` | `rui-common-core` | 新增(基于 Result 分页字段) |
| `PageService.java` | `rui-common-mybatis` | `rui-common-data/PageService.java` |
| `BaseController.java` | `rui-common-web` | `rui-service-system/controller/BaseController.java` |
| 防全表更新 | `MyBatisPlusConfig.java` | `MybatisPlusConfiguration.java` 中的 `BlockAttackInnerInterceptor` |
---
## 四、后续可选移植项
### 优先级高(核心能力)
| 功能 | 来源模块 | 说明 |
|------|---------|------|
| `SpringUtil.java` | core | Spring 上下文静态获取 |
| `ServletUtil.java` | core | 请求参数提取工具(PageService 用) |
| `JsonUtil.java` | core | JSON 合并/赋值(`assign` 方法在更新场景很有用) |
| `MybatisProperties.java` | data | 配置化表前缀/租户模式,替代硬编码 |
### 优先级中(开发提效)
| 功能 | 来源模块 | 说明 |
|------|---------|------|
| `StringUtil.java` / `AmountUtil.java` / `NumberUtil.java` | core | 常用工具 |
| `LocalDateTimeUtil.java` | core | 时间工具 |
| `IdWorker.java` | core | ID 生成器 |
| `IPSeekerUtil.java` / `IpUtil.java` | core | IP 工具 |
| `ArrayTypeHandler.java` 系列 | data | MP 类型处理器(JSON/数组) |
| `KeyStrResolver.java` 系列 | core | 缓存 Key 租户隔离 |
### 优先级低(按需移植)
| 功能 | 来源模块 | 说明 |
|------|---------|------|
| 加密工具链(RSA/AES/MD5/SHA/证书) | core | spring-rui 安全相关 |
| `HttpClient.java` | core | HTTP 客户端 |
| `DelayQueueManager.java` | core | 延迟队列 |
| 定时任务调度 | core | 自定义任务框架 |
| 国际化 | core | Locale 工具 |
| 灰度负载均衡 | gateway | 与部署环境强相关 |
| Feign 远程调用 | security | 与认证架构耦合 |
---
## 五、CRUD 编程规范(推荐)
基于 spring-rui 模式,推荐 {root} 业务模块遵循以下规范:
### Controller 规范
```java
@RestController
@RequiredArgsConstructor
@RequestMapping("system/tenant")
public class SysTenantController extends BaseController {
private final SysTenantService service;
@GetMapping("/list")
public R<PageResult<SysTenant>> list(HttpServletRequest request) {
PageService<SysTenant> ps = new PageService<>(request);
ps.setDefaultOrderColumn("updatedAt");
ps.putBoolean("deleted").putQuery("deleted", false);
ps.putLike("name");
return ps.getResults(service);
}
@GetMapping("{id}")
public R<SysTenant> get(@PathVariable Long id) {
return R.ok(service.getById(id));
}
@PostMapping
public R<Void> add(@RequestBody SysTenant bean) {
return service.doAdd(bean);
}
@PutMapping("{id}")
public R<Void> update(@RequestBody SysTenant bean) {
return service.doUpdate(bean);
}
@DeleteMapping("{id}")
public R<Void> delete(@PathVariable Long id) {
return service.doDelete(id);
}
}
```
### Service 规范
```java
public interface SysTenantService extends IService<SysTenant> {
R<Void> doAdd(SysTenant bean);
R<Void> doUpdate(SysTenant bean);
R<Void> doDelete(Long id);
}
public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant>
implements SysTenantService {
public R<Void> doAdd(SysTenant bean) {
bean.setDefaultValue();
if (this.save(bean)) return R.ok("添加成功");
return R.fail("添加失败");
}
public R<Void> doUpdate(SysTenant bean) {
bean.setUpdatedAt(LocalDateTime.now());
if (this.updateById(bean)) return R.ok("更新成功");
return R.fail("更新失败");
}
public R<Void> doDelete(Long id) {
if (baseMapper.doDelete(id) == 1) return R.ok("删除成功");
return R.fail("删除失败");
}
}
```
### Mapper 规范
```java
@Mapper
public interface SysTenantMapper extends BaseMapper<SysTenant> {
@Update("UPDATE rui_system_tenant SET deleted = !deleted WHERE id = #{id}")
int doDelete(@Param("id") Long id);
}
```
> 注:spring-rui 使用 `#prefix#` 占位符 + TableInterceptor 自动替换表前缀。本项目暂不引入该机制,直接在 SQL 中写完整表名。
+54
View File
@@ -0,0 +1,54 @@
# <模块/功能名称> 设计文档
> **设计日期**: YYYY-MM-DD
> **版本**: v1.0
> **状态**: 设计中/已批准
> **目标**: <一句话描述目标>
---
## 一、背景与目标
### 1.1 现状分析
<描述当前现状、存在的问题>
### 1.2 目标定义
<明确本次设计的目标,建议 3-5 条>
---
## 二、详细设计
### 2.1 整体架构
<架构图、流程图>
### 2.2 核心组件
<各组件的职责、接口>
### 2.3 数据流
<数据如何流转>
### 2.4 接口设计
<API 定义>
### 2.5 数据库设计
<表结构、索引>
### 2.6 错误处理
<异常场景、错误码>
---
## 三、验收标准
- [ ] <可验证的验收条件 1>
- [ ] <可验证的验收条件 2>
- [ ] <可验证的验收条件 3>
---
## 四、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| <风险 1> | 高/中/低 | <措施> |
+32
View File
@@ -0,0 +1,32 @@
# <模块/功能名称> 实施计划
> **计划日期**: YYYY-MM-DD
> **版本**: v1.0
> **状态**: 待执行/执行中/已完成
> **关联设计**: <链接到设计文档>
---
## 一、任务清单
### Phase 1: <阶段名称>
| 序号 | 任务 | 负责人 | 预估工时 | 状态 | 验证方式 |
|------|------|--------|---------|------|---------|
| 1.1 | <具体任务> | <负责人> | <工时> | ⬜ | <如何验证> |
---
## 二、进度跟踪
| 日期 | 完成任务 | 遇到的问题 | 解决方案 |
|------|---------|-----------|---------|
| YYYY-MM-DD | <任务> | <问题> | <方案> |
---
## 三、完成总结
- **实际工时**: <X 小时>
- **偏差分析**: <与预估的差异及原因>
- **经验教训**: <可复用的经验>
+25
View File
@@ -0,0 +1,25 @@
# 代码审查清单
## 基础规范
- [ ] 使用 Lombok,无手写 getter/setter
- [ ] Service 使用构造器注入
- [ ] Entity 继承 BaseEntity
- [ ] 类名不加 Rui 前缀(除非冲突)
## Mapper 规范
- [ ] SQL 使用 `#prefix#` 占位符
- [ ] 无硬编码表前缀
## Controller 规范
- [ ] 标准 CRUD 继承 BaseController
- [ ] URL 路径符合规范
- [ ] 使用正确注解(@Inner@AuthIgnore 等)
## 异常与日志
- [ ] 使用 BizException 而非 RuntimeException
- [ ] 返回 Result.ok()/Result.fail()
- [ ] 日志无敏感信息
## 测试
- [ ] 核心逻辑有单元测试
- [ ] 测试命名符合规范(should_ 开头)
+63
View File
@@ -0,0 +1,63 @@
# 项目文档治理改进报告
> **报告日期**: 2026-06-02
> **治理范围**: 项目文档体系 + AGENTS.md + Superpowers 流程
> **执行人**: OpenCode AI
---
## 一、检查统计
| 维度 | 检查项数 | 发现问题 | 已修复 | 待修复 |
|------|---------|---------|--------|--------|
| 文档体系完整性 | 7 | 4 | 4 | 0 |
| 代码规范落地 | 8 | 待抽样确认 | - | - |
| 项目结构与配置 | 5 | 待确认 | - | - |
| Superpowers 就绪度 | 4 | 4 | 4 | 0 |
| **合计** | **24** | **8+** | **8** | **0** |
## 二、已完成的改进
### 2.1 文档体系
- [x] 创建 README.md(项目总览与快速开始)
- [x] 创建环境搭建指南(docs/environment-setup.md
- [x] 创建 GitNexus 独立指南(docs/gitnexus-guide.md
### 2.2 AGENTS.md 重构
- [x] 新增文档地图(第 1 章)
- [x] 新增环境准备(第 3 章)
- [x] 新增 Superpowers 工作流(第 4 章)
- [x] 新增模块开发指南(第 7 章)
- [x] 新增附录(第 10 章)
- [x] 优化编码规范、基础设施速查、运维规范、协作规范
- [x] 修复格式错误(** 标记不匹配)
- [x] 结构重组为 10 章体系,增加目录导航
### 2.3 Superpowers 流程
- [x] 创建设计文档模板
- [x] 创建实施计划模板
- [x] 创建代码审查清单
- [x] 建立 docs/superpowers/ 目录体系
## 三、提交记录
```
fa51237 docs(project): 添加项目 README.md
9ac019b docs(gitnexus): 创建独立的 GitNexus 使用指南
9fb19b1 docs(agents): 全面重构 AGENTS.md,建立 10 章规范体系
88646e9 docs(template): 添加 Superpowers 模板和环境搭建指南
```
## 四、后续建议
1. **代码规范自动化检查**: 考虑引入 Checkstyle 或 Spotless 自动检查代码规范
2. **文档交叉引用**: 在各文档间添加相互引用链接
3. **持续维护**: 每次规范变更时同步更新 AGENTS.md
4. **培训推广**: 向团队介绍 Superpowers 工作流
---
**报告完成 ✅**
@@ -0,0 +1,371 @@
# Admin-UI 分模块打包功能设计文档
> **设计日期**: 2026-06-04
> **版本**: v1.0
> **状态**: 已批准
> **目标**: 实现 Admin-UI 按系统配置分模块打包,支持不同租户类型输出不同产物包
---
## 一、背景与目标
### 1.1 现状分析
当前 Admin-UI 存在以下问题:
1. **路由硬编码**:所有页面路由集中在 `router/index.ts` 中硬编码,无法按模块裁剪
2. **构建产物单一**:无论服务哪个租户,都打包所有页面代码,产物体积大
3. **缺乏系统差异化**:Dashboard、登录页等核心页面无法根据不同系统定制
4. **模块管理已有雏形**:后端已支持租户模块配置(`ModuleDialog.vue`),但前端构建未与之配合
### 1.2 目标定义
1. **构建时分包**:根据 JSON 配置文件,构建时只打包指定模块的代码
2. **动态路由生成**:替换硬编码路由,构建时根据配置动态生成路由表
3. **系统差异化页面**:支持 Dashboard、登录页按系统配置加载不同子组件
4. **多产物输出**:不同系统输出到 `dist/{systemKey}/` 目录
5. **保持现有功能**:菜单 API 获取、权限控制、主题切换等功能不受影响
---
## 二、详细设计
### 2.1 整体架构
```
构建流程:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ build-config/ │ │ Vite Plugin │ │ 构建产物 │
│ cashier.json │────→│ (module-build) │────→│ dist/cashier/ │
│ admin.json │ │ │ │ dist/admin/ │
│ super.json │ │ 1. 读取配置 │ │ dist/super/ │
└─────────────────┘ │ 2. 生成路由 │ │ │
│ 3. 注入配置 │ └─────────────────┘
│ 4. 配置输出 │
└──────────────────┘
运行时:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 统一入口页面 │────→│ 虚拟模块配置 │────→│ 系统特定子组件 │
│ Dashboard │ │ __SYSTEM_CONFIG__│ │ systems/ │
│ Login │ │ │ │ Cashier.vue │
└─────────────────┘ └──────────────────┘ │ Super.vue │
└─────────────────┘
```
### 2.2 核心组件
#### 2.2.1 配置文件(`build-config/`
每个系统一个 JSON 配置文件:
```typescript
interface BuildConfig {
/** 系统唯一标识,产物目录名 */
key: string
/** 系统显示名称 */
name: string
/** 系统描述 */
description?: string
/** 包含的模块列表 */
modules: string[]
/** 登录页配置 */
login: {
/** 登录组件名(对应 views/login/systems/ 下的组件) */
component: string
/** 是否显示租户ID输入 */
showTenantInput: boolean
/** 页面标题 */
title: string
/** 副标题 */
subtitle?: string
/** 背景图路径 */
background?: string
/** Logo路径 */
logo?: string
}
/** Dashboard配置 */
dashboard: {
/** Dashboard组件名(对应 views/dashboard/systems/ 下的组件) */
component: string
/** 页面标题 */
title: string
}
/** 主题配置 */
theme: {
/** 主题色 */
primaryColor: string
/** 页面标题 */
title: string
}
}
```
**示例配置:**
```json
// build-config/super.json
{
"key": "super",
"name": "超级管理后台",
"description": "超级租户专用,包含租户管理",
"modules": ["system", "user"],
"login": {
"component": "Super",
"showTenantInput": false,
"title": "睿核平台管理",
"subtitle": "超级管理员登录"
},
"dashboard": {
"component": "Super",
"title": "平台运营概览"
},
"theme": {
"primaryColor": "#722ed1",
"title": "睿核平台管理"
}
}
```
```json
// build-config/cashier.json
{
"key": "cashier",
"name": "收银系统",
"description": "面向收银场景的管理后台",
"modules": ["system", "user", "cms", "cashier"],
"login": {
"component": "Cashier",
"showTenantInput": true,
"title": "睿核收银",
"subtitle": "门店管理系统"
},
"dashboard": {
"component": "Cashier",
"title": "收银数据概览"
},
"theme": {
"primaryColor": "#1677ff",
"title": "睿核收银"
}
}
```
#### 2.2.2 Vite 插件(`scripts/vite-plugin-module-build.ts`
插件职责:
1. **解析命令行参数**:读取 `--system={key}` 参数
2. **加载配置**:读取 `build-config/{key}.json`
3. **生成虚拟路由模块**`virtual:generated-routes`
- 根据 `config.modules` 从路由模板中组装路由表
- 只包含指定模块的路由 + 核心页面路由(登录、Dashboard入口、个人中心、设置)
4. **生成虚拟配置模块**`virtual:system-config`
- 将配置对象注入为全局常量 `__SYSTEM_CONFIG__`
5. **配置构建输出**
- `build.outDir = dist/${config.key}`
- `build.rollupOptions.treeshake = true` 确保未使用代码被移除
**路由生成逻辑:**
```typescript
// router/modules/system.ts
export const systemRoutes = [
{ path: 'system/menu', name: 'SystemMenu', component: () => import('@/views/system/menu/Index.vue'), meta: { i18n: 'menu.systemMenu' } },
{ path: 'system/role', name: 'SystemRole', component: () => import('@/views/system/role/Index.vue'), meta: { i18n: 'menu.systemRole' } },
// ...
]
// router/modules/user.ts
export const userRoutes = [
{ path: 'user/info', name: 'UserInfo', component: () => import('@/views/user/info/Index.vue'), meta: { i18n: 'menu.userInfo' } },
// ...
]
// 插件根据 config.modules 动态组装
const moduleRoutes = config.modules.flatMap(module => {
const routeModule = routeModules[module]
return routeModule ? routeModule.routes : []
})
```
#### 2.2.3 统一入口页面
**Dashboard 入口(`views/dashboard/Index.vue`):**
```vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
import systemConfig from 'virtual:system-config'
// 动态加载系统特定的 Dashboard 组件
const dashboardComponent = defineAsyncComponent(() =>
import(`./systems/${systemConfig.dashboard.component}.vue`)
)
</script>
<template>
<component :is="dashboardComponent" />
</template>
```
**登录页入口(`views/login/Index.vue`):**
```vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
import systemConfig from 'virtual:system-config'
// 动态加载系统特定的登录组件
const loginComponent = defineAsyncComponent(() =>
import(`./systems/${systemConfig.login.component}.vue`)
)
</script>
<template>
<component :is="loginComponent" :config="systemConfig.login" />
</template>
```
### 2.3 模块映射关系
建立 `src/views/` 下的页面目录与模块标识的映射:
| 模块标识 | 对应目录 | 包含页面 |
|---------|---------|---------|
| `system` | `views/system/*` | 菜单、角色、部门、岗位、字典、配置、日志、登录日志、租户、租户套餐、数据权限、OAuth2客户端 |
| `user` | `views/user/*` | 用户信息、用户详情、等级、等级日志、地址、账户 |
| `order` | `views/order/*` | 订单列表、退款记录 |
| `cms` | `views/cms/*` | 文章、分类、轮播图 |
| `marketing` | `views/marketing/*` | 优惠券、活动管理 |
| `demo` | `views/demo/*` | 图标演示、列表演示 |
| `cashier` | `views/cashier/*` | 门店、包间、定价、订单、商品、报表 |
**核心页面(所有系统默认包含,不依赖模块配置):**
- `views/login/Index.vue` - 登录页入口
- `views/login/systems/*.vue` - 系统特定登录组件
- `views/dashboard/Index.vue` - Dashboard 入口
- `views/dashboard/systems/*.vue` - 系统特定 Dashboard 组件
- `views/profile/Index.vue` - 个人中心
- `views/settings/Index.vue` - 系统设置
### 2.4 目录结构
```
admin-ui/
├── build-config/ # 系统打包配置
│ ├── cashier.json
│ ├── admin.json
│ ├── super.json
│ └── default.json # 默认配置(全模块,用于开发)
├── scripts/ # 构建脚本
│ └── vite-plugin-module-build.ts # Vite 插件
├── src/
│ ├── router/
│ │ ├── index.ts # 改造:使用虚拟路由模块
│ │ └── modules/ # 新增:按模块拆分路由配置
│ │ ├── core.ts # 核心路由(登录、Dashboard入口等)
│ │ ├── system.ts
│ │ ├── user.ts
│ │ ├── order.ts
│ │ ├── cms.ts
│ │ ├── marketing.ts
│ │ ├── demo.ts
│ │ └── cashier.ts
│ ├── views/
│ │ ├── login/
│ │ │ ├── Index.vue # 改造:统一入口
│ │ │ └── systems/ # 新增:系统特定登录组件
│ │ │ ├── Default.vue # 默认登录页
│ │ │ ├── Super.vue # 超级租户登录页
│ │ │ └── Cashier.vue # 收银系统登录页
│ │ ├── dashboard/
│ │ │ ├── Index.vue # 改造:统一入口
│ │ │ └── systems/ # 新增:系统特定 Dashboard
│ │ │ ├── Default.vue # 默认 Dashboard
│ │ │ ├── Cashier.vue # 收银系统 Dashboard
│ │ │ └── Super.vue # 超级租户 Dashboard
│ │ └── ... # 业务页面(保持现有结构)
│ ├── types/
│ │ └── system-config.d.ts # 系统配置类型定义
│ └── ...
├── package.json # 改造:添加构建命令
└── vite.config.ts # 改造:注册插件
```
### 2.5 构建命令
```json
// package.json
{
"scripts": {
"dev": "vite --port 3000",
"dev:cashier": "vite --port 3000 -- --system=cashier",
"dev:super": "vite --port 3000 -- --system=super",
"build": "vue-tsc && vite build",
"build:cashier": "vue-tsc && vite build -- --system=cashier",
"build:super": "vue-tsc && vite build -- --system=super",
"build:admin": "vue-tsc && vite build -- --system=admin",
"build:all": "pnpm build:cashier && pnpm build:super && pnpm build:admin"
}
}
```
**产物输出:**
```
dist/
├── cashier/ # 收银系统(system + user + cms + cashier
├── super/ # 超级租户(system + user
├── admin/ # 普通后台(system + user + order + cms + marketing
└── default/ # 默认(全模块,用于开发测试)
```
### 2.6 主题配置应用
系统配置中的 `theme` 字段在运行时应用:
```typescript
// App.vue 或布局组件
import systemConfig from 'virtual:system-config'
// 设置页面标题
document.title = systemConfig.theme.title
// 设置主题色(Element Plus
const el = document.documentElement
el.style.setProperty('--el-color-primary', systemConfig.theme.primaryColor)
```
---
## 三、验收标准
- [ ] 执行 `pnpm build:super` 成功构建,产物输出到 `dist/super/`,只包含 system 和 user 模块的页面
- [ ] 执行 `pnpm build:cashier` 成功构建,产物输出到 `dist/cashier/`,包含 system、user、cms、cashier 模块的页面
- [ ] 不同系统的 Dashboard 显示不同的子组件内容
- [ ] 不同系统的登录页显示不同的子组件内容(超级租户无租户ID输入)
- [ ] 构建产物中不包含未配置模块的页面代码(Tree Shaking 生效)
- [ ] 现有菜单 API 获取、权限控制、主题切换功能正常
- [ ] 开发模式 `pnpm dev:cashier` 正常工作,热更新无问题
---
## 四、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| Vite 插件开发复杂度 | 中 | 插件逻辑清晰拆分:配置读取、路由生成、虚拟模块、输出配置 |
| Tree Shaking 不彻底 | 中 | 使用 `import()` 动态导入,配合 Rollup 的 `treeshake` 配置,构建后检查产物 |
| 动态组件加载失败 | 低 | 添加错误处理,加载失败时回退到 Default 组件 |
| 现有功能回归 | 中 | 构建后逐一验证核心功能:登录、菜单、CRUD、主题切换 |
| 多人协作冲突 | 低 | 配置文件集中管理,模块路由独立文件,减少冲突 |
---
## 五、后续扩展
1. **国际化支持**:配置文件中可扩展 `locales` 字段,支持系统特定的翻译覆盖
2. **模块懒加载**:未来可考虑运行时动态加载模块(Module Federation
3. **版本管理**:配置文件支持 `version` 字段,用于产物版本控制
4. **CI/CD 集成**:构建命令可直接接入 Jenkins/GitHub Actions,参数化构建不同系统