迁移:包名 cn.g3soft.fmsapi → cn.g3soft.erp;artifactId/name fms-api → erp-api;启动类 FmsApiApplication → ErpApiApplication;配置前缀 fms.* → erp.*;FMS_JWT_SECRET → ERP_JWT_SECRET;未带入流程引擎(service/workflow、WorkflowController*)、数据库迁移脚本(config/migrations、tools/migration)与 data/files;一并迁入 Maven Wrapper、config/dbconfigs、config/legacy-tls.security、start.cmd。
重构:拆解 utils.DbUtils(1470 行)为 common.util.SqlText + database.{MetadataReader,RowReader,RowWriter} + database.model(5 个 record);认证专用账号查询移出访问层到 service.auth.UserAccountReader;新建 common/(ApiResponse + exception 6 + util 5 + util/snowflake 9);database/ 分 org/(多机构连接)与 model/(纯数据载体);JwtUtils 与客户端信息采集簇归入 service.auth。
清理:删除迁移带入的空目录、HELP.md,及 pom 中 Initializr 留下的空 url/licenses/developers/scm;修正 OrgDataSourceFactoryTests 的陈旧断言(停留在 2026-07 的旧策略,实现已于 2026-08 反转为继续重试、不停建连线程、不 failFast)。
验证:mvn test 80/80 通过;另有 2 个 @SpringBootTest 需真实 SQL Server。本次仅提交 server/;根 .gitignore、根 .gitattributes 与 docs/ 的同步改动不在本次范围内。
103 lines
5.1 KiB
Markdown
103 lines
5.1 KiB
Markdown
# server —— 后端
|
||
|
||
后端服务(Maven + Spring Boot)。现有代码是从原型 `fms-api` 迁入的元数据驱动引擎实现:
|
||
包名 `cn.g3soft.erp`,artifactId `erp-api`,启动类 `ErpApiApplication`。
|
||
|
||
构建 `mvnw.cmd test`;启动 `start.cmd`(`spring-boot:run`,默认 8088、context-path `/api`)。
|
||
|
||
> 迁入时**未带入**三项:流程引擎(`service/workflow`、`WorkflowController`、
|
||
> `WorkflowRuntimeController`)、数据库迁移脚本(`config/migrations`、`tools/migration`)、
|
||
> 本地文件存储数据(`data/files`)。
|
||
|
||
## 已定的技术栈
|
||
|
||
| 项 | 选型 |
|
||
|---|---|
|
||
| 框架 | Spring Boot 4.1.0 |
|
||
| 语言 | Java 21 |
|
||
| Web | spring-boot-starter-webmvc |
|
||
| 持久层 | spring-boot-starter-jdbc(**无 ORM**) |
|
||
| 连接池 | Druid 1.2.18 |
|
||
| 数据库 | **SQL Server**(mssql-jdbc) |
|
||
| 鉴权 | JJWT 0.12.6 |
|
||
| Excel | Apache Fesod 2.0.2-incubating |
|
||
| 其他 | ip2region、ua-parser |
|
||
| 构建 | Maven |
|
||
|
||
## IP 归属地
|
||
|
||
登录日志里的 IP 归属地走**本地 `ip2region` 库**,不联网。实测:加载 5~7ms,单次查询 1~4 微秒,
|
||
所以它不构成启动瓶颈(应用自身启动约 1.2~1.7s)。
|
||
|
||
> **部署须知**:`ip2region.xdb`(约 11MB)被 `.gitignore` 排除,不在仓库里,部署时必须手工放到
|
||
> `src/main/resources/`。漏放**不会导致启动失败**,只会让所有归属地静默变成「未知」。
|
||
|
||
内网 / 保留地址(RFC1918、回环、链路本地、IPv6 ULA)直接记「内网」,不外呼。
|
||
|
||
## 数据访问层(`cn.g3soft.erp.database`)
|
||
|
||
`database` 下分两块,**两者零互相引用**(一边管"连接怎么来",一边管"拿到连接后怎么读写"):
|
||
|
||
| 包 / 类 | 职责 |
|
||
|---|---|
|
||
| `database/`(根) | 通用数据访问,方法都走 JDBC、无 ORM |
|
||
| `database.MetadataReader` | 表结构内省:`loadTableMetadata` / `getColumn` / `describeColumns`;列名归一化也在这里 |
|
||
| `database.RowReader` | 只读查询与分页:`loadData` / `loadDataBySql` / `page`;唯一持有默认数据源 |
|
||
| `database.RowWriter` | 通用写入与条件更新语义:`insert` / `update` / `delete`、`$original` / `$expr` / guard、零行原因定位 |
|
||
| `database.model` | 纯数据载体,无任何逻辑:`TableMetadata` / `ColumnMetadata` / `PageResult` / `GuardCondition` / `ZeroRowCause` |
|
||
| `database.org` | 多机构数据库连接:`OrgContext`(请求级机构/用户上下文)、`OrgDatabaseProperties` / `OrgDatabaseConfig*`(配置与热加载)、`OrgDataSourceFactory` / `OrgDataSourceManager` / `OrgRoutingDataSource`(装配与路由) |
|
||
| `common.util.SqlText` | SQL 文本与安全校验:标识符校验与加引号、只读 SQL 校验、排序子句构建;纯静态 |
|
||
|
||
三条约定:
|
||
|
||
1. `SqlText` 不依赖任何人,所以依赖方向固定为 `service → database → common`,不成环。
|
||
2. `RowReader` 之外的方法都接收调用方传入的 `Connection`(不自己开事务),事务边界留在服务层。
|
||
3. 认证要按 `orgId` 取该机构连接(登录时机构上下文尚未建立),所以那条查询在
|
||
`service.auth.UserAccountReader` 里,**不在**访问层。
|
||
|
||
## 分割现状与规划
|
||
|
||
**现状**(迁入的原型代码,已按职责归过一轮):
|
||
|
||
```
|
||
cn.g3soft.erp
|
||
├── common/ # 通用返回、异常、纯静态工具
|
||
│ ├── ApiResponse.java # HTTP 响应外壳 + 全部错误码常量
|
||
│ ├── exception/ # 6 个异常与 GlobalExceptionHandler
|
||
│ └── util/ # ParamUtils / SqlText / StringUtils / TimeUtils /
|
||
│ # UuidV7Generator + snowflake/(内嵌的雪花 ID 生成器)
|
||
├── config/ # AuthProperties / FileStorageProperties / JacksonConfig / JwtAuthFilter
|
||
├── controller/ # 6 个 Controller
|
||
├── database/ # 数据访问层(见上):RowReader / RowWriter / MetadataReader + model/ + org/
|
||
└── service/
|
||
├── auth/ # 登录、会话、登录日志、JwtUtils,以及 client/(客户端信息采集)
|
||
├── data/ # 通用保存 / 删除 / 查询编排
|
||
├── export/ file/ formula/ permission/
|
||
```
|
||
|
||
**规划**(新代码按此落位,老代码后续按域重构):
|
||
|
||
```
|
||
cn.g3soft.erp
|
||
├── common/ # 通用返回、异常、工具 —— 已建
|
||
├── engine/ # 元数据驱动引擎(查询 / 保存 / 权限)—— 未设计;
|
||
│ # 现在的 database + service/{data,formula,permission} 是它的雏形
|
||
└── module/
|
||
├── ocean/ # 海运
|
||
├── air/ # 空运
|
||
├── container/ # 箱管
|
||
├── finance/ # 财务
|
||
├── crm/ # CRM
|
||
└── system/ # 系统管理
|
||
```
|
||
|
||
## 与前端的关系
|
||
|
||
**唯一契约是 HTTP 接口。** 前端不读后端源码,后端不管前端怎么调。
|
||
|
||
同仓的三条纪律(见根 `README.md`):
|
||
|
||
1. **独立构建** —— `mvn package` 不触发前端构建,反之亦然
|
||
2. **独立版本** —— tag 分开打(`api-v1.2.0` / `web-v1.2.0`)
|
||
3. **接口契约写进 `docs/`** 并版本化
|