新增业务打印绑定功能,整合打印模板与业务数据的映射配置,优化打印数据生成逻辑。新增免密接口,支持桌面端打印模板的查询与列表展示,提升用户体验和系统的实时数据同步能力。同时,重构相关控制器以增强系统的可维护性和扩展性。
This commit is contained in:
@@ -9,12 +9,14 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
@@ -36,11 +38,13 @@ import org.jeecg.modules.print.vo.PrintBizFieldItemVO;
|
||||
import org.jeecg.modules.print.vo.PrintBizTypeVO;
|
||||
import org.jeecg.modules.print.vo.PrintTemplateFieldItemVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 业务与打印模板绑定:可视化配置字段映射
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "业务打印绑定")
|
||||
@RestController
|
||||
@RequestMapping("/print/bizTemplateBind")
|
||||
@@ -51,6 +55,7 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
@Autowired private IPrintTemplateService printTemplateService;
|
||||
@Autowired private IPrintBizBindPermWhitelistService printBizBindPermWhitelistService;
|
||||
@Autowired private IPrintBizPermEntityService printBizPermEntityService;
|
||||
@Autowired private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
@Operation(summary = "业务打印绑定-分页列表")
|
||||
@GetMapping("/list")
|
||||
@@ -81,6 +86,7 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
}
|
||||
normalizeMappingJson(entity);
|
||||
service.save(entity);
|
||||
publishPrintBizTemplateBindChanged("add", entity.getId());
|
||||
return Result.OK("添加成功");
|
||||
}
|
||||
|
||||
@@ -103,6 +109,7 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
}
|
||||
normalizeMappingJson(entity);
|
||||
service.updateById(entity);
|
||||
publishPrintBizTemplateBindChanged("edit", entity.getId());
|
||||
return Result.OK("编辑成功");
|
||||
}
|
||||
|
||||
@@ -111,7 +118,9 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
@DeleteMapping("/delete")
|
||||
@RequiresPermissions("print:bizBind:delete")
|
||||
public Result<String> delete(@RequestParam(name = "id") String id) {
|
||||
service.removeById(id);
|
||||
if (service.removeById(id)) {
|
||||
publishPrintBizTemplateBindChanged("delete", id);
|
||||
}
|
||||
return Result.OK("删除成功");
|
||||
}
|
||||
|
||||
@@ -239,6 +248,10 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
ArrayNode mapping = PrintBizDataMappingUtil.parseMappingArray(bind.getFieldMappingJson());
|
||||
JsonNode bizRoot = PrintBizDataMappingUtil.parseBizJson(body.getBizDataJson());
|
||||
ObjectNode printData = PrintBizDataMappingUtil.mapBizToPrintData(bizRoot, mapping);
|
||||
PrintTemplate tpl = printTemplateService.getById(bind.getTemplateId());
|
||||
if (tpl != null && StringUtils.isNotBlank(tpl.getTemplateJson())) {
|
||||
PrintBizDataMappingUtil.fillMissingDataBindingParamKeys(printData, tpl.getTemplateJson());
|
||||
}
|
||||
Map<String, Object> res = new HashMap<>(4);
|
||||
res.put("templateCode", bind.getTemplateCode());
|
||||
res.put("templateId", bind.getTemplateId());
|
||||
@@ -308,4 +321,43 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
public static class PermWhitelistBody {
|
||||
private java.util.List<String> permIds;
|
||||
}
|
||||
|
||||
// ═══════════════════════════ 桌面端免密只读 ═══════════════════════════
|
||||
|
||||
@Operation(summary = "业务打印绑定-免密分页列表(桌面端缓存同步)")
|
||||
@GetMapping("/anon/list")
|
||||
public Result<IPage<PrintBizTemplateBind>> anonList(
|
||||
PrintBizTemplateBind query,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "100") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<PrintBizTemplateBind> qw =
|
||||
QueryGenerator.initQueryWrapper(query, req.getParameterMap());
|
||||
qw.orderByDesc("create_time");
|
||||
Page<PrintBizTemplateBind> page = new Page<>(pageNo, pageSize);
|
||||
return Result.OK(service.page(page, qw));
|
||||
}
|
||||
|
||||
@Operation(summary = "业务打印绑定-免密按 id 查询(桌面端)")
|
||||
@GetMapping("/anon/queryById")
|
||||
public Result<PrintBizTemplateBind> anonQueryById(@RequestParam(name = "id") String id) {
|
||||
PrintBizTemplateBind row = service.getById(id);
|
||||
return row != null ? Result.OK(row) : Result.error("未找到记录");
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播到 /topic/sync/print-biz-binds,与桌面端 PrintBizTemplateBindSyncCoordinator 对应。
|
||||
*/
|
||||
private void publishPrintBizTemplateBindChanged(String action, String bindId) {
|
||||
try {
|
||||
Map<String, Object> event = new HashMap<>();
|
||||
event.put("cmd", "PRINT_BIZ_TEMPLATE_BIND_CHANGED");
|
||||
event.put("action", action);
|
||||
event.put("bindId", bindId);
|
||||
event.put("timestamp", System.currentTimeMillis());
|
||||
messagingTemplate.convertAndSend("/topic/sync/print-biz-binds", JSON.toJSONString(event));
|
||||
} catch (Exception e) {
|
||||
log.debug("广播 STOMP 事件失败 [PRINT_BIZ_TEMPLATE_BIND_CHANGED]: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.modules.print.vo.PrintTemplateFieldItemVO;
|
||||
|
||||
/** 按映射规则把业务 JSON 转为模板打印数据(键为模板 bindField) */
|
||||
public final class PrintBizDataMappingUtil {
|
||||
@@ -24,15 +26,75 @@ public final class PrintBizDataMappingUtil {
|
||||
}
|
||||
String templateField = text(rule, "templateField");
|
||||
String bizField = text(rule, "bizField");
|
||||
if (StringUtils.isAnyBlank(templateField, bizField)) {
|
||||
// 仅要求模板字段名;业务字段为空表示「不参与取数」,仍向 printData 写入空字符串,避免模板占位符缺键
|
||||
if (StringUtils.isBlank(templateField)) {
|
||||
continue;
|
||||
}
|
||||
JsonNode val = resolvePath(bizRoot, bizField);
|
||||
JsonNode val;
|
||||
if (StringUtils.isBlank(bizField)) {
|
||||
val = MAPPER.getNodeFactory().textNode("");
|
||||
} else {
|
||||
val = resolvePath(bizRoot, bizField);
|
||||
}
|
||||
setPath(printData, templateField, val);
|
||||
}
|
||||
return printData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板中已声明的绑定路径({@code dataBinding.params}、画布/表格等元素的 {@code bindField},与
|
||||
* {@link PrintNativeTemplateFieldExtractor} 一致),向 printData 补齐缺失路径(空字符串)。
|
||||
*
|
||||
* <p>避免字段映射未包含某键时 API 缺键,桌面端渲染把「设计稿占位 text」当成数据显示。
|
||||
*/
|
||||
public static ObjectNode fillMissingDataBindingParamKeys(ObjectNode printData, String templateJson) {
|
||||
if (printData == null) {
|
||||
printData = MAPPER.createObjectNode();
|
||||
}
|
||||
if (StringUtils.isBlank(templateJson)) {
|
||||
return printData;
|
||||
}
|
||||
try {
|
||||
List<PrintTemplateFieldItemVO> fields = PrintNativeTemplateFieldExtractor.extract(templateJson);
|
||||
for (PrintTemplateFieldItemVO item : fields) {
|
||||
if (item == null || StringUtils.isBlank(item.getBindField())) {
|
||||
continue;
|
||||
}
|
||||
String bf = item.getBindField().trim();
|
||||
if (!hasPath(printData, bf)) {
|
||||
setPath(printData, bf, MAPPER.getNodeFactory().textNode(""));
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 模板解析异常时不阻断打印
|
||||
}
|
||||
return printData;
|
||||
}
|
||||
|
||||
/** 判断 printData 上是否存在该点分路径(含嵌套对象) */
|
||||
private static boolean hasPath(ObjectNode root, String path) {
|
||||
if (StringUtils.isBlank(path)) {
|
||||
return false;
|
||||
}
|
||||
String[] parts = path.split("\\.");
|
||||
JsonNode cur = root;
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (cur == null || !cur.isObject()) {
|
||||
return false;
|
||||
}
|
||||
ObjectNode obj = (ObjectNode) cur;
|
||||
String p = parts[i];
|
||||
if (p.isEmpty() || !obj.has(p)) {
|
||||
return false;
|
||||
}
|
||||
if (i == parts.length - 1) {
|
||||
return true;
|
||||
}
|
||||
cur = obj.get(p);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JsonNode resolvePath(JsonNode root, String path) {
|
||||
if (root == null || StringUtils.isBlank(path)) {
|
||||
return null;
|
||||
|
||||
@@ -48,6 +48,7 @@ public class JeecgSystemApplication extends SpringBootServletInitializer {
|
||||
String port = env.getProperty("server.port");
|
||||
String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path"));
|
||||
log.info("\n----------------------------------------------------------\n\t" +
|
||||
|
||||
"Application Jeecg-Boot is running! Access URLs:\n\t" +
|
||||
"Local: \t\thttp://localhost:" + port + path + "\n\t" +
|
||||
"External: \thttp://" + ip + ":" + port + path + "/doc.html\n\t" +
|
||||
|
||||
Reference in New Issue
Block a user