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:
@@ -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
|
||||
@@ -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 框架仓库?
|
||||
|
||||
AI(OpenCode)基于 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"})
|
||||
AI:AuthUtil 提供 getUserId()、getTenantId()、getUser()...
|
||||
|
||||
5. 员工:退款金额用 BigDecimal 吗?
|
||||
【AI执行】gitnexus_query({query:"金额计算 BigDecimal", repo:"spring-ai"})
|
||||
AI:框架规范要求金额使用 DECIMAL(19,4),Java 对应 BigDecimal...
|
||||
|
||||
6. AI 生成完整代码
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、常见问题
|
||||
|
||||
### Q1:AI 提示 "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` |
|
||||
|
||||
---
|
||||
|
||||
> **提示**:本手册随项目演进持续更新。如有问题,联系框架维护者或技术负责人。
|
||||
@@ -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 + Resilience4j(ThreadPoolBulkhead)+ TransmittableThreadLocal(TTL)
|
||||
|
||||
---
|
||||
|
||||
## 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) {
|
||||
// ① 先把任务提交到工厂自己的 ExecutorService(newCachedThreadPool)
|
||||
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 客户端 |
|
||||
@@ -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 节启用
|
||||
@@ -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
|
||||
```
|
||||
@@ -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 窗口同时工作
|
||||
- 窗口 1:spring-ai 目录,开发后端
|
||||
- 窗口 2:rui-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 DTO(dto)
|
||||
|
||||
| 类名 | 功能 | 说明 |
|
||||
|------|------|------|
|
||||
| `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 JsonUtil(JSON 处理)
|
||||
|
||||
```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 SpringUtil(Spring 上下文)
|
||||
|
||||
```java
|
||||
// 获取 Bean
|
||||
UserService service = SpringUtil.getBean(UserService.class);
|
||||
|
||||
// 获取配置
|
||||
String value = SpringUtil.getProperty("server.port");
|
||||
|
||||
// 判断环境
|
||||
boolean isDev = SpringUtil.isDev();
|
||||
boolean isProd = SpringUtil.isProd();
|
||||
```
|
||||
|
||||
### 3.7 BeanUtil(Bean 拷贝)
|
||||
|
||||
```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;补充文档使用说明 |
|
||||
@@ -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 等通知
|
||||
- ✅ **权限管理**:组织、团队、仓库级权限控制
|
||||
|
||||
---
|
||||
|
||||
## 二、部署方案
|
||||
|
||||
### 方案 A:Docker 部署(推荐)
|
||||
|
||||
适合:有 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
|
||||
|
||||
# 或者使用 snap(Ubuntu/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/CD(Gitea 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)。
|
||||
@@ -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~600MB(JVM 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 |
|
||||
@@ -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) 查找
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**MqPublisher(Provider 接口)**
|
||||
|
||||
```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, REDIS(MQTT 暂不需要) |
|
||||
| Action 枚举 | `Actions`(80+ 项) | `MqAction`(精简 9 项) |
|
||||
| 包名 | `org.rui.common.mq` | `com.rui.common.mq` |
|
||||
@@ -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 中写完整表名。
|
||||
@@ -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> | 高/中/低 | <措施> |
|
||||
@@ -0,0 +1,32 @@
|
||||
# <模块/功能名称> 实施计划
|
||||
|
||||
> **计划日期**: YYYY-MM-DD
|
||||
> **版本**: v1.0
|
||||
> **状态**: 待执行/执行中/已完成
|
||||
> **关联设计**: <链接到设计文档>
|
||||
|
||||
---
|
||||
|
||||
## 一、任务清单
|
||||
|
||||
### Phase 1: <阶段名称>
|
||||
|
||||
| 序号 | 任务 | 负责人 | 预估工时 | 状态 | 验证方式 |
|
||||
|------|------|--------|---------|------|---------|
|
||||
| 1.1 | <具体任务> | <负责人> | <工时> | ⬜ | <如何验证> |
|
||||
|
||||
---
|
||||
|
||||
## 二、进度跟踪
|
||||
|
||||
| 日期 | 完成任务 | 遇到的问题 | 解决方案 |
|
||||
|------|---------|-----------|---------|
|
||||
| YYYY-MM-DD | <任务> | <问题> | <方案> |
|
||||
|
||||
---
|
||||
|
||||
## 三、完成总结
|
||||
|
||||
- **实际工时**: <X 小时>
|
||||
- **偏差分析**: <与预估的差异及原因>
|
||||
- **经验教训**: <可复用的经验>
|
||||
@@ -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_ 开头)
|
||||
@@ -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,参数化构建不同系统
|
||||
Reference in New Issue
Block a user