material_manage_system/docs/项目后端结构说明.md
文睿 张 062abcb3d0 物料备注放宽到 500 字,补回被截断的两条备注
- scripts/init.sql:materials.remark 由 VARCHAR(200) 改为 VARCHAR(500);并从
  源 SQLite 库补回武汉 B010059、B010076 被截断的完整备注(222 字,此前只落库
  前 200 字,结尾断在 &upStre)
- 后端:maxMaterialRemarkLength 与错误文案改为 500,Excel 模板填写说明同步
- 前端:物料表单备注输入框 maxlength 与提交前校验改为 500
- 文档:api.md、数据库表结构与关系说明、后端结构说明同步;并补充说明默认用户
  由后端启动时创建,重建数据库后重启后端即可恢复
2026-10-14 17:05:37 +08:00

89 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 项目后端结构说明
本文介绍仓库的整体布局,重点是后端 `internal/` 的分层职责:每个文件负责什么、一次请求经过哪些环节。配套阅读:《[数据库表结构与关系说明](数据库表结构与关系说明.md)》《[API 文档](api.md)》。
## 仓库顶层布局
| 目录/文件 | 说明 |
| --- | --- |
| `cmd/server/` | 后端入口 `main.go`:读 DSN、连库、建默认用户、校验 schema、注册路由并启动 |
| `internal/` | 后端内核,见下文分层说明 |
| `pkg/` | 通用基础包:`database`(连接与 DSN 规范化)、`response`(统一响应格式与状态码封装) |
| `frontend/` | 前端源码与构建产物(`frontend/dist` 为后端静态托管的目录) |
| `scripts/` | `init.sql`、增量升级 SQL 与校验脚本 |
| `docs/` | 项目文档 |
上表提到的「建默认用户」,指 `main.go` 启动时调用 `repository.CreateDefaultUsers()`:幂等创建 `admin`/`user`(`admin` 已存在就跳过),并绑定 `super_admin` / `warehouse_admin` 角色。这两个账号**不在 `scripts/init.sql` 里**——该脚本只建 `users` 表、不插入任何用户数据——因此数据库重建后**重启后端即可恢复默认账号**,不必手工执行建用户的 SQL;反过来,改过的密码或另建的账号不会随重建回来。
## 整体
`cmd/server/main.go` 是入口(注册路由、连库、校验 schema),`internal/` 只是内核。一次请求的链路是:
```
middleware(认证/公司校验)→ handler(绑参、回响应)→ service(业务规则、校验、组装)→ repository(纯 SQL)→ model(结构体)
```
响应统一走 `pkg/response`,连库在 `pkg/database`。
## handler/(HTTP 层)
`api.go` 是大头(1604 行)。
| 文件 | 职责 |
| --- | --- |
| [api.go](../internal/handler/api.go) | `Handler` 聚合全部 service;`RegisterRoutes` 定义所有路由与中间件挂载;物料、出入库、库存、用户、申请等主体 handler |
| [admin_master.go](../internal/handler/admin_master.go) | 管理端:公司、角色、权限、物料分类、单位、编码规则、往来单位、仓库/库位,另含 `organizationID` / `pathID` / `bind` 三个公共小工具 |
| [organization_context.go](../internal/handler/organization_context.go) | 公司切换器:列出可访问公司、切换当前公司上下文 |
| [upload.go](../internal/handler/upload.go) | 图片上传 |
| [websocket.go](../internal/handler/websocket.go) | 库存变更实时推送的 Hub、Client、广播,以及 `/ws` 握手(凭一次性票据 + 实时复核授权) |
## middleware/(3 个)
| 文件 | 职责 |
| --- | --- |
| [auth.go](../internal/middleware/auth.go) | JWT 解析、`AdminOnly`、`OptionalAuth`,以及取 userID / username / roles 的辅助函数 |
| [organization.go](../internal/middleware/organization.go) | 校验 `X-Organization-ID` 头并确认该用户能访问这家公司,通过后写进 context |
| [rate_limiter.go](../internal/middleware/rate_limiter.go) | 登录失败次数限流 + 定期清理 |
## service/(业务规则)
| 文件 | 职责 |
| --- | --- |
| [material.go](../internal/service/material.go) | 物料增改查、作废/恢复 |
| [inbound.go](../internal/service/inbound.go) / [outbound.go](../internal/service/outbound.go) | 出入库单的业务校验与库存联动 |
| [stock.go](../internal/service/stock.go) | 库存查询、预警分类、变动量计算 |
| [inventory_scope.go](../internal/service/inventory_scope.go) | 公司维度的仓库/库位解析:库存读写一律由服务端从库里推导,不收前端传的 ID;写操作要求启用状态 |
| [material_log.go](../internal/service/material_log.go) | 物料变更日志的 diff 生成与中文描述 |
| [application.go](../internal/service/application.go) | 物料申请单的提交/审批/驳回/撤销 |
| [auth.go](../internal/service/auth.go) | 登录、签发与校验 token session、密码哈希 |
| [admin_master.go](../internal/service/admin_master.go) | 公司、主数据(分类/单位/编码规则)、往来单位、仓库四组服务 |
| [organization_access.go](../internal/service/organization_access.go) | 公司可访问范围与切换 |
| [websocket_ticket.go](../internal/service/websocket_ticket.go) | WebSocket 一次性票据的签发/消费 |
| [errors.go](../internal/service/errors.go) | 共享哨兵错误(库存不足、公司维度不可用等),供 handler 映射状态码 |
## repository/(纯 SQL)
| 文件 | 职责 |
| --- | --- |
| [material.go](../internal/repository/material.go) | 物料,含事务版新增(锁编码规则、判重)/更新/作废/恢复 |
| [inbound.go](../internal/repository/inbound.go) / [outbound.go](../internal/repository/outbound.go) | 出入库单及明细读写,共用 `orderQuerier` 接口 |
| [stock.go](../internal/repository/stock.go) | 库存余额、流水、预警规则,含 upsert |
| [master_data.go](../internal/repository/master_data.go) | 分类/单位/编码规则 |
| [material_log.go](../internal/repository/material_log.go) | 物料日志落库与查询 |
| [warehouse.go](../internal/repository/warehouse.go) | 仓库/库位,含 `Summary`(按公司取唯一仓库 + 库位) |
| [partner.go](../internal/repository/partner.go) | 往来单位 |
| [user.go](../internal/repository/user.go) | 用户与角色,含保护最后一个超管、建默认用户/表 |
| [user_organization.go](../internal/repository/user_organization.go) | 用户-公司关联的替换与批量加载 |
| [organization.go](../internal/repository/organization.go) / [organization_access.go](../internal/repository/organization_access.go) | 公司基础 CRUD;`ListAccessible` / `CanAccess` 的授权判定(每次查库,不依赖 JWT 里的角色快照) |
| [organization_audit.go](../internal/repository/organization_audit.go) | 公司相关操作的审计日志写入 |
| [application.go](../internal/repository/application.go) | 申请单及明细 |
| [update.go](../internal/repository/update.go) | `execExpectingRow`:UPDATE 后校验影响行数,避免"0 行也当成功" |
## model/(数据结构,无逻辑)
[model.go](../internal/model/model.go) 集中了大部分结构体(物料、出入库单与明细、库存与流水、用户、申请单,以及各类 Request/Response);后来又按业务域另拆出 `inventory.go`、`master_data.go`、`organization.go`、`partner.go`、`warehouse.go` 五个文件。
## testutil/
`fixtures.go`、`isolation.go`、`testdb.go` 是测试辅助包,不属于业务代码。