Files
workspace/code/g3soft-erp/server/README.md
T
oneao ae3ca3cfa1 feat(server): 后端从原型 fms-api 迁入并重整包结构
迁移:包名 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/ 的同步改动不在本次范围内。
2026-10-12 20:04:52 +08:00

5.1 KiB
Raw Blame History

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 库,不联网。实测:加载 57ms,单次查询 14 微秒, 所以它不构成启动瓶颈(应用自身启动约 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/ 并版本化