# eams-control **Repository Path**: lssjzmn/eams-control ## Basic Information - **Project Name**: eams-control - **Description**: 企业资产全生命周期管理系统后端 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-09-22 - **Last Updated**: 2026-11-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: eams ## README # EAMS 后端(eams-control) EAMS 资产管理系统的后端(Spring Boot 单体)。它负责业务数据与权限,并且在标签打印链路里承担一个**边界清晰**的角色:**只产出「单张标签的指令片段」,不碰打印机**。 - 前端:`../eams-ui`(Vite dev :3087,走 `/api` → 本服务 `/eams`) - 打印工具:`../eams-print-server`(Python 桌面客户端,**与前端并列**的第二个打印发起端) - 打印落地:**本机 TSC PrintServer**(默认 `ws://127.0.0.1:8888`)——由前端或工具去连,**本服务不连** --- ## 一、架构 ``` 前端 /api/* ──► eams-control :8087 /eams/* ──► MySQL 8(业务+参数) │ │ ├─► Redis(token 会话 / bd_param 缓存 / 验证码计数) │ │ ├─► MinIO :9090(附件) │ │ └─► XXL-JOB(admin :8058,执行器 :9087,如每日折旧) │ └─ 自研权限:注解 + 拦截器 + 切面(权限串如 asset:list) └─ 标签打印:只产出指令片段 JSON(含最终版面坐标),不含 openport/closeport ``` 包结构(**两套并行组织**,新代码按业务域放 `modules/`): | 位置 | 内容 | |---|---| | `modules/<域>/` | `entity` / `mapper` / `dto` / `vo`:asset、basic(Bd\*)、check、repair、scrap、transfer、stats、sys、todo | | `controller/` `service/` | 19 个控制器 + 18 个服务:auth、file、health 等横切能力与部分聚合接口 | | `common/` | `result`(`Result`/`PageResult`)、`enums`、`constant`、`model` | | `config/` `aspect/` `annotation/` `interceptor/` `exception/` `utils/` `job/` | 配置、权限切面、操作日志注解、全局异常、工具、定时任务 | 技术栈:JDK **1.8**(强约束)· Spring Boot **2.7.18** · MyBatis 2.1.3 + PageHelper 5.1.10(**不是** MyBatis-Plus)· MySQL 8 · Redis(Lettuce) · MinIO 8.5 · XXL-JOB 2.5 · EasyExcel · Hutool。 配置:`src/main/resources/application.properties`(开发默认)+ `application-prod.properties`(`--spring.profiles.active=prod`,敏感值走 `${ENV:默认值}`)。 **业务参数不在配置文件里**,而在数据库表 `bd_param`(「基础数据 > 参数管理」页面维护)。 ## 二、核心功能 - **认证授权**:登录换 token(Redis)、当前用户信息、菜单树、角色与权限串、操作日志(`@OperationLog`)。 - **基础数据**:部门、资产分类、字典、**参数 `bd_param`**(含标签打印的 `label.*` 共 30+ 项)。 - **资产台账**:列表/详情/履历历史、二维码数据源 `AssetQrcodeVO`、Excel 导入导出。 - **业务闭环**:调拨申请、维修、报废、盘点(任务/报告)、统计大屏数据、待办。 - **标签打印指令产出** + **二维码内容三形态与扫码接口**(见下节)。 - **健康检查**:`GET /health`(浅)与 `GET /health/ready`(深:逐项探 MySQL/Redis,异常返回 503 与 `costMs`)。 ## 三、标签打印:指令产出与二维码内容(本服务只到这里为止) ### 3.1 指令片段:`service/LabelPrintService.java` 它做的事: 1. 读参数:`bd_param` 的 `label.*`(`dpi/width_mm/height_mm/speed/density/sensor/gap_*/direction/qr_x/qr_cell/qr_fit_height/qr_ecc/qr_multiline/font_face/font_height/title/title_height/title_top/qr_text_gap/line_gap/max_batch/timeout_sec` …)。 2. 产出**单张标签的指令片段**(`LabelCommandItemVO.commands`),顺序固定: `clearbuffer → setup → DIRECTION → CODEPAGE UTF-8 → [标题 windowsfont] → QRCODE → [文字 windowsfont ×N] → printlabel 1,1` —— **不含 `openport_*` / `closeport`**(打印机由客户端决定,同一份指令可用不同打印机重放)。 3. 回传**最终生效版面坐标**(`LabelLayoutVO`:标题/二维码/每行文字的 x、y、字号、cell…),前端预览直接照抄,保证「预览即所打」。 4. 留痕:前端打印后回调写二维码履历。 对外接口(均在 `/asset` 下,权限 `asset:list`): | 接口 | 用途 | |---|---| | `GET /asset/label/params` | 回传当前生效的 `label.*` 参数(含 `wsUrl`、`ecc`、`maxBatch`、`timeoutSec` …) | | `POST /asset/label/commands` | 入参 `items[{assetId, serialNo, qrText, qrModules?}]`,出参每项的 `commands` + `layout` | ### 3.2 二维码内容三形态与扫码接口(2026-10-31) 标签上的二维码原来只写 9 项文字(资产改了旧标签就过时)。现由参数 `label.qr_mode` 切换三种形态: | 形态 | 二维码里写什么 | 数据新鲜度 | |---|---|---| | `simple`(默认) | 9 项文字(与改造前逐字一致) | 打码那一刻的快照 | | `api` | `{label.qr_base_url}/open/qr/text/{编码}?pwd={label.qr_password}` | 每次扫码读**实时** | | `html` | `{label.qr_base_url}/open/qr/html/{编码}?pwd=…` | 每次扫码读**实时**,且是排版页 | 配套参数(「基础数据 > 参数管理」维护,改后清 `EAMS:CACHE:param` 即生效): | 参数 | 默认 | 说明 | |---|---|---| | `label.qr_mode` | `simple` | `simple` / `api` / `html` | | `label.qr_base_url` | `http://<公网IP>:3087/api` | 手机可达的服务前缀(**不含** `/open/qr`);**留空强制回退 simple**,避免打出扫不开的死链 | | `label.qr_password` | `AIF09F1B910007B8B21C70` | 扫码 URL 的 `pwd`,改后旧标签失效 | 三个**免登录**接口(白名单 `/open/qr/**`,靠 `pwd` 校验;错误按同类类型返回,不吐 JSON): | 接口 | 响应 | 说明 | |---|---|---| | `GET /open/qr/text/{assetCode}?pwd=` | `text/plain` | 9 行纯文本,字段与 `simple` 完全一致 | | `GET /open/qr/html/{assetCode}?pwd=` | `text/html` | 资产详情全量字段(4 组 23 行、斑马条纹、`label.title` 作标题与页脚)+ 资产图片 + 最近 20 条履历 | | `GET /open/qr/image/{assetCode}?pwd=` | 图片字节流 | 只按该资产的 `image_url` 取对象(不接受任意 objectName),后端中转、不暴露 MinIO 地址 | 配置下发与取数: | 接口 | 鉴权 | 用途 | |---|---|---| | `GET /asset/qrcode/config` | 需登录 | 前端拼 URL 用:`{mode, baseUrl, password}` | | `POST /open/print/asset` | 免登录 + `eams.print.api-password` | 桌面打印工具取数;响应额外带 `labelTitle / qrMode / qrBaseUrl / qrPassword`,工具据此决定码里写文字还是写 URL | - **唯一格式化实现**:`service/QrCodeContentService.java` —— 9 项顺序、空值 `-`、金额两位小数、日期 `yyyy-MM-dd`, 与前端 `qrcodeContent.ts`、工具 `model.py` **逐字一致**;`api` 文本用真实换行(simple 因 TSPL 引号串才压成分号)。 - 兼容:`simple`(默认)下行为与改造前**完全一致**;已打印的旧标签不会变。 ### 3.3 版面与内容口径(**权威来源**) | 项 | 口径 | |---|---| | 右侧文字 | **4 项 / 6 行**:`资产编码` / `资产名称` / `所属部门` / `使用人`;**前 2 项字段名一行 + 值一行,后 2 项只打值**(即第 5 行是部门名、第 6 行是使用人);值超宽**换行不截断**;空值 `-` | | 二维码正文 | 由**前端/工具**按 `label.qr_mode` 生成(`simple` 为 9 项文字,`api`/`html` 为查询 URL);本服务只做 TSPL 归一化(`\n` → `"; "`、双引号→单引号);模块数由前端用 `qrcode` 库算出 `qrModules` 一并提交。`api`/`html` 接口侧由 `QrCodeContentService` 产出同口径文本 | | 标题 | 固定**顶部居中单行**;字号 `title_height`、顶端留白 `title_top`,过宽先缩字号再截断 | | 二维码 | `QRCODE x,y,ECC,cell,A,0,M2,"…"`;`cell` 以参数为**上限**并按正文区高度自动封顶(绝不溢出) | | 静区 | `qr_x` 与 `qr_text_gap` 同时是静区;低于 `4 × cell` 只记 warn,**不擅自替用户改值** | ## 四、与前端、打印工具的关系与约束 | 关系 | 事实 | 约束 | |---|---|---| | 前端 → 本服务 | `/api/*` →(dev 代理 / nginx 重写)`/eams/*`;`Authorization: Bearer`;统一 `Result{code,message,data}`,前端直接拿 `data` | Controller **只用 GET/POST**;分页 `page/size` → `{total,list}`;数组参数 `arr.join(',')` | | 本服务 → 打印服务 | **没有任何连接** | 打印落地由客户端负责;本服务不得引入打印相关 IO/依赖 | | 本服务 ⇄ 打印工具 | **不通信、不共享进程**,只共享**指令契约** | 工具的 `app/webcompat.py` 是本类 `buildItem()` 的**逐条移植** ⇒ **改版面/内容/指令序列必须两侧同步**;契约以工具侧 `docs/PROTOCOL.md` §3.1/§5 为对照文档 | | 内容口径单一来源 | 二维码 9 项:前端 `qrcodeContent.ts` 为 Web 侧唯一来源,后端 `QrCodeContentService` 为接口侧唯一来源;右侧 **4 项**在本类 `LINE_LABELS` | 改字段清单要同时改:前端 `FIELD_ORDER` → 本类 `LINE_LABELS` + `QrCodeContentService.LABELS` → 工具 `app/model.py` 的两个常量 | | 扫码接口(2026-10-31) | `/open/qr/text`、`/open/qr/html`、`/open/qr/image` 三个接口免登录,仅靠 `label.qr_password` 把关;`/asset/qrcode/config` 需登录 | 口令错 401、资产不存在 404,错误按 `text/plain` / `text/html` 返回;换口令后**已打印的旧标签会失效**(无新旧口令宽限) | | 两处刻意差异 | 只在工具侧:标题按**粗体**下发(`style=2`)、`DIRECTION` 默认 **1** | 不要"顺手对齐"回去;已在工具 PROTOCOL §3.1 标注 | | 参数分叉 | Web 侧参数在本服务 `bd_param.label.*`;工具用自己的 `config.json`(`print` 段 + 每打印机 `profiles`),默认值对齐但**独立维护** | 两边改参数互不影响;现场以各自界面为准 | | 版本号 | `eams.app-version`(application.properties)= pom `` = `eams-ui/package.json` | **三处必须同步** | ## 五、开发与构建 ```bash mvn spring-boot:run # :8087 /eams mvn clean package # → target/eams-control.jar java -jar target/eams-control.jar --spring.profiles.active=prod ``` 启动依赖顺序:MySQL(3306) → Redis(6379) → MinIO(9090) → XXL-JOB admin(8058) → 本服务(8087);前端 dev 代理指向 8087。 数据库变更约定(`src/main/resources/db/`): - `init.sql`:全量建表 + 参数种子(新增参数必须同时改这里)。 - `alter/YYYY-MM-DD_NN_简短描述.sql`:**当天序号从 01 起、按文件名字典序执行、必须幂等**(MySQL 8 没有 `ADD COLUMN IF NOT EXISTS`,用 `information_schema`+`PREPARE` 或 `ON DUPLICATE KEY UPDATE`);禁止 `DROP DATABASE` / `TRUNCATE` 生产表;每批改动同步写 `CHANGELOG.md`。 ## 六、目录速查 ``` src/main/java/com/lssjzmn/eams/ ├── EamsApplication.java ├── modules/{asset,basic,check,repair,scrap,transfer,stats,sys,todo}/… # 按业务域 ├── controller/ service/ dto/ vo/ # 聚合接口与横切能力 ├── common/{result,enums,constant,model}/ ├── config/ aspect/ annotation/ interceptor/ exception/ utils/ job/ ├── service/LabelPrintService.java ★ 标签指令片段的唯一产出口 ├── service/QrCodeContentService.java ★ 二维码 9 项/HTML 页的唯一格式化实现(api/html 接口用) ├── controller/OpenQrController.java ★ 扫码接口(/open/qr/text|html|image,免登录 + 口令) └── controller/OpenPrintController.java ★ 桌面工具取数(/open/print/asset,免登录 + 口令) src/main/resources/ ├── application.properties / application-prod.properties / logback-spring.xml └── db/{init.sql, alter/} ``` ## 七、文档索引 | 文档 | 内容 | |---|---| | `CHANGELOG.md` | 后端变更记录(含口径与回退历史) | | `deploy/DEPLOY.md` | 部署步骤与检查清单 | | `src/main/resources/db/alter/README.md` | DDL 增量脚本约定 | | `../eams-ui/README.md` | 前端架构与接口约定 | | `../eams-print-server/README.md` | 打印工具(并列的第二个打印发起端) | | `../标签打印方案_TSC-PrintServer.md` | 标签打印方案(跨三者的总体设计) |