package com.vincent.rsf.server.manager.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.manager.entity.MatnrPrintTemplate; import com.vincent.rsf.server.manager.mapper.MatnrPrintTemplateMapper; import com.vincent.rsf.server.manager.service.MatnrPrintTemplateService; import com.vincent.rsf.server.system.entity.User; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @Service("matnrPrintTemplateService") public class MatnrPrintTemplateServiceImpl extends ServiceImpl implements MatnrPrintTemplateService { private static final Set SUPPORTED_ELEMENT_TYPES = Collections.unmodifiableSet( new LinkedHashSet<>(Arrays.asList("text", "barcode", "qrcode", "line", "rect", "table")) ); @Override public List listCurrentTenantTemplates() { List templates = this.list(new LambdaQueryWrapper() .orderByDesc(MatnrPrintTemplate::getIsDefault) .orderByDesc(MatnrPrintTemplate::getUpdateTime) .orderByDesc(MatnrPrintTemplate::getCreateTime) ); return templates == null ? new ArrayList<>() : templates; } @Override public MatnrPrintTemplate getCurrentTenantTemplate(Long id) { if (id == null) { throw new CoolException("模板ID不能为空"); } MatnrPrintTemplate template = this.getById(id); if (template == null) { throw new CoolException("模板不存在或已被删除"); } return template; } @Override public MatnrPrintTemplate getCurrentTenantDefaultTemplate() { MatnrPrintTemplate template = this.getOne(new LambdaQueryWrapper() .eq(MatnrPrintTemplate::getStatus, 1) .eq(MatnrPrintTemplate::getIsDefault, 1) .orderByDesc(MatnrPrintTemplate::getUpdateTime) .last("limit 1") ); if (template != null) { return template; } return this.getOne(new LambdaQueryWrapper() .eq(MatnrPrintTemplate::getStatus, 1) .orderByDesc(MatnrPrintTemplate::getUpdateTime) .orderByDesc(MatnrPrintTemplate::getCreateTime) .last("limit 1") ); } @Override @Transactional(rollbackFor = Exception.class) public MatnrPrintTemplate saveTemplate(MatnrPrintTemplate template) { MatnrPrintTemplate normalized = prepareTemplateForSave(template, false); long currentCount = this.count(); boolean shouldDefault = Objects.equals(normalized.getIsDefault(), 1) || currentCount == 0; normalized.setIsDefault(shouldDefault ? 1 : 0); if (shouldDefault) { clearCurrentTenantDefaults(); } if (!this.save(normalized)) { throw new CoolException("模板保存失败"); } return this.getCurrentTenantTemplate(normalized.getId()); } @Override @Transactional(rollbackFor = Exception.class) public MatnrPrintTemplate updateTemplate(MatnrPrintTemplate template) { if (template == null || template.getId() == null) { throw new CoolException("模板ID不能为空"); } MatnrPrintTemplate existing = getCurrentTenantTemplate(template.getId()); MatnrPrintTemplate normalized = prepareTemplateForSave(template, true); normalized.setTenantId(existing.getTenantId()); normalized.setCreateBy(existing.getCreateBy()); normalized.setCreateTime(existing.getCreateTime()); normalized.setDeleted(existing.getDeleted()); boolean shouldDefault = Objects.equals(normalized.getIsDefault(), 1); if (shouldDefault) { clearCurrentTenantDefaults(); } else if (Objects.equals(existing.getIsDefault(), 1)) { normalized.setIsDefault(1); } if (!this.updateById(normalized)) { throw new CoolException("模板更新失败"); } ensureOneDefaultTemplate(); return this.getCurrentTenantTemplate(normalized.getId()); } @Override @Transactional(rollbackFor = Exception.class) public boolean removeTemplates(List ids) { if (ids == null || ids.isEmpty()) { throw new CoolException("请选择要删除的模板"); } List templates = this.listByIds(ids); if (templates == null || templates.isEmpty()) { return true; } boolean removedDefault = templates.stream().anyMatch(item -> Objects.equals(item.getIsDefault(), 1)); if (!this.removeByIds(ids)) { throw new CoolException("模板删除失败"); } if (removedDefault) { ensureOneDefaultTemplate(); } return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean setDefaultTemplate(Long id) { MatnrPrintTemplate template = getCurrentTenantTemplate(id); clearCurrentTenantDefaults(); boolean updated = this.update(new LambdaUpdateWrapper() .eq(MatnrPrintTemplate::getId, template.getId()) .set(MatnrPrintTemplate::getIsDefault, 1) .set(MatnrPrintTemplate::getUpdateBy, resolveCurrentUserId()) .set(MatnrPrintTemplate::getUpdateTime, new Date()) ); if (!updated) { throw new CoolException("默认模板设置失败"); } return true; } private MatnrPrintTemplate prepareTemplateForSave(MatnrPrintTemplate template, boolean updating) { if (template == null) { throw new CoolException("模板参数不能为空"); } Long currentTenantId = resolveCurrentTenantId(); Long currentUserId = resolveCurrentUserId(); if (currentTenantId == null) { throw new CoolException("当前租户信息缺失"); } String name = normalizeText(template.getName()); String code = normalizeText(template.getCode()); if (name.isEmpty()) { throw new CoolException("模板名称不能为空"); } if (code.isEmpty()) { throw new CoolException("模板编码不能为空"); } Map canvasJson = template.getCanvasJson(); if (canvasJson == null || canvasJson.isEmpty()) { throw new CoolException("模板画布不能为空"); } validateCanvasJson(canvasJson); ensureTemplateCodeUnique(code, updating ? template.getId() : null); Date now = new Date(); template.setTenantId(currentTenantId) .setName(name) .setCode(code) .setStatus(template.getStatus() == null ? 1 : template.getStatus()) .setIsDefault(Objects.equals(template.getIsDefault(), 1) ? 1 : 0) .setMemo(normalizeText(template.getMemo())) .setUpdateBy(currentUserId) .setUpdateTime(now); if (!updating) { template.setCreateBy(currentUserId); template.setCreateTime(now); } return template; } private void ensureTemplateCodeUnique(String code, Long excludeId) { long duplicateCount = this.count(new LambdaQueryWrapper() .eq(MatnrPrintTemplate::getCode, code) .ne(excludeId != null, MatnrPrintTemplate::getId, excludeId) ); if (duplicateCount > 0) { throw new CoolException("模板编码已存在,请更换后重试"); } } private void clearCurrentTenantDefaults() { this.update(new LambdaUpdateWrapper() .eq(MatnrPrintTemplate::getIsDefault, 1) .set(MatnrPrintTemplate::getIsDefault, 0) .set(MatnrPrintTemplate::getUpdateBy, resolveCurrentUserId()) .set(MatnrPrintTemplate::getUpdateTime, new Date()) ); } private void ensureOneDefaultTemplate() { long defaultCount = this.count(new LambdaQueryWrapper() .eq(MatnrPrintTemplate::getIsDefault, 1) ); if (defaultCount > 0) { return; } MatnrPrintTemplate newest = this.getOne(new LambdaQueryWrapper() .orderByDesc(MatnrPrintTemplate::getUpdateTime) .orderByDesc(MatnrPrintTemplate::getCreateTime) .last("limit 1") ); if (newest == null) { return; } this.update(new LambdaUpdateWrapper() .eq(MatnrPrintTemplate::getId, newest.getId()) .set(MatnrPrintTemplate::getIsDefault, 1) .set(MatnrPrintTemplate::getUpdateBy, resolveCurrentUserId()) .set(MatnrPrintTemplate::getUpdateTime, new Date()) ); } private void validateCanvasJson(Map canvasJson) { JSONObject root = JSONObject.parseObject(JSON.toJSONString(canvasJson)); if (root == null) { throw new CoolException("模板画布格式不正确"); } if (root.getInteger("version") == null) { throw new CoolException("模板版本不能为空"); } JSONObject canvas = root.getJSONObject("canvas"); if (canvas == null) { throw new CoolException("模板画布配置不能为空"); } double width = getPositiveNumber(canvas, "width", "画布宽度"); double height = getPositiveNumber(canvas, "height", "画布高度"); if (width <= 0 || height <= 0) { throw new CoolException("画布尺寸必须大于0"); } String unit = normalizeText(canvas.getString("unit")); if (!"mm".equals(unit)) { throw new CoolException("画布单位仅支持 mm"); } JSONArray elements = root.getJSONArray("elements"); if (elements == null) { throw new CoolException("模板元素不能为空"); } for (int index = 0; index < elements.size(); index++) { JSONObject element = elements.getJSONObject(index); if (element == null) { throw new CoolException("模板元素格式不正确"); } validateElement(element, index); } } private void validateElement(JSONObject element, int index) { String type = normalizeText(element.getString("type")); if (!SUPPORTED_ELEMENT_TYPES.contains(type)) { throw new CoolException("第" + (index + 1) + "个元素类型不支持"); } if (normalizeText(element.getString("id")).isEmpty()) { throw new CoolException("第" + (index + 1) + "个元素缺少 ID"); } ensureNumber(element, "x", "元素 X 坐标"); ensureNumber(element, "y", "元素 Y 坐标"); if (!"line".equals(type)) { getPositiveNumber(element, "w", "元素宽度"); getPositiveNumber(element, "h", "元素高度"); } else { String direction = normalizeText(element.getString("direction")); if (!Arrays.asList("horizontal", "vertical").contains(direction)) { throw new CoolException("线条元素方向仅支持 horizontal 或 vertical"); } getPositiveNumber(element, "w", "线条长度"); getPositiveNumber(element, "h", "线条粗细"); } switch (type) { case "text": String contentMode = normalizeText(element.getString("contentMode")); if (!Arrays.asList("static", "template").contains(contentMode)) { throw new CoolException("文本元素内容模式不支持"); } if (normalizeText(element.getString("contentTemplate")).isEmpty()) { throw new CoolException("文本元素内容不能为空"); } break; case "barcode": if (normalizeText(element.getString("valueTemplate")).isEmpty()) { throw new CoolException("条码元素值模板不能为空"); } String symbology = normalizeText(element.getString("symbology")); if (!symbology.isEmpty() && !"CODE128".equals(symbology)) { throw new CoolException("一维码仅支持 CODE128"); } break; case "qrcode": if (normalizeText(element.getString("valueTemplate")).isEmpty()) { throw new CoolException("二维码元素值模板不能为空"); } break; case "table": if (element.getJSONArray("columns") == null) { throw new CoolException("表格元素 columns 不能为空"); } if (element.getJSONArray("rows") == null) { throw new CoolException("表格元素 rows 不能为空"); } if (element.getJSONArray("cells") == null) { throw new CoolException("表格元素 cells 不能为空"); } break; default: break; } } private void ensureNumber(JSONObject object, String key, String label) { if (object.getBigDecimal(key) == null) { throw new CoolException(label + "不能为空"); } } private double getPositiveNumber(JSONObject object, String key, String label) { if (object.getBigDecimal(key) == null) { throw new CoolException(label + "不能为空"); } double value = object.getBigDecimal(key).doubleValue(); if (value <= 0) { throw new CoolException(label + "必须大于0"); } return value; } private String normalizeText(String value) { return value == null ? "" : value.trim(); } private Long resolveCurrentTenantId() { User loginUser = getCurrentUser(); return loginUser == null ? null : loginUser.getTenantId(); } private Long resolveCurrentUserId() { User loginUser = getCurrentUser(); return loginUser == null ? null : loginUser.getId(); } private User getCurrentUser() { try { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof User) { return (User) authentication.getPrincipal(); } } catch (Exception ignored) { } return null; } }