docs: 完善单位转换系统使用文档,添加Feign跨模块调用示例
- 补充Feign客户端接口定义示例 - 补充跨服务调用的具体实现示例 - 修正常见问题Q1中的API端点路径为正确的 /unt-info/page 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
package com.zt.plat.module.base.service.doctemplate;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档渲染API服务
|
||||||
|
* 供业务模块调用,支持多种渲染方式
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
*/
|
||||||
|
public interface DocumentRenderApiService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据模板ID渲染 (方式1:直接模板渲染)
|
||||||
|
*
|
||||||
|
* @param templateId 模板ID
|
||||||
|
* @param dataMap 数据Map
|
||||||
|
* @return 渲染后的HTML
|
||||||
|
*/
|
||||||
|
String renderByTemplate(Long templateId, Map<String, Object> dataMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据实例ID渲染 (方式2:实例渲染)
|
||||||
|
* 优先使用实例的editedContent,如果为空则使用模板内容
|
||||||
|
*
|
||||||
|
* @param instanceId 实例ID
|
||||||
|
* @param dataMap 数据Map
|
||||||
|
* @return 渲染后的HTML
|
||||||
|
*/
|
||||||
|
String renderByInstance(Long instanceId, Map<String, Object> dataMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据业务类型渲染 (方式3:业务接入渲染)
|
||||||
|
* 业务系统可根据业务类型自定义数据集和渲染逻辑
|
||||||
|
*
|
||||||
|
* @param instanceId 实例ID
|
||||||
|
* @param businessType 业务类型 (如: 'PURCHASE_ORDER', 'SALES_ORDER' 等)
|
||||||
|
* @param businessDataMap 业务数据Map (由业务系统自己组织)
|
||||||
|
* @return 渲染后的HTML
|
||||||
|
*/
|
||||||
|
String renderByBusinessType(Long instanceId, String businessType, Map<String, Object> businessDataMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据直接内容渲染 (方式4:前端预览)
|
||||||
|
* 用于前端编辑时的实时预览,使用标签默认值
|
||||||
|
*
|
||||||
|
* @param content 模板内容 (HTML/Velocity语法)
|
||||||
|
* @param dataMap 数据Map (标签默认值)
|
||||||
|
* @return 渲染后的HTML
|
||||||
|
*/
|
||||||
|
String renderByContent(String content, Map<String, Object> dataMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将HTML导出为Word文档
|
||||||
|
*
|
||||||
|
* @param html HTML内容
|
||||||
|
* @param fileName 文件名 (不需要后缀,自动添加.docx)
|
||||||
|
* @return Word文件字节数组
|
||||||
|
*/
|
||||||
|
byte[] exportToWord(String html, String fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染并导出为Word (一步完成)
|
||||||
|
*
|
||||||
|
* @param instanceId 实例ID
|
||||||
|
* @param dataMap 数据Map
|
||||||
|
* @param fileName 导出文件名
|
||||||
|
* @return Word文件字节数组
|
||||||
|
*/
|
||||||
|
byte[] renderAndExportToWord(Long instanceId, Map<String, Object> dataMap, String fileName);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
package com.zt.plat.module.base.service.doctemplate;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.zt.plat.module.base.dal.dataobject.doctemplate.DocTemplateDO;
|
||||||
|
import com.zt.plat.module.base.dal.dataobject.doctemplate.DocTemplateInstanceDO;
|
||||||
|
import com.zt.plat.module.base.dal.dao.doctemplate.DocTemplateMapper;
|
||||||
|
import com.zt.plat.module.base.dal.dao.doctemplate.DocTemplateInstanceMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.docx4j.Docx4J;
|
||||||
|
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||||
|
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
|
||||||
|
import org.docx4j.wml.*;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static com.zt.plat.module.base.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档渲染API服务实现类
|
||||||
|
* 使用 docx4j 库处理 Word 导出,支持多种渲染方式
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class DocumentRenderApiServiceImpl implements DocumentRenderApiService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DocTemplateRenderService templateRenderService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DocTemplateMapper templateMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DocTemplateInstanceMapper instanceMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderByTemplate(Long templateId, Map<String, Object> dataMap) {
|
||||||
|
if (templateId == null) {
|
||||||
|
throw new IllegalArgumentException("模板ID不能为空");
|
||||||
|
}
|
||||||
|
DocTemplateDO template = templateMapper.selectById(templateId);
|
||||||
|
if (template == null) {
|
||||||
|
throw exception(TEMPLATE_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
return templateRenderService.render(templateId, null, null, dataMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderByInstance(Long instanceId, Map<String, Object> dataMap) {
|
||||||
|
if (instanceId == null) {
|
||||||
|
throw new IllegalArgumentException("实例ID不能为空");
|
||||||
|
}
|
||||||
|
DocTemplateInstanceDO instance = instanceMapper.selectById(instanceId);
|
||||||
|
if (instance == null) {
|
||||||
|
throw exception(TEMPLATE_INSTANCE_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
return templateRenderService.render(null, instanceId, null, dataMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderByBusinessType(Long instanceId, String businessType, Map<String, Object> businessDataMap) {
|
||||||
|
if (instanceId == null) {
|
||||||
|
throw new IllegalArgumentException("实例ID不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(businessType)) {
|
||||||
|
throw new IllegalArgumentException("业务类型不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取实例信息
|
||||||
|
DocTemplateInstanceDO instance = instanceMapper.selectById(instanceId);
|
||||||
|
if (instance == null) {
|
||||||
|
throw exception(TEMPLATE_INSTANCE_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务系统自己组织的dataMap,可以包含SQL查询结果、业务数据等
|
||||||
|
// 直接使用该dataMap进行渲染
|
||||||
|
if (businessDataMap == null || businessDataMap.isEmpty()) {
|
||||||
|
log.warn("业务数据集为空,instanceId: {}, businessType: {}", instanceId, businessType);
|
||||||
|
businessDataMap = Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return templateRenderService.render(null, instanceId, null, businessDataMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderByContent(String content, Map<String, Object> dataMap) {
|
||||||
|
if (StrUtil.isBlank(content)) {
|
||||||
|
throw new IllegalArgumentException("模板内容不能为空");
|
||||||
|
}
|
||||||
|
return templateRenderService.render(null, null, content, dataMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] exportToWord(String html, String fileName) {
|
||||||
|
if (StrUtil.isBlank(html)) {
|
||||||
|
throw new IllegalArgumentException("HTML内容不能为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
fileName = "document";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建 Word 文档
|
||||||
|
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
|
||||||
|
MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
|
||||||
|
|
||||||
|
// 解析 HTML
|
||||||
|
Document htmlDoc = Jsoup.parse(html);
|
||||||
|
|
||||||
|
// 处理 HTML 内容并添加到 Word 文档
|
||||||
|
processHtmlToWord(mainDocumentPart, htmlDoc.body());
|
||||||
|
|
||||||
|
// 转换为字节数组
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
wordPackage.save(baos);
|
||||||
|
|
||||||
|
log.info("Word导出成功,文件名: {}.docx, 大小: {} bytes", fileName, baos.size());
|
||||||
|
return baos.toByteArray();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Word导出失败,fileName: {}", fileName, e);
|
||||||
|
throw new RuntimeException("Word导出失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] renderAndExportToWord(Long instanceId, Map<String, Object> dataMap, String fileName) {
|
||||||
|
// 先渲染获取HTML
|
||||||
|
String html = renderByInstance(instanceId, dataMap);
|
||||||
|
// 再导出为Word
|
||||||
|
return exportToWord(html, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 HTML 转换为 Word 文档内容
|
||||||
|
* 递归处理 HTML 节点,使用 docx4j API 构建完整的 Word 文档
|
||||||
|
*/
|
||||||
|
private void processHtmlToWord(MainDocumentPart mainDocumentPart, Element element) throws Exception {
|
||||||
|
for (Node node : element.childNodes()) {
|
||||||
|
if (node instanceof TextNode) {
|
||||||
|
String text = ((TextNode) node).getWholeText();
|
||||||
|
if (StrUtil.isNotBlank(text.trim())) {
|
||||||
|
mainDocumentPart.addStyledParagraphOfText("Normal", text.trim());
|
||||||
|
}
|
||||||
|
} else if (node instanceof Element) {
|
||||||
|
Element element1 = (Element) node;
|
||||||
|
String tagName = element1.tagName().toLowerCase();
|
||||||
|
|
||||||
|
switch (tagName) {
|
||||||
|
case "h1", "h2", "h3", "h4", "h5", "h6" -> {
|
||||||
|
String styleId = "Heading" + tagName.substring(1);
|
||||||
|
mainDocumentPart.addStyledParagraphOfText(styleId, element1.text());
|
||||||
|
}
|
||||||
|
case "p" -> {
|
||||||
|
mainDocumentPart.addStyledParagraphOfText("Normal", element1.text());
|
||||||
|
}
|
||||||
|
case "br" -> {
|
||||||
|
mainDocumentPart.addParagraphOfText("");
|
||||||
|
}
|
||||||
|
case "table" -> {
|
||||||
|
processTable(mainDocumentPart, (Element) node);
|
||||||
|
}
|
||||||
|
case "ul", "ol" -> {
|
||||||
|
processHtmlToWord(mainDocumentPart, element1);
|
||||||
|
}
|
||||||
|
case "li" -> {
|
||||||
|
mainDocumentPart.addParagraphOfText("• " + element1.text());
|
||||||
|
}
|
||||||
|
case "strong", "b" -> {
|
||||||
|
mainDocumentPart.addStyledParagraphOfText("Normal", element1.text());
|
||||||
|
}
|
||||||
|
case "i", "em" -> {
|
||||||
|
mainDocumentPart.addStyledParagraphOfText("Normal", element1.text());
|
||||||
|
}
|
||||||
|
case "div", "section", "article" -> {
|
||||||
|
processHtmlToWord(mainDocumentPart, element1);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
processHtmlToWord(mainDocumentPart, element1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格
|
||||||
|
* 使用 docx4j 的 Table 和 Tbl API 创建 Word 表格
|
||||||
|
*/
|
||||||
|
private void processTable(MainDocumentPart mainDocumentPart, Element tableElement) throws Exception {
|
||||||
|
Elements rows = tableElement.select("tr");
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectFactory factory = new ObjectFactory();
|
||||||
|
Tbl tbl = factory.createTbl();
|
||||||
|
|
||||||
|
// 设置表格属性
|
||||||
|
TblPr tblPr = factory.createTblPr();
|
||||||
|
tbl.setTblPr(tblPr);
|
||||||
|
|
||||||
|
// 处理每一行
|
||||||
|
for (Element row : rows) {
|
||||||
|
Elements cells = row.select("td, th");
|
||||||
|
Tr tr = factory.createTr();
|
||||||
|
|
||||||
|
for (Element cell : cells) {
|
||||||
|
Tc tc = factory.createTc();
|
||||||
|
TcPr tcPr = factory.createTcPr();
|
||||||
|
tc.setTcPr(tcPr);
|
||||||
|
|
||||||
|
// 添加单元格内容
|
||||||
|
P cellParagraph = factory.createP();
|
||||||
|
R cellRun = factory.createR();
|
||||||
|
Text cellText = factory.createText();
|
||||||
|
cellText.setValue(cell.text());
|
||||||
|
cellRun.getContent().add(cellText);
|
||||||
|
cellParagraph.getContent().add(cellRun);
|
||||||
|
tc.getContent().add(cellParagraph);
|
||||||
|
|
||||||
|
tr.getContent().add(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbl.getContent().add(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将表格添加到文档
|
||||||
|
mainDocumentPart.getContent().add(tbl);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("表格处理失败,跳过表格内容", e);
|
||||||
|
// 降级处理:将表格内容作为文本添加
|
||||||
|
for (Element row : rows) {
|
||||||
|
Elements cells = row.select("td, th");
|
||||||
|
StringBuilder rowText = new StringBuilder();
|
||||||
|
for (Element cell : cells) {
|
||||||
|
rowText.append(cell.text()).append(" | ");
|
||||||
|
}
|
||||||
|
mainDocumentPart.addParagraphOfText(rowText.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
224
单位转换系统使用文档.md
Normal file
224
单位转换系统使用文档.md
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# 单位转换系统业务使用文档
|
||||||
|
|
||||||
|
## 一、系统概述
|
||||||
|
|
||||||
|
单位转换系统提供统一的计量单位转换服务,支持同一量纲内的单位自动转换。采用**单向配置、双向生效**机制,只需配置"非基准单位 → 基准单位"的转换规则,系统自动推导反向和间接转换。
|
||||||
|
|
||||||
|
**核心特性**:
|
||||||
|
- 单向配置、双向生效的转换机制
|
||||||
|
- 支持按单位ID、符号、名称进行转换
|
||||||
|
- 高精度计算,支持批量操作
|
||||||
|
- 跨模块统一服务
|
||||||
|
|
||||||
|
## 二、内容配置
|
||||||
|
|
||||||
|
### 2.1 管理菜单路径
|
||||||
|
后台管理 → 基础管理 → 计量单位 → 计量单位管理
|
||||||
|
|
||||||
|
### 2.2 配置功能
|
||||||
|
|
||||||
|
#### 计量量纲管理
|
||||||
|
- **功能**:创建和管理不同的量纲类型(如重量、长度、体积等)
|
||||||
|
- **操作**:新增量纲、编辑量纲信息、删除量纲
|
||||||
|
- **每个量纲只能设置一个基准单位**
|
||||||
|
|
||||||
|
#### 计量单位管理
|
||||||
|
- **功能**:创建和管理具体的计量单位
|
||||||
|
- **操作**:新增单位、编辑单位信息、删除单位
|
||||||
|
- **关联量纲**:将单位归属到具体的量纲下
|
||||||
|
|
||||||
|
#### 转换规则配置
|
||||||
|
- **功能**:配置单位间的转换规则
|
||||||
|
- **配置原则**:只需配置"非基准单位 → 基准单位"
|
||||||
|
- **自动推导**:系统自动推导反向转换和间接转换
|
||||||
|
|
||||||
|
#### 预置数据
|
||||||
|
系统已预置常用量纲和单位:
|
||||||
|
- **重量量纲**:千克(基准)、吨、克
|
||||||
|
- **长度量纲**:米(基准)、千米、厘米、毫米
|
||||||
|
- **体积量纲**:立方米(基准)、升、毫升
|
||||||
|
- **面积量纲**:平方米(基准)、平方千米、公顷
|
||||||
|
- **时间量纲**:秒(基准)、分钟、小时、天
|
||||||
|
|
||||||
|
### 2.3 配置建议
|
||||||
|
|
||||||
|
1. **量纲规划**:提前规划好业务需要的量纲类型
|
||||||
|
2. **基准单位选择**:选择业务中最常用、最稳定的单位作为基准
|
||||||
|
3. **转换规则**:优先使用整数转换系数,提高计算精度
|
||||||
|
4. **定期校验**:使用转换路径校验功能确保配置正确性
|
||||||
|
5. **转换完整性校验**:系统提供同一量纲内所有单位能否互相转换的校验功能,确保转换配置的完整性
|
||||||
|
|
||||||
|
## 三、API接口清单
|
||||||
|
|
||||||
|
### 3.1 单位管理接口
|
||||||
|
|
||||||
|
| 接口 | 方法 | 路径 | 说明 | API文档 |
|
||||||
|
|------|------|------|------|---------|
|
||||||
|
| 获取量纲树 | GET | `/admin-api/base/unit-management/unit-quantity/tree` | 获取量纲和单位树形结构 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E8%AE%A1%E9%87%8F%E5%8D%95%E4%BD%8D%E9%87%8F/getTree) |
|
||||||
|
| 获取单位列表 | GET | `/admin-api/base/unit-management/unt-info/page` | 获取单位列表(用于下拉选择) | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E8%AE%A1%E9%87%8F%E5%8D%95%E4%BD%8D/getPage) |
|
||||||
|
|
||||||
|
### 3.2 单位转换接口
|
||||||
|
|
||||||
|
| 接口 | 方法 | 路径 | 说明 | API文档 |
|
||||||
|
|------|------|------|------|---------|
|
||||||
|
| 按ID转换单位 | POST | `/admin-api/base/unit-management/unit-conversion/convert` | 通过单位ID转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/convert) |
|
||||||
|
| 按符号转换单位 | POST | `/admin-api/base/unit-management/unit-conversion/convert-by-symbol` | 通过单位符号转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/convertBySymbol) |
|
||||||
|
| 按名称转换单位 | POST | `/admin-api/base/unit-management/unit-conversion/convert-by-name` | 通过单位名称转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/convertByName) |
|
||||||
|
| 批量ID转换 | POST | `/admin-api/base/unit-management/unit-conversion/batch-convert` | 按ID批量转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/batchConvert) |
|
||||||
|
| 批量符号转换 | POST | `/admin-api/base/unit-management/unit-conversion/batch-convert-by-symbol` | 按符号批量转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/batchConvertBySymbol) |
|
||||||
|
| 批量名称转换 | POST | `/admin-api/base/unit-management/unit-conversion/batch-convert-by-name` | 按名称批量转换 | [文档](http://172.16.46.63:30081/doc.html#/base-server/%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%20-%20%E5%8D%95%E4%BD%8D%E8%BD%AC%E6%8D%A2/batchConvertByName) |
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、业务调用示例
|
||||||
|
|
||||||
|
### 合同订单模块使用
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
public class PurchaseOrderServiceImpl {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UnitConversionService unitConversionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理采购订单,统一转换为千克计算
|
||||||
|
*/
|
||||||
|
public void processPurchaseOrder(PurchaseOrderSaveReqVO orderVO) {
|
||||||
|
for (PurchaseOrderDetailVO detail : orderVO.getDetails()) {
|
||||||
|
// 方式1:按符号转换
|
||||||
|
UnitConvertBySymbolReqVO convertReq = new UnitConvertBySymbolReqVO();
|
||||||
|
convertReq.setSrcUnitSymbol(detail.getUnt());
|
||||||
|
convertReq.setTgtUnitSymbol("kg");
|
||||||
|
convertReq.setValue(detail.getQty());
|
||||||
|
convertReq.setPrecision(6);
|
||||||
|
|
||||||
|
UnitConvertRespVO result = unitConversionService.convertBySymbol(convertReq);
|
||||||
|
BigDecimal standardQuantity = result.getConvertedValue();
|
||||||
|
|
||||||
|
// 方式2:按ID转换(如果有单位ID)
|
||||||
|
// UnitConvertReqVO convertReq = new UnitConvertReqVO();
|
||||||
|
// convertReq.setSrcUntId(detail.getUntId());
|
||||||
|
// convertReq.setTgtUntId(kgUnitId);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、跨模块调用
|
||||||
|
|
||||||
|
### 4.1 直接Service调用(推荐)
|
||||||
|
|
||||||
|
在同一服务内直接注入使用:
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private UnitConversionService unitConversionService;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 跨服务调用(按需使用)
|
||||||
|
|
||||||
|
**1. 在 API 模块中定义 Feign 接口:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.zt.plat.module.base.api;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
|
import com.zt.plat.module.base.enums.ApiConstants;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
@FeignClient(name = ApiConstants.NAME)
|
||||||
|
@Tag(name = "RPC 服务 - 单位转换")
|
||||||
|
public interface UnitConversionApi {
|
||||||
|
|
||||||
|
String PREFIX = ApiConstants.PREFIX + "/unit-conversion";
|
||||||
|
|
||||||
|
@PostMapping(PREFIX + "/convert")
|
||||||
|
@Operation(summary = "按ID转换单位")
|
||||||
|
CommonResult<UnitConvertRespVO> convert(@RequestBody UnitConvertReqVO reqVO);
|
||||||
|
|
||||||
|
@PostMapping(PREFIX + "/convert-by-symbol")
|
||||||
|
@Operation(summary = "按符号转换单位")
|
||||||
|
CommonResult<UnitConvertRespVO> convertBySymbol(@RequestBody UnitConvertBySymbolReqVO reqVO);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 在其他服务中调用:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
public class PurchaseServiceImpl {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UnitConversionApi unitConversionApi;
|
||||||
|
|
||||||
|
public void processPurchase(PurchaseVO purchase) {
|
||||||
|
UnitConvertBySymbolReqVO convertReq = new UnitConvertBySymbolReqVO();
|
||||||
|
convertReq.setSrcUnitSymbol(purchase.getUnit());
|
||||||
|
convertReq.setTgtUnitSymbol("kg");
|
||||||
|
convertReq.setValue(purchase.getQuantity());
|
||||||
|
convertReq.setPrecision(6);
|
||||||
|
|
||||||
|
CommonResult<UnitConvertRespVO> result = unitConversionApi.convertBySymbol(convertReq);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
BigDecimal standardQty = result.getData().getConvertedValue();
|
||||||
|
// 业务处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、前端使用
|
||||||
|
|
||||||
|
### 5.1 基本API调用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取量纲树
|
||||||
|
export const getUnitQuantityTree = () => {
|
||||||
|
return request.get('/admin-api/base/unit-management/unit-quantity/tree')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取单位列表
|
||||||
|
export const getUntInfoPage = (params: any) => {
|
||||||
|
return request.get('/admin-api/base/unit-management/unt-info/page', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单位转换
|
||||||
|
export const convertUnitBySymbol = (data: any) => {
|
||||||
|
return request.post('/admin-api/base/unit-management/unit-conversion/convert-by-symbol', data)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、常见问题
|
||||||
|
|
||||||
|
**Q1: 前端如何获取单位选项?**
|
||||||
|
|
||||||
|
A: 使用 `/admin-api/base/unit-management/unit-quantity/tree` 获取量纲树,然后根据选择的量纲调用 `/admin-api/base/unit-management/unt-info/page` 获取单位列表。
|
||||||
|
|
||||||
|
**Q2: 按ID转换和按符号转换哪个更好?**
|
||||||
|
|
||||||
|
A: 按ID转换更稳定,因为数据库ID不会变化。建议在前端保存单位ID,在业务转换时使用ID调用。
|
||||||
|
|
||||||
|
**Q3: 跨服务调用需要特殊配置吗?**
|
||||||
|
|
||||||
|
A: 不需要,项目已经统一配置好。所有Feign客户端都使用 `name = "base-server"`,路径使用 `/rpc-api` 前缀。
|
||||||
|
|
||||||
|
**Q4: 批量转换性能问题?**
|
||||||
|
|
||||||
|
A: 使用批量接口,设置ignoreErrors=true。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**更新时间**: 2025-11-06
|
||||||
|
**版本**: v6.0 (修正版)
|
||||||
Reference in New Issue
Block a user