diff --git a/sql/archives20230915JilingLee.sql b/sql/archives20230915JilingLee.sql
new file mode 100644
index 00000000..5ce4aaef
--- /dev/null
+++ b/sql/archives20230915JilingLee.sql
@@ -0,0 +1,55 @@
+-- ----------------------------
+-- 创建装册管理表 for archives_package
+-- ----------------------------
+DROP TABLE IF EXISTS `archives_package`;
+CREATE TABLE `archives_package` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `depot_id` bigint(20) NULL DEFAULT NULL COMMENT '库房id',
+ `depot_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '库房名称',
+ `cabinet_id` bigint(20) NULL DEFAULT NULL COMMENT '档案柜id',
+ `cabinet_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '档案柜名称',
+ `record_id` bigint(20) NULL DEFAULT NULL COMMENT '档案id',
+ `company_id` bigint(20) NULL DEFAULT NULL COMMENT '业务实体id',
+ `company` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '业务实体',
+ `year` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '会计年份',
+ `period` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '会计月份',
+ `manege_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '负责人名称',
+ `case_num` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '全宗号',
+ `catalog_num` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '目录号',
+ `files_num` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '案卷号',
+ `flow_id` bigint(20) NULL DEFAULT NULL COMMENT '关联号',
+ `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建日期',
+ `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
+ `creator` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
+ `status` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '状态待装册,已装册',
+ `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
+ `package_time` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '装册时间',
+ `dept_id` bigint(20) NULL DEFAULT NULL COMMENT '所属部门id',
+ `user_id` bigint(20) NULL DEFAULT NULL COMMENT '用户id',
+ `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新者',
+ `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门',
+ `tenant_id` bigint(20) NOT NULL COMMENT '租户编号',
+ `attr1` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '预留字段1',
+ `attr2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '预留字段2',
+ `attr3` int(11) NULL DEFAULT NULL COMMENT '预留字段3',
+ `attr4` int(11) NULL DEFAULT NULL COMMENT '预留字段4',
+ `create_by` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '制单人',
+ `code_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '条码地址',
+ `code_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '条码值',
+ `package_num` int(11) NULL DEFAULT NULL COMMENT '装册数量',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '装册表' ROW_FORMAT = Dynamic;
+
+SET FOREIGN_KEY_CHECKS = 1;
+
+-- 插入装册管理菜单
+SELECT * FROM system_menu WHERE name='档案管理';
+SET @menuId = LAST_INSERT_ID();
+INSERT INTO `lyr-one`.`system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('装册管理', '', 2, 0, @menuId, 'packages', '', 'archives/packages/index', 'Packages', 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
+SET @menuId2 = LAST_INSERT_ID();
+INSERT INTO `lyr-one`.`system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (NULL, '装册查询', 'archives:packages:query', 3, 1, @menuId2, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
+INSERT INTO `lyr-one`.`system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (NULL, '装册创建', 'archives:packages:create', 3, 2, @menuId2, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
+INSERT INTO `lyr-one`.`system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (NULL, '装册更新', 'archives:packages:update', 3, 3, @menuId2, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
+INSERT INTO `lyr-one`.`system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (NULL, '装册删除', 'archives:packages:delete', 3, 4, @menuId2, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
+INSERT INTO `lyr-one`.`system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (NULL, '装册导出', 'archives:packages:export', 3, 5, @menuId2, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-15 13:20:18', '', '2023-09-15 13:20:18', b'0');
diff --git a/yudao-framework/yudao-common/pom.xml b/yudao-framework/yudao-common/pom.xml
index 76016033..d1818f36 100644
--- a/yudao-framework/yudao-common/pom.xml
+++ b/yudao-framework/yudao-common/pom.xml
@@ -22,6 +22,23 @@
spring-core
provided
+
+
+ net.sf.barcode4j
+ barcode4j-light
+ 2.0
+
+
+
+ commons-io
+ commons-io
+
+
+
+ com.google.zxing
+ core
+ 3.3.3
+
org.springframework
spring-expression
@@ -70,6 +87,13 @@
apm-toolkit-trace
+
+
+ commons-fileupload
+ commons-fileupload
+ 1.4
+
+
org.projectlombok
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/constant/UserConstants.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/constant/UserConstants.java
new file mode 100644
index 00000000..0d402aee
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/constant/UserConstants.java
@@ -0,0 +1,133 @@
+package cn.iocoder.yudao.framework.common.constant;
+
+/**
+ * 用户常量信息
+ *
+ * @author ruoyi
+ */
+public class UserConstants {
+ /**
+ * 平台内系统用户的唯一标志
+ */
+ public static final String SYS_USER = "SYS_USER";
+
+ /**
+ * 正常状态
+ */
+ public static final String NORMAL = "0";
+
+ /**
+ * 异常状态
+ */
+ public static final String EXCEPTION = "1";
+
+ /**
+ * 用户封禁状态
+ */
+ public static final String USER_DISABLE = "1";
+
+ /**
+ * 角色封禁状态
+ */
+ public static final String ROLE_DISABLE = "1";
+
+ /**
+ * 部门正常状态
+ */
+ public static final String DEPT_NORMAL = "0";
+
+ /**
+ * 部门停用状态
+ */
+ public static final String DEPT_DISABLE = "1";
+
+ /**
+ * 字典正常状态
+ */
+ public static final String DICT_NORMAL = "0";
+
+ /**
+ * 是否为系统默认(是)
+ */
+ public static final String YES = "Y";
+
+ public static final String NO = "N";
+
+ /**
+ * 是否菜单外链(是)
+ */
+ public static final String YES_FRAME = "0";
+
+ /**
+ * 是否菜单外链(否)
+ */
+ public static final String NO_FRAME = "1";
+
+ /**
+ * 菜单类型(目录)
+ */
+ public static final String TYPE_DIR = "M";
+
+ /**
+ * 菜单类型(菜单)
+ */
+ public static final String TYPE_MENU = "C";
+
+ /**
+ * 菜单类型(按钮)
+ */
+ public static final String TYPE_BUTTON = "F";
+
+ /**
+ * Layout组件标识
+ */
+ public final static String LAYOUT = "Layout";
+
+ /**
+ * ParentView组件标识
+ */
+ public final static String PARENT_VIEW = "ParentView";
+
+ /**
+ * InnerLink组件标识
+ */
+ public final static String INNER_LINK = "InnerLink";
+
+ public final static String WEBSOCKET_HEARTBEAT = "-heartbeat-";
+
+ /**
+ * 校验返回结果码
+ */
+ public final static String UNIQUE = "0";
+ public final static String NOT_UNIQUE = "1";
+
+ /**
+ * 用户名长度限制
+ */
+ public static final int USERNAME_MIN_LENGTH = 2;
+ public static final int USERNAME_MAX_LENGTH = 20;
+
+ /**
+ * 密码长度限制
+ */
+ public static final int PASSWORD_MIN_LENGTH = 5;
+ public static final int PASSWORD_MAX_LENGTH = 20;
+
+
+ /**
+ * 条码格式
+ */
+ public static final String QR_CODE = "QR_CODE";
+ public static final String EAN_CODE = "EAN_CODE";
+ public static final String UPC_CODE = "UPC_CODE";
+ public static final String CODE39_CODE = "CODE39_CODE";
+
+
+ /**
+ * 消息状态
+ */
+ public static final String MESSAGE_STATUS_UNREAD = "UNREAD"; //未读
+ public static final String MESSAGE_STATUS_READ = "READ";//已读
+ public static final String MESSAGE_STATUS_PROCEED = "PROCEED";//已处理
+
+}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/FileUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/FileUtils.java
new file mode 100644
index 00000000..458c8c6c
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/FileUtils.java
@@ -0,0 +1,227 @@
+package cn.iocoder.yudao.framework.common.util;
+
+import cn.iocoder.yudao.framework.common.util.string.StringUtils;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.http.MediaType;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 文件处理工具类
+ *
+ * @author ruoyi
+ */
+public class FileUtils
+{
+ public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+ /**
+ * 输出指定文件的byte数组
+ *
+ * @param filePath 文件路径
+ * @param os 输出流
+ * @return
+ */
+ public static void writeBytes(String filePath, OutputStream os) throws IOException
+ {
+ FileInputStream fis = null;
+ try
+ {
+ File file = new File(filePath);
+ if (!file.exists())
+ {
+ throw new FileNotFoundException(filePath);
+ }
+ fis = new FileInputStream(file);
+ byte[] b = new byte[1024];
+ int length;
+ while ((length = fis.read(b)) > 0)
+ {
+ os.write(b, 0, length);
+ }
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ finally
+ {
+ IOUtils.close(os);
+ IOUtils.close(fis);
+ }
+ }
+
+
+ /**
+ * 删除文件
+ *
+ * @param filePath 文件
+ * @return
+ */
+ public static boolean deleteFile(String filePath)
+ {
+ boolean flag = false;
+ File file = new File(filePath);
+ // 路径为文件且不为空则进行删除
+ if (file.isFile() && file.exists())
+ {
+ file.delete();
+ flag = true;
+ }
+ return flag;
+ }
+
+ /**
+ * 文件名称验证
+ *
+ * @param filename 文件名称
+ * @return true 正常 false 非法
+ */
+ public static boolean isValidFilename(String filename)
+ {
+ return filename.matches(FILENAME_PATTERN);
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param request 请求对象
+ * @param fileName 文件名
+ * @return 编码后的文件名
+ */
+ public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
+ {
+ final String agent = request.getHeader("USER-AGENT");
+ String filename = fileName;
+ if (agent.contains("MSIE"))
+ {
+ // IE浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ filename = filename.replace("+", " ");
+ }
+ else if (agent.contains("Firefox"))
+ {
+ // 火狐浏览器
+ filename = new String(fileName.getBytes(), "ISO8859-1");
+ }
+ else if (agent.contains("Chrome"))
+ {
+ // google浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ else
+ {
+ // 其它浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ return filename;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param response 响应对象
+ * @param realFileName 真实文件名
+ */
+ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
+ {
+ String percentEncodedFileName = percentEncode(realFileName);
+
+ StringBuilder contentDispositionValue = new StringBuilder();
+ contentDispositionValue.append("attachment; filename=")
+ .append(percentEncodedFileName)
+ .append(";")
+ .append("filename*=")
+ .append("utf-8''")
+ .append(percentEncodedFileName);
+
+ response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
+ response.setHeader("Content-disposition", contentDispositionValue.toString());
+ response.setHeader("download-filename", percentEncodedFileName);
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode(String s) throws UnsupportedEncodingException
+ {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+ return encode.replaceAll("\\+", "%20");
+ }
+
+ /**
+ * 获取图像后缀
+ *
+ * @param photoByte 图像数据
+ * @return 后缀名
+ */
+ public static String getFileExtendName(byte[] photoByte)
+ {
+ String strFileExtendName = "jpg";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
+ {
+ strFileExtendName = "gif";
+ }
+ else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
+ {
+ strFileExtendName = "jpg";
+ }
+ else if ((photoByte[0] == 66) && (photoByte[1] == 77))
+ {
+ strFileExtendName = "bmp";
+ }
+ else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
+ {
+ strFileExtendName = "png";
+ }
+ return strFileExtendName;
+ }
+
+ /**
+ * 获取名称
+ *
+ * @param fileName 路径名称
+ * @return 没有文件路径的名称
+ */
+ public static String getName(String fileName)
+ {
+ if (fileName == null)
+ {
+ return null;
+ }
+ int lastUnixPos = fileName.lastIndexOf('/');
+ int lastWindowsPos = fileName.lastIndexOf('\\');
+ int index = Math.max(lastUnixPos, lastWindowsPos);
+ return fileName.substring(index + 1);
+ }
+
+ public static MultipartFile getMultipartFile(File file) {
+ FileItem item = new DiskFileItemFactory().createItem("file"
+ , MediaType.MULTIPART_FORM_DATA_VALUE
+ , true
+ , file.getName());
+ try (InputStream input = new FileInputStream(file);
+ OutputStream os = item.getOutputStream()) {
+ // 流转移
+ IOUtils.copy(input, os);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Invalid file: " + e, e);
+ }
+
+ return new CommonsMultipartFile(item);
+ }
+}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/barcode/BarcodeUtil.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/barcode/BarcodeUtil.java
new file mode 100644
index 00000000..a7b41322
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/barcode/BarcodeUtil.java
@@ -0,0 +1,217 @@
+package cn.iocoder.yudao.framework.common.util.barcode;
+
+import cn.iocoder.yudao.framework.common.constant.UserConstants;
+import cn.iocoder.yudao.framework.common.util.string.StringUtils;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import org.krysalis.barcode4j.impl.AbstractBarcodeBean;
+import org.krysalis.barcode4j.impl.code39.Code39Bean;
+import org.krysalis.barcode4j.impl.upcean.EAN13Bean;
+import org.krysalis.barcode4j.impl.upcean.UPCABean;
+import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
+import org.krysalis.barcode4j.tools.UnitConv;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class BarcodeUtil {
+ private static final String CHARSET = "utf-8";
+ private static final String FORMAT_NAME = "PNG";
+ // 二维码尺寸,长宽尺寸一致
+ private static final int QRCODE_SIZE = 300;
+ // 二维码内部logo的宽和高
+ private static final int WIDTH = 60;
+ private static final int HEIGHT = 60;
+
+ public static File generateBarCode(String msg, String barCodeFormat, String path) {
+ File file = new File(path);
+ try {
+ if(StringUtils.isEmpty(msg)){
+ throw new RuntimeException("条码内容不能为空!");
+ }
+ //检查父级目录是否存在,不存在先新建
+ File parentFile = file.getParentFile();
+ if(!parentFile.exists()){
+ parentFile.mkdirs();
+ }
+ /*
+ * 生成条码
+ */
+ if(UserConstants.QR_CODE.equals(barCodeFormat)){
+ //生成二维码
+ generateQRCode(msg, null, file.getAbsolutePath(), false);
+ }else{
+ //生成一维码
+ generateLineCode(msg, barCodeFormat, file.getAbsolutePath());
+ }
+ }catch (Exception e) {
+ throw new RuntimeException("生成条码发生错误:"+e.getMessage());
+ }
+ return file;
+ }
+ /**
+ * 生成一维条形码
+ * @param msg 条形码的内容
+ * @param barCodeFormat 条形码格式 see {@link UserConstants}
+ * @param destFilePath 条形码存储的路径
+ * @throws Exception
+ */
+ public static void generateLineCode(String msg,String barCodeFormat,String destFilePath) throws Exception {
+ AbstractBarcodeBean bean = null;
+ if(UserConstants.EAN_CODE.equals(barCodeFormat)){
+ bean = new EAN13Bean();
+ }else if(UserConstants.CODE39_CODE.equals(barCodeFormat)){
+ bean = new Code39Bean();
+ }else if(UserConstants.UPC_CODE.equals(barCodeFormat)){
+ bean = new UPCABean();
+ }else{
+ throw new Exception("不支持的条码类型!");
+ }
+ // 精细度
+ final int dpi = 150;
+ if (bean instanceof Code39Bean) {
+ Code39Bean codeBean = (Code39Bean) bean;
+ // module宽度
+ final double moduleWidth = UnitConv.in2mm(1.0f / dpi);
+
+ // 配置对象
+ bean.setModuleWidth(moduleWidth);
+ codeBean.setWideFactor(3);
+ bean.doQuietZone(false);
+ }
+
+ String format = "image/png";
+ try {
+
+ // 输出到流
+ BitmapCanvasProvider canvas = new BitmapCanvasProvider(new FileOutputStream(destFilePath), format, dpi,
+ BufferedImage.TYPE_BYTE_BINARY, false, 0);
+
+ // 生成条形码
+ bean.generateBarcode(canvas, msg);
+
+ // 结束绘制
+ canvas.finish();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ /**
+ * 生成带logo二维码,并保存到磁盘
+ *
+ * @param content 二维码的内容
+ * @param imgPath 二维码内部插入的图片路径,如果不需要可以传空
+ * @param destFilePath 整个二维码存储的路径
+ * @param needCompress 二维码内部插入的图片是否需要压缩的标识
+ * @throws Exception 异常
+ */
+ public static void generateQRCode(String content, String imgPath, String destFilePath, boolean needCompress) throws Exception {
+ BufferedImage image = createQrCode(content, imgPath, needCompress);
+ ImageIO.write(image, FORMAT_NAME, new File(destFilePath));
+ }
+
+ /**
+ * 生成二维码图片
+ *
+ * @param content 二维码内容
+ * @param imagePath 二维码内部图片路径,如果不需要可以传空
+ * @param needCompress 二维码内部图片是否需要压缩标识
+ * @return 二维码图片
+ * @throws WriterException 异常
+ */
+ private static BufferedImage createQrCode(String content,String imagePath,boolean needCompress) throws WriterException, IOException {
+ /**
+ * EncodeHintType 编码提示类型 枚举类型:
+ * CHARACTER_SET: 设置字符编码类型 "UTF-8" QR_CODE类型默认的编码格式 "ISO-8859-1"
+ * MARGIN: 设置二维码边距,单位像素,值越小,二维码距离四周越近
+ * ERROR_CORRECTION: 设置误差校正
+ * ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction
+ * 不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的
+ */
+ Map hints = new ConcurrentHashMap<>(3);
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
+ hints.put(EncodeHintType.MARGIN, 1);
+ //加密生成二维码的矩阵对象 二维的0 1 然后根据特定的值选择属性
+ BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
+ int width = bitMatrix.getWidth();
+ int height = bitMatrix.getHeight();
+ BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ for (int i = 0; i < width; i++) {
+ for (int j = 0; j < height; j++) {
+ //设置二维码的黑色0xFF000000 和白色0xFFFFFFFF
+ image.setRGB(i, j, bitMatrix.get(i, j) ? 0xFF000000 : 0xFFFFFFFF);
+ }
+ }
+ if (StringUtils.isBlank(imagePath)) {
+ return image;
+ }
+ //插入小图标 logo
+ insertImage(image, imagePath, needCompress);
+ return image;
+ }
+ /**
+ * 将logo插入二维码中间
+ *
+ * @param source 生成的二位码存储对象
+ * @param imagePath 二维码内部图片的路径
+ * 一般的业务场景会把插入的小logo放到oss生成url
+ * @param needCompress 二维码内部图片是否需要压缩
+ * @throws IOException 异常
+ */
+ private static void insertImage(BufferedImage source, String imagePath, boolean needCompress) throws IOException {
+ File file = new File(imagePath);
+ if (!file.exists()) {
+ //可以自定义抛出异常
+ throw new RuntimeException("文件路径不存在:" + imagePath);
+ }
+ //这里修改ImageIO.read() 内可以接受Url InputStream File ImageInputStream
+ Image src = ImageIO.read(file);
+ int width = src.getWidth(null);
+ int height = src.getHeight(null);
+ //是否压缩
+ if (needCompress) {
+ if (width > WIDTH) {
+ width = WIDTH;
+ }
+ if (height > HEIGHT) {
+ height = HEIGHT;
+ }
+ Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+ BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ Graphics g = tag.getGraphics();
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+ src = image;
+ }
+ Graphics2D graphics = source.createGraphics();
+ int x = (QRCODE_SIZE - width) / 2;
+ int y = (QRCODE_SIZE - height) / 2;
+ graphics.drawImage(src,x,y,width,height,null);
+ Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
+ graphics.setStroke(new BasicStroke(3f));
+ graphics.draw(shape);
+ graphics.dispose();
+ }
+
+
+ public static void main(String[] args) {
+ String msg="item12345678";
+ String EANmsg="012345678901";
+ String UPCmsg="12345678901";
+ String path = "D:/test/barcode/"+UUID.randomUUID()+".png";
+ generateBarCode(msg, UserConstants.QR_CODE, path);
+ }
+}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StringUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StringUtils.java
new file mode 100644
index 00000000..ffb92c63
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StringUtils.java
@@ -0,0 +1,568 @@
+package cn.iocoder.yudao.framework.common.util.string;
+
+import cn.hutool.core.text.StrFormatter;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ *
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+ /** 空字符串 */
+ private static final String NULLSTR = "";
+
+ /** 下划线 */
+ private static final char SEPARATOR = '_';
+
+ /**
+ * 获取参数不为空值
+ *
+ * @param value defaultValue 要判断的value
+ * @return value 返回值
+ */
+ public static T nvl(T value, T defaultValue)
+ {
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * * 判断一个Collection是否为空, 包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Collection> coll)
+ {
+ return isNull(coll) || coll.isEmpty();
+ }
+
+ /**
+ * * 判断一个Collection是否非空,包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Collection> coll)
+ {
+ return !isEmpty(coll);
+ }
+
+ /**
+ * * 判断一个对象数组是否为空
+ *
+ * @param objects 要判断的对象数组
+ ** @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Object[] objects)
+ {
+ return isNull(objects) || (objects.length == 0);
+ }
+
+ /**
+ * * 判断一个对象数组是否非空
+ *
+ * @param objects 要判断的对象数组
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Object[] objects)
+ {
+ return !isEmpty(objects);
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(Map, ?> map)
+ {
+ return isNull(map) || map.isEmpty();
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty(Map, ?> map)
+ {
+ return !isEmpty(map);
+ }
+
+ /**
+ * * 判断一个字符串是否为空串
+ *
+ * @param str String
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty(String str)
+ {
+ return isNull(str) || NULLSTR.equals(str.trim());
+ }
+
+ /**
+ * * 判断一个字符串是否为非空串
+ *
+ * @param str String
+ * @return true:非空串 false:空串
+ */
+ public static boolean isNotEmpty(String str)
+ {
+ return !isEmpty(str);
+ }
+
+ /**
+ * * 判断一个对象是否为空
+ *
+ * @param object Object
+ * @return true:为空 false:非空
+ */
+ public static boolean isNull(Object object)
+ {
+ return object == null;
+ }
+
+ /**
+ * * 判断一个对象是否非空
+ *
+ * @param object Object
+ * @return true:非空 false:空
+ */
+ public static boolean isNotNull(Object object)
+ {
+ return !isNull(object);
+ }
+
+ /**
+ * * 判断一个对象是否是数组类型(Java基本型别的数组)
+ *
+ * @param object 对象
+ * @return true:是数组 false:不是数组
+ */
+ public static boolean isArray(Object object)
+ {
+ return isNotNull(object) && object.getClass().isArray();
+ }
+
+ /**
+ * 去空格
+ */
+ public static String trim(String str)
+ {
+ return (str == null ? "" : str.trim());
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ * @return 结果
+ */
+ public static String substring(final String str, int start)
+ {
+ if (str == null)
+ {
+ return NULLSTR;
+ }
+
+ if (start < 0)
+ {
+ start = str.length() + start;
+ }
+
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (start > str.length())
+ {
+ return NULLSTR;
+ }
+
+ return str.substring(start);
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ * @param end 结束
+ * @return 结果
+ */
+ public static String substring(final String str, int start, int end)
+ {
+ if (str == null)
+ {
+ return NULLSTR;
+ }
+
+ if (end < 0)
+ {
+ end = str.length() + end;
+ }
+ if (start < 0)
+ {
+ start = str.length() + start;
+ }
+
+ if (end > str.length())
+ {
+ end = str.length();
+ }
+
+ if (start > end)
+ {
+ return NULLSTR;
+ }
+
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (end < 0)
+ {
+ end = 0;
+ }
+
+ return str.substring(start, end);
+ }
+
+ /**
+ * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ *
+ * @param template 文本模板,被替换的部分用 {} 表示
+ * @param params 参数值
+ * @return 格式化后的文本
+ */
+ public static String format(String template, Object... params)
+ {
+ if (isEmpty(params) || isEmpty(template))
+ {
+ return template;
+ }
+ return StrFormatter.format(template, params);
+ }
+
+
+ /**
+ * 字符串转set
+ *
+ * @param str 字符串
+ * @param sep 分隔符
+ * @return set集合
+ */
+ public static final Set str2Set(String str, String sep)
+ {
+ return new HashSet(str2List(str, sep, true, false));
+ }
+
+ /**
+ * 字符串转list
+ *
+ * @param str 字符串
+ * @param sep 分隔符
+ * @param filterBlank 过滤纯空白
+ * @param trim 去掉首尾空白
+ * @return list集合
+ */
+ public static final List str2List(String str, String sep, boolean filterBlank, boolean trim)
+ {
+ List list = new ArrayList();
+ if (StringUtils.isEmpty(str))
+ {
+ return list;
+ }
+
+ // 过滤空白字符串
+ if (filterBlank && StringUtils.isBlank(str))
+ {
+ return list;
+ }
+ String[] split = str.split(sep);
+ for (String string : split)
+ {
+ if (filterBlank && StringUtils.isBlank(string))
+ {
+ continue;
+ }
+ if (trim)
+ {
+ string = string.trim();
+ }
+ list.add(string);
+ }
+
+ return list;
+ }
+
+ /**
+ * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+ *
+ * @param cs 指定字符串
+ * @param searchCharSequences 需要检查的字符串数组
+ * @return 是否包含任意一个字符串
+ */
+ public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+ {
+ if (isEmpty(cs) || isEmpty(searchCharSequences))
+ {
+ return false;
+ }
+ for (CharSequence testStr : searchCharSequences)
+ {
+ if (containsIgnoreCase(cs, testStr))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 驼峰转下划线命名
+ */
+ public static String toUnderScoreCase(String str)
+ {
+ if (str == null)
+ {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ // 前置字符是否大写
+ boolean preCharIsUpperCase = true;
+ // 当前字符是否大写
+ boolean curreCharIsUpperCase = true;
+ // 下一字符是否大写
+ boolean nexteCharIsUpperCase = true;
+ for (int i = 0; i < str.length(); i++)
+ {
+ char c = str.charAt(i);
+ if (i > 0)
+ {
+ preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+ }
+ else
+ {
+ preCharIsUpperCase = false;
+ }
+
+ curreCharIsUpperCase = Character.isUpperCase(c);
+
+ if (i < (str.length() - 1))
+ {
+ nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+ }
+
+ if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+ {
+ sb.append(SEPARATOR);
+ }
+ else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+ {
+ sb.append(SEPARATOR);
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * 是否包含字符串
+ *
+ * @param str 验证字符串
+ * @param strs 字符串组
+ * @return 包含返回true
+ */
+ public static boolean inStringIgnoreCase(String str, String... strs)
+ {
+ if (str != null && strs != null)
+ {
+ for (String s : strs)
+ {
+ if (str.equalsIgnoreCase(trim(s)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+ *
+ * @param name 转换前的下划线大写方式命名的字符串
+ * @return 转换后的驼峰式命名的字符串
+ */
+ public static String convertToCamelCase(String name)
+ {
+ StringBuilder result = new StringBuilder();
+ // 快速检查
+ if (name == null || name.isEmpty())
+ {
+ // 没必要转换
+ return "";
+ }
+ else if (!name.contains("_"))
+ {
+ // 不含下划线,仅将首字母大写
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }
+ // 用下划线将原始字符串分割
+ String[] camels = name.split("_");
+ for (String camel : camels)
+ {
+ // 跳过原始字符串中开头、结尾的下换线或双重下划线
+ if (camel.isEmpty())
+ {
+ continue;
+ }
+ // 首字母大写
+ result.append(camel.substring(0, 1).toUpperCase());
+ result.append(camel.substring(1).toLowerCase());
+ }
+ return result.toString();
+ }
+
+ /**
+ * 驼峰式命名法 例如:user_name->userName
+ */
+ public static String toCamelCase(String s)
+ {
+ if (s == null)
+ {
+ return null;
+ }
+ s = s.toLowerCase();
+ StringBuilder sb = new StringBuilder(s.length());
+ boolean upperCase = false;
+ for (int i = 0; i < s.length(); i++)
+ {
+ char c = s.charAt(i);
+
+ if (c == SEPARATOR)
+ {
+ upperCase = true;
+ }
+ else if (upperCase)
+ {
+ sb.append(Character.toUpperCase(c));
+ upperCase = false;
+ }
+ else
+ {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+ *
+ * @param str 指定字符串
+ * @param strs 需要检查的字符串数组
+ * @return 是否匹配
+ */
+ public static boolean matches(String str, List strs)
+ {
+ if (isEmpty(str) || isEmpty(strs))
+ {
+ return false;
+ }
+ for (String pattern : strs)
+ {
+ if (isMatch(pattern, str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断url是否与规则配置:
+ * ? 表示单个字符;
+ * * 表示一层路径内的任意字符串,不可跨层级;
+ * ** 表示任意层路径;
+ *
+ * @param pattern 匹配规则
+ * @param url 需要匹配的url
+ * @return
+ */
+ public static boolean isMatch(String pattern, String url)
+ {
+ AntPathMatcher matcher = new AntPathMatcher();
+ return matcher.match(pattern, url);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T cast(Object obj)
+ {
+ return (T) obj;
+ }
+
+ /**
+ * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+ *
+ * @param num 数字对象
+ * @param size 字符串指定长度
+ * @return 返回数字的字符串格式,该字符串为指定长度。
+ */
+ public static final String padl(final Number num, final int size)
+ {
+ return padl(num.toString(), size, '0');
+ }
+
+ /**
+ * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+ *
+ * @param s 原始字符串
+ * @param size 字符串指定长度
+ * @param c 用于补齐的字符
+ * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+ */
+ public static final String padl(final String s, final int size, final char c)
+ {
+ final StringBuilder sb = new StringBuilder(size);
+ if (s != null)
+ {
+ final int len = s.length();
+ if (s.length() <= size)
+ {
+ for (int i = size - len; i > 0; i--)
+ {
+ sb.append(c);
+ }
+ sb.append(s);
+ }
+ else
+ {
+ return s.substring(len - size, len);
+ }
+ }
+ else
+ {
+ for (int i = size; i > 0; i--)
+ {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/yudao-module-archives/yudao-module-archives-api/src/main/java/cn/iocoder/yudao/module/archives/enums/ErrorCodeConstants.java b/yudao-module-archives/yudao-module-archives-api/src/main/java/cn/iocoder/yudao/module/archives/enums/ErrorCodeConstants.java
index be46a3bf..252721ba 100644
--- a/yudao-module-archives/yudao-module-archives-api/src/main/java/cn/iocoder/yudao/module/archives/enums/ErrorCodeConstants.java
+++ b/yudao-module-archives/yudao-module-archives-api/src/main/java/cn/iocoder/yudao/module/archives/enums/ErrorCodeConstants.java
@@ -5,6 +5,7 @@ public interface ErrorCodeConstants {
ErrorCode RECORD_NOT_EXISTS = new ErrorCode(200100, "归档不存在");
ErrorCode CODE_RULE_NOT_EXISTS = new ErrorCode(300100, "编码不存在");
ErrorCode TURN_OVER_NOT_EXISTS = new ErrorCode(400100, "移交管理不存在");
+ ErrorCode PACKAGES_NOT_EXISTS = new ErrorCode(500100, "装册不存在");
ErrorCode BORROW_APPLY_NOT_EXISTS = new ErrorCode(300200,"借阅申请不存在");
diff --git a/yudao-module-archives/yudao-module-archives-biz/pom.xml b/yudao-module-archives/yudao-module-archives-biz/pom.xml
index de4bd964..45900e0e 100644
--- a/yudao-module-archives/yudao-module-archives-biz/pom.xml
+++ b/yudao-module-archives/yudao-module-archives-biz/pom.xml
@@ -96,6 +96,12 @@
1.7.3-snapshot
compile
+
+ cn.iocoder.boot
+ yudao-module-infra-biz
+ 1.7.3-snapshot
+ compile
+
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/PackagesController.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/PackagesController.java
new file mode 100644
index 00000000..700e8405
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/PackagesController.java
@@ -0,0 +1,110 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+import cn.iocoder.yudao.module.archives.convert.packages.PackagesConvert;
+import cn.iocoder.yudao.module.archives.service.packages.PackagesService;
+
+@Tag(name = "管理后台 - 装册")
+@RestController
+@RequestMapping("/archives/packages")
+@Validated
+public class PackagesController {
+
+ @Resource
+ private PackagesService packagesService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建装册")
+ @PreAuthorize("@ss.hasPermission('archives:packages:create')")
+ public CommonResult createPackages(@Valid @RequestBody PackagesCreateReqVO createReqVO) {
+ return success(packagesService.createPackages(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新装册")
+ @PreAuthorize("@ss.hasPermission('archives:packages:update')")
+ public CommonResult updatePackages(@Valid @RequestBody PackagesUpdateReqVO updateReqVO) {
+ packagesService.updatePackages(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除装册")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('archives:packages:delete')")
+ public CommonResult deletePackages(@RequestParam("id") Long id) {
+ packagesService.deletePackages(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得装册")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('archives:packages:query')")
+ public CommonResult getPackages(@RequestParam("id") Long id) {
+ PackagesDO packages = packagesService.getPackages(id);
+ return success(PackagesConvert.INSTANCE.convert(packages));
+ }
+
+ @GetMapping("/getByCodeValue")
+ @Operation(summary = "获得装册")
+ @Parameter(name = "id", description = "条码值", required = true, example = "1-2-3")
+ public CommonResult getByCodeValue(@RequestParam("codeValue") String codeValue) {
+ PackagesDO packages = packagesService.getByCodeValue(codeValue);
+ return success(PackagesConvert.INSTANCE.convert(packages));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得装册列表")
+ @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+ @PreAuthorize("@ss.hasPermission('archives:packages:query')")
+ public CommonResult> getPackagesList(@RequestParam("ids") Collection ids) {
+ List list = packagesService.getPackagesList(ids);
+ return success(PackagesConvert.INSTANCE.convertList(list));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得装册分页")
+ @PreAuthorize("@ss.hasPermission('archives:packages:query')")
+ public CommonResult> getPackagesPage(@Valid PackagesPageReqVO pageVO) {
+ PageResult pageResult = packagesService.getPackagesPage(pageVO);
+ return success(PackagesConvert.INSTANCE.convertPage(pageResult));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出装册 Excel")
+ @PreAuthorize("@ss.hasPermission('archives:packages:export')")
+ @OperateLog(type = EXPORT)
+ public void exportPackagesExcel(@Valid PackagesExportReqVO exportReqVO,
+ HttpServletResponse response) throws IOException {
+ List list = packagesService.getPackagesList(exportReqVO);
+ // 导出 Excel
+ List datas = PackagesConvert.INSTANCE.convertList02(list);
+ ExcelUtils.write(response, "装册.xls", "数据", PackagesExcelVO.class, datas);
+ }
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesBaseVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesBaseVO.java
new file mode 100644
index 00000000..c0bb2252
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesBaseVO.java
@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import javax.validation.constraints.*;
+
+/**
+ * 装册 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class PackagesBaseVO {
+
+ @Schema(description = "库房id", example = "27849")
+ private Long depotId;
+
+ @Schema(description = "库房名称", example = "芋艿")
+ private String depotName;
+
+ @Schema(description = "档案柜id", example = "13350")
+ private Long cabinetId;
+
+ @Schema(description = "档案柜名称", example = "芋艿")
+ private String cabinetName;
+
+ @Schema(description = "档案id", example = "320")
+ private Long recordId;
+
+ @Schema(description = "业务实体id", example = "26066")
+ private Long companyId;
+
+ @Schema(description = "业务实体")
+ private String company;
+
+ @Schema(description = "会计年份")
+ private String year;
+
+ @Schema(description = "会计月份")
+ private String period;
+
+ @Schema(description = "负责人名称", example = "王五")
+ private String manegeName;
+
+ @Schema(description = "全宗号")
+ private String caseNum;
+
+ @Schema(description = "目录号")
+ private String catalogNum;
+
+ @Schema(description = "案卷号")
+ private String filesNum;
+
+ @Schema(description = "关联号", example = "6116")
+ private Long flowId;
+
+ @Schema(description = "状态待装册,已装册", example = "1")
+ private String status;
+
+ @Schema(description = "备注", example = "随便")
+ private String remark;
+
+ @Schema(description = "装册时间")
+ private String packageTime;
+
+ @Schema(description = "所属部门id", example = "29567")
+ private Long deptId;
+
+ @Schema(description = "用户id", example = "12826")
+ private Long userId;
+
+ @Schema(description = "所属部门", example = "张三")
+ private String deptName;
+
+ @Schema(description = "制单人")
+ private String createBy;
+
+ @Schema(description = "条码地址", example = "https://www.iocoder.cn")
+ private String codeUrl;
+
+ @Schema(description = "条码值")
+ private String codeValue;
+
+ @Schema(description = "装册数量")
+ private Integer packageNum;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesCreateReqVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesCreateReqVO.java
new file mode 100644
index 00000000..8f83920b
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesCreateReqVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 装册创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PackagesCreateReqVO extends PackagesBaseVO {
+ @Schema(description = "全宗号")
+ @NotNull(message = "全宗号不能为空")
+ private String caseNum;
+
+ @Schema(description = "目录号")
+ @NotNull(message = "目录号不能为空")
+ private String catalogNum;
+
+ @Schema(description = "案卷号")
+ @NotNull(message = "案卷号不能为空")
+ private String filesNum;
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExcelVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExcelVO.java
new file mode 100644
index 00000000..db635e03
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExcelVO.java
@@ -0,0 +1,97 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+/**
+ * 装册 Excel VO
+ *
+ * @author devin1
+ */
+@Data
+public class PackagesExcelVO {
+
+ @ExcelProperty("主键")
+ private Long id;
+
+ @ExcelProperty("库房id")
+ private Long depotId;
+
+ @ExcelProperty("库房名称")
+ private String depotName;
+
+ @ExcelProperty("档案柜id")
+ private Long cabinetId;
+
+ @ExcelProperty("档案柜名称")
+ private String cabinetName;
+
+ @ExcelProperty("档案id")
+ private Long recordId;
+
+ @ExcelProperty("业务实体id")
+ private Long companyId;
+
+ @ExcelProperty("业务实体")
+ private String company;
+
+ @ExcelProperty("会计年份")
+ private String year;
+
+ @ExcelProperty("会计月份")
+ private String period;
+
+ @ExcelProperty("负责人名称")
+ private String manegeName;
+
+ @ExcelProperty("全宗号")
+ private String caseNum;
+
+ @ExcelProperty("目录号")
+ private String catalogNum;
+
+ @ExcelProperty("案卷号")
+ private String filesNum;
+
+ @ExcelProperty("关联号")
+ private Long flowId;
+
+ @ExcelProperty("创建日期")
+ private LocalDateTime createTime;
+
+ @ExcelProperty("状态待装册,已装册")
+ private String status;
+
+ @ExcelProperty("备注")
+ private String remark;
+
+ @ExcelProperty("装册时间")
+ private String packageTime;
+
+ @ExcelProperty("所属部门id")
+ private Long deptId;
+
+ @ExcelProperty("用户id")
+ private Long userId;
+
+ @ExcelProperty("所属部门")
+ private String deptName;
+
+ @ExcelProperty("制单人")
+ private String createBy;
+
+ @ExcelProperty("条码地址")
+ private String codeUrl;
+
+ @ExcelProperty("条码值")
+ private String codeValue;
+
+ @ExcelProperty("装册数量")
+ private Integer packageNum;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExportReqVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExportReqVO.java
new file mode 100644
index 00000000..0bf8f3f5
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesExportReqVO.java
@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 装册 Excel 导出 Request VO,参数和 PackagesPageReqVO 是一致的")
+@Data
+public class PackagesExportReqVO {
+
+ @Schema(description = "库房id", example = "27849")
+ private Long depotId;
+
+ @Schema(description = "库房名称", example = "芋艿")
+ private String depotName;
+
+ @Schema(description = "档案柜id", example = "13350")
+ private Long cabinetId;
+
+ @Schema(description = "档案柜名称", example = "芋艿")
+ private String cabinetName;
+
+ @Schema(description = "档案id", example = "320")
+ private Long recordId;
+
+ @Schema(description = "业务实体id", example = "26066")
+ private Long companyId;
+
+ @Schema(description = "业务实体")
+ private String company;
+
+ @Schema(description = "会计年份")
+ private String year;
+
+ @Schema(description = "会计月份")
+ private String period;
+
+ @Schema(description = "负责人名称", example = "王五")
+ private String manegeName;
+
+ @Schema(description = "全宗号")
+ private String caseNum;
+
+ @Schema(description = "目录号")
+ private String catalogNum;
+
+ @Schema(description = "案卷号")
+ private String filesNum;
+
+ @Schema(description = "关联号", example = "6116")
+ private Long flowId;
+
+ @Schema(description = "创建日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+ @Schema(description = "状态待装册,已装册", example = "1")
+ private String status;
+
+ @Schema(description = "备注", example = "随便")
+ private String remark;
+
+ @Schema(description = "装册时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] packageTime;
+
+ @Schema(description = "所属部门id", example = "29567")
+ private Long deptId;
+
+ @Schema(description = "用户id", example = "12826")
+ private Long userId;
+
+ @Schema(description = "所属部门", example = "张三")
+ private String deptName;
+
+ @Schema(description = "制单人")
+ private String createBy;
+
+ @Schema(description = "条码地址", example = "https://www.iocoder.cn")
+ private String codeUrl;
+
+ @Schema(description = "条码值")
+ private String codeValue;
+
+ @Schema(description = "装册数量")
+ private Integer packageNum;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesPageReqVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesPageReqVO.java
new file mode 100644
index 00000000..d0b64595
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesPageReqVO.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 装册分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PackagesPageReqVO extends PageParam {
+
+ @Schema(description = "库房id", example = "27849")
+ private Long depotId;
+
+ @Schema(description = "库房名称", example = "芋艿")
+ private String depotName;
+
+ @Schema(description = "档案柜id", example = "13350")
+ private Long cabinetId;
+
+ @Schema(description = "档案柜名称", example = "芋艿")
+ private String cabinetName;
+
+ @Schema(description = "档案id", example = "320")
+ private Long recordId;
+
+ @Schema(description = "业务实体id", example = "26066")
+ private Long companyId;
+
+ @Schema(description = "业务实体")
+ private String company;
+
+ @Schema(description = "会计年份")
+ private String year;
+
+ @Schema(description = "会计月份")
+ private String period;
+
+ @Schema(description = "负责人名称", example = "王五")
+ private String manegeName;
+
+ @Schema(description = "全宗号")
+ private String caseNum;
+
+ @Schema(description = "目录号")
+ private String catalogNum;
+
+ @Schema(description = "案卷号")
+ private String filesNum;
+
+ @Schema(description = "关联号", example = "6116")
+ private Long flowId;
+
+ @Schema(description = "创建日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+ @Schema(description = "状态待装册,已装册", example = "1")
+ private String status;
+
+ @Schema(description = "备注", example = "随便")
+ private String remark;
+
+ @Schema(description = "装册时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] packageTime;
+
+ @Schema(description = "所属部门id", example = "29567")
+ private Long deptId;
+
+ @Schema(description = "用户id", example = "12826")
+ private Long userId;
+
+ @Schema(description = "所属部门", example = "张三")
+ private String deptName;
+
+ @Schema(description = "制单人")
+ private String createBy;
+
+ @Schema(description = "条码地址", example = "https://www.iocoder.cn")
+ private String codeUrl;
+
+ @Schema(description = "条码值")
+ private String codeValue;
+
+ @Schema(description = "装册数量")
+ private Integer packageNum;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesRespVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesRespVO.java
new file mode 100644
index 00000000..0e724d63
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesRespVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 装册 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PackagesRespVO extends PackagesBaseVO {
+
+ @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "18922")
+ private Long id;
+
+ @Schema(description = "创建日期")
+ private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesUpdateReqVO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesUpdateReqVO.java
new file mode 100644
index 00000000..aa71acc0
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/controller/admin/packages/vo/PackagesUpdateReqVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.archives.controller.admin.packages.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 装册更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PackagesUpdateReqVO extends PackagesBaseVO {
+
+ @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "18922")
+ @NotNull(message = "主键不能为空")
+ private Long id;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/convert/packages/PackagesConvert.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/convert/packages/PackagesConvert.java
new file mode 100644
index 00000000..4df707c6
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/convert/packages/PackagesConvert.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.archives.convert.packages;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+
+/**
+ * 装册 Convert
+ *
+ * @author devin1
+ */
+@Mapper
+public interface PackagesConvert {
+
+ PackagesConvert INSTANCE = Mappers.getMapper(PackagesConvert.class);
+
+ PackagesDO convert(PackagesCreateReqVO bean);
+
+ PackagesDO convert(PackagesUpdateReqVO bean);
+
+ PackagesRespVO convert(PackagesDO bean);
+
+ List convertList(List list);
+
+ PageResult convertPage(PageResult page);
+
+ List convertList02(List list);
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/dataobject/packages/PackagesDO.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/dataobject/packages/PackagesDO.java
new file mode 100644
index 00000000..2652d476
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/dataobject/packages/PackagesDO.java
@@ -0,0 +1,143 @@
+package cn.iocoder.yudao.module.archives.dal.dataobject.packages;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 装册 DO
+ *
+ * @author devin1
+ */
+@TableName("archives_package")
+@KeySequence("archives_package_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PackagesDO extends BaseDO {
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 库房id
+ */
+ private Long depotId;
+ /**
+ * 库房名称
+ */
+ private String depotName;
+ /**
+ * 档案柜id
+ */
+ private Long cabinetId;
+ /**
+ * 档案柜名称
+ */
+ private String cabinetName;
+ /**
+ * 档案id
+ */
+ private Long recordId;
+ /**
+ * 业务实体id
+ */
+ private Long companyId;
+ /**
+ * 业务实体
+ */
+ private String company;
+ /**
+ * 会计年份
+ */
+ private String year;
+ /**
+ * 会计月份
+ */
+ private String period;
+ /**
+ * 负责人名称
+ */
+ private String manegeName;
+ /**
+ * 全宗号
+ */
+ private String caseNum;
+ /**
+ * 目录号
+ */
+ private String catalogNum;
+ /**
+ * 案卷号
+ */
+ private String filesNum;
+ /**
+ * 关联号
+ */
+ private Long flowId;
+ /**
+ * 状态待装册,已装册
+ */
+ private String status;
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 装册时间
+ */
+ private String packageTime;
+ /**
+ * 所属部门id
+ */
+ private Long deptId;
+ /**
+ * 用户id
+ */
+ private Long userId;
+ /**
+ * 所属部门
+ */
+ private String deptName;
+ /**
+ * 预留字段1
+ */
+ private String attr1;
+ /**
+ * 预留字段2
+ */
+ private String attr2;
+ /**
+ * 预留字段3
+ */
+ private Integer attr3;
+ /**
+ * 预留字段4
+ */
+ private Integer attr4;
+ /**
+ * 制单人
+ */
+ private String createBy;
+ /**
+ * 条码地址
+ */
+ private String codeUrl;
+ /**
+ * 条码值
+ */
+ private String codeValue;
+ /**
+ * 装册数量
+ */
+ private Integer packageNum;
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/mysql/packages/PackagesMapper.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/mysql/packages/PackagesMapper.java
new file mode 100644
index 00000000..beca8589
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/dal/mysql/packages/PackagesMapper.java
@@ -0,0 +1,85 @@
+package cn.iocoder.yudao.module.archives.dal.mysql.packages;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * 装册 Mapper
+ *
+ * @author devin1
+ */
+@Mapper
+public interface PackagesMapper extends BaseMapperX {
+
+ default PageResult selectPage(PackagesPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(PackagesDO::getDepotId, reqVO.getDepotId())
+ .likeIfPresent(PackagesDO::getDepotName, reqVO.getDepotName())
+ .eqIfPresent(PackagesDO::getCabinetId, reqVO.getCabinetId())
+ .likeIfPresent(PackagesDO::getCabinetName, reqVO.getCabinetName())
+ .eqIfPresent(PackagesDO::getRecordId, reqVO.getRecordId())
+ .eqIfPresent(PackagesDO::getCompanyId, reqVO.getCompanyId())
+ .eqIfPresent(PackagesDO::getCompany, reqVO.getCompany())
+ .eqIfPresent(PackagesDO::getYear, reqVO.getYear())
+ .eqIfPresent(PackagesDO::getPeriod, reqVO.getPeriod())
+ .likeIfPresent(PackagesDO::getManegeName, reqVO.getManegeName())
+ .eqIfPresent(PackagesDO::getCaseNum, reqVO.getCaseNum())
+ .eqIfPresent(PackagesDO::getCatalogNum, reqVO.getCatalogNum())
+ .eqIfPresent(PackagesDO::getFilesNum, reqVO.getFilesNum())
+ .eqIfPresent(PackagesDO::getFlowId, reqVO.getFlowId())
+ .betweenIfPresent(PackagesDO::getCreateTime, reqVO.getCreateTime())
+ .eqIfPresent(PackagesDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(PackagesDO::getRemark, reqVO.getRemark())
+ .betweenIfPresent(PackagesDO::getPackageTime, reqVO.getPackageTime())
+ .eqIfPresent(PackagesDO::getDeptId, reqVO.getDeptId())
+ .eqIfPresent(PackagesDO::getUserId, reqVO.getUserId())
+ .likeIfPresent(PackagesDO::getDeptName, reqVO.getDeptName())
+ .eqIfPresent(PackagesDO::getCreateBy, reqVO.getCreateBy())
+ .eqIfPresent(PackagesDO::getCodeUrl, reqVO.getCodeUrl())
+ .eqIfPresent(PackagesDO::getCodeValue, reqVO.getCodeValue())
+ .eqIfPresent(PackagesDO::getPackageNum, reqVO.getPackageNum())
+ .orderByDesc(PackagesDO::getId));
+ }
+
+ default List selectList(PackagesExportReqVO reqVO) {
+ return selectList(new LambdaQueryWrapperX()
+ .eqIfPresent(PackagesDO::getDepotId, reqVO.getDepotId())
+ .likeIfPresent(PackagesDO::getDepotName, reqVO.getDepotName())
+ .eqIfPresent(PackagesDO::getCabinetId, reqVO.getCabinetId())
+ .likeIfPresent(PackagesDO::getCabinetName, reqVO.getCabinetName())
+ .eqIfPresent(PackagesDO::getRecordId, reqVO.getRecordId())
+ .eqIfPresent(PackagesDO::getCompanyId, reqVO.getCompanyId())
+ .eqIfPresent(PackagesDO::getCompany, reqVO.getCompany())
+ .eqIfPresent(PackagesDO::getYear, reqVO.getYear())
+ .eqIfPresent(PackagesDO::getPeriod, reqVO.getPeriod())
+ .likeIfPresent(PackagesDO::getManegeName, reqVO.getManegeName())
+ .eqIfPresent(PackagesDO::getCaseNum, reqVO.getCaseNum())
+ .eqIfPresent(PackagesDO::getCatalogNum, reqVO.getCatalogNum())
+ .eqIfPresent(PackagesDO::getFilesNum, reqVO.getFilesNum())
+ .eqIfPresent(PackagesDO::getFlowId, reqVO.getFlowId())
+ .betweenIfPresent(PackagesDO::getCreateTime, reqVO.getCreateTime())
+ .eqIfPresent(PackagesDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(PackagesDO::getRemark, reqVO.getRemark())
+ .betweenIfPresent(PackagesDO::getPackageTime, reqVO.getPackageTime())
+ .eqIfPresent(PackagesDO::getDeptId, reqVO.getDeptId())
+ .eqIfPresent(PackagesDO::getUserId, reqVO.getUserId())
+ .likeIfPresent(PackagesDO::getDeptName, reqVO.getDeptName())
+ .eqIfPresent(PackagesDO::getCreateBy, reqVO.getCreateBy())
+ .eqIfPresent(PackagesDO::getCodeUrl, reqVO.getCodeUrl())
+ .eqIfPresent(PackagesDO::getCodeValue, reqVO.getCodeValue())
+ .eqIfPresent(PackagesDO::getPackageNum, reqVO.getPackageNum())
+ .orderByDesc(PackagesDO::getId));
+ }
+
+ @Select("SELECT id, depot_id, depot_name, cabinet_id, cabinet_name, record_id, company_id, company, year, period, manege_name, case_num, catalog_num, files_num, flow_id, status, remark, package_time, dept_id, user_id, dept_name, attr1, attr2, attr3, attr4, create_by, code_url, code_value, package_num "
+ +" FROM archives_package WHERE code_value = #{codeValue} ORDER BY create_time DESC LIMIT 1")
+ PackagesDO getByCodeValue(String codeValue);
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesService.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesService.java
new file mode 100644
index 00000000..be6daf99
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesService.java
@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.archives.service.packages;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 装册 Service 接口
+ *
+ * @author devin1
+ */
+public interface PackagesService {
+
+ /**
+ * 创建装册
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createPackages(@Valid PackagesCreateReqVO createReqVO);
+
+ /**
+ * 更新装册
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updatePackages(@Valid PackagesUpdateReqVO updateReqVO);
+
+ /**
+ * 删除装册
+ *
+ * @param id 编号
+ */
+ void deletePackages(Long id);
+
+ /**
+ * 获得装册
+ *
+ * @param id 编号
+ * @return 装册
+ */
+ PackagesDO getPackages(Long id);
+
+ /**
+ * 获得装册列表
+ *
+ * @param ids 编号
+ * @return 装册列表
+ */
+ List getPackagesList(Collection ids);
+
+ /**
+ * 获得装册分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 装册分页
+ */
+ PageResult getPackagesPage(PackagesPageReqVO pageReqVO);
+
+ /**
+ * 根据条码值获得装册详情
+ *
+ * @param codeValue 条码值
+ * @return 装册分页
+ */
+ PackagesDO getByCodeValue(String codeValue);
+
+ /**
+ * 获得装册列表, 用于 Excel 导出
+ *
+ * @param exportReqVO 查询条件
+ * @return 装册列表
+ */
+ List getPackagesList(PackagesExportReqVO exportReqVO);
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImpl.java b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImpl.java
new file mode 100644
index 00000000..5693c4e4
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImpl.java
@@ -0,0 +1,124 @@
+package cn.iocoder.yudao.module.archives.service.packages;
+
+import cn.hutool.core.io.IoUtil;
+import cn.iocoder.yudao.framework.common.util.FileUtils;
+import cn.iocoder.yudao.framework.common.util.barcode.BarcodeUtil;
+import cn.iocoder.yudao.module.infra.service.file.FileService;
+import me.zhyd.oauth.log.Log;
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import cn.iocoder.yudao.module.archives.convert.packages.PackagesConvert;
+import cn.iocoder.yudao.module.archives.dal.mysql.packages.PackagesMapper;
+import org.springframework.web.multipart.MultipartFile;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.archives.enums.ErrorCodeConstants.*;
+
+/**
+ * 装册 Service 实现类
+ *
+ * @author devin1
+ */
+@Service
+@Validated
+public class PackagesServiceImpl implements PackagesService {
+
+ @Resource
+ private PackagesMapper packagesMapper;
+ @Resource
+ private FileService fileService;
+
+
+ @Override
+ public Long createPackages(PackagesCreateReqVO createReqVO) {
+ // 插入
+ PackagesDO packages = PackagesConvert.INSTANCE.convert(createReqVO);
+ File buf = BarcodeUtil.generateBarCode(packages.getCaseNum()+"-"+packages.getCatalogNum()+"-"+packages.getFilesNum(), "QR_CODE",
+ "./tmp/Packages/" + packages.getCaseNum()+"-"+packages.getCatalogNum()+"-"+packages.getFilesNum() + ".png");
+ MultipartFile file = FileUtils.getMultipartFile(buf);
+ String url="";
+ try {
+ url = fileService.createFile(null, null, IoUtil.readBytes(file.getInputStream()));
+ } catch (IOException e) {
+ Log.error(e.toString());
+ }
+ packages.setCodeUrl(url);
+ packages.setCodeValue(packages.getCaseNum()+"-"+packages.getCatalogNum()+"-"+packages.getFilesNum());
+ packagesMapper.insert(packages);
+ // 返回
+ return packages.getId();
+ }
+
+ @Override
+ public void updatePackages(PackagesUpdateReqVO updateReqVO) {
+ // 校验存在
+ validatePackagesExists(updateReqVO.getId());
+ // 更新
+ PackagesDO updateObj = PackagesConvert.INSTANCE.convert(updateReqVO);
+ PackagesDO packagesDO = packagesMapper.selectById(updateReqVO.getId());
+ boolean b = packagesDO.getCaseNum().equals(updateObj.getCaseNum()) && packagesDO.getCatalogNum().equals(updateObj.getCatalogNum()) && packagesDO.getFilesNum().equals(updateObj.getFilesNum());
+ if (!b){
+ File buf = BarcodeUtil.generateBarCode(updateObj.getCaseNum()+"-"+updateObj.getCatalogNum()+"-"+updateObj.getFilesNum(), "QR_CODE",
+ "./tmp/Packages/" + updateObj.getCaseNum()+"-"+updateObj.getCatalogNum()+"-"+updateObj.getFilesNum() + ".png");
+ MultipartFile file = FileUtils.getMultipartFile(buf);
+ String url="";
+ try {
+ url = fileService.createFile(null, null, IoUtil.readBytes(file.getInputStream()));
+ } catch (IOException e) {
+ Log.error(e.toString());
+ }
+ updateObj.setCodeUrl(url);
+ updateObj.setCodeValue(updateObj.getCaseNum()+"-"+updateObj.getCatalogNum()+"-"+updateObj.getFilesNum());
+ }
+ packagesMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deletePackages(Long id) {
+ // 校验存在
+ validatePackagesExists(id);
+ // 删除
+ packagesMapper.deleteById(id);
+ }
+
+ private void validatePackagesExists(Long id) {
+ if (packagesMapper.selectById(id) == null) {
+ throw exception(PACKAGES_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public PackagesDO getPackages(Long id) {
+ return packagesMapper.selectById(id);
+ }
+
+ @Override
+ public List getPackagesList(Collection ids) {
+ return packagesMapper.selectBatchIds(ids);
+ }
+
+ @Override
+ public PackagesDO getByCodeValue(String codeValue) {
+ return packagesMapper.getByCodeValue(codeValue);
+ }
+
+ @Override
+ public PageResult getPackagesPage(PackagesPageReqVO pageReqVO) {
+ return packagesMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public List getPackagesList(PackagesExportReqVO exportReqVO) {
+ return packagesMapper.selectList(exportReqVO);
+ }
+
+}
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/main/resources/mapper/packages/PackagesMapper.xml b/yudao-module-archives/yudao-module-archives-biz/src/main/resources/mapper/packages/PackagesMapper.xml
new file mode 100644
index 00000000..0b303d8c
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/main/resources/mapper/packages/PackagesMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/yudao-module-archives/yudao-module-archives-biz/src/test/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImplTest.java b/yudao-module-archives/yudao-module-archives-biz/src/test/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImplTest.java
new file mode 100644
index 00000000..a95c57db
--- /dev/null
+++ b/yudao-module-archives/yudao-module-archives-biz/src/test/java/cn/iocoder/yudao/module/archives/service/packages/PackagesServiceImplTest.java
@@ -0,0 +1,343 @@
+package cn.iocoder.yudao.module.archives.service.packages;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.archives.controller.admin.packages.vo.*;
+import cn.iocoder.yudao.module.archives.dal.dataobject.packages.PackagesDO;
+import cn.iocoder.yudao.module.archives.dal.mysql.packages.PackagesMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+import static cn.iocoder.yudao.module.archives.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link PackagesServiceImpl} 的单元测试类
+ *
+ * @author devin1
+ */
+@Import(PackagesServiceImpl.class)
+public class PackagesServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private PackagesServiceImpl packagesService;
+
+ @Resource
+ private PackagesMapper packagesMapper;
+
+ @Test
+ public void testCreatePackages_success() {
+ // 准备参数
+ PackagesCreateReqVO reqVO = randomPojo(PackagesCreateReqVO.class);
+
+ // 调用
+ Long packagesId = packagesService.createPackages(reqVO);
+ // 断言
+ assertNotNull(packagesId);
+ // 校验记录的属性是否正确
+ PackagesDO packages = packagesMapper.selectById(packagesId);
+ assertPojoEquals(reqVO, packages);
+ }
+
+ @Test
+ public void testUpdatePackages_success() {
+ // mock 数据
+ PackagesDO dbPackages = randomPojo(PackagesDO.class);
+ packagesMapper.insert(dbPackages);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ PackagesUpdateReqVO reqVO = randomPojo(PackagesUpdateReqVO.class, o -> {
+ o.setId(dbPackages.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ packagesService.updatePackages(reqVO);
+ // 校验是否更新正确
+ PackagesDO packages = packagesMapper.selectById(reqVO.getId()); // 获取最新的
+ assertPojoEquals(reqVO, packages);
+ }
+
+ @Test
+ public void testUpdatePackages_notExists() {
+ // 准备参数
+ PackagesUpdateReqVO reqVO = randomPojo(PackagesUpdateReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> packagesService.updatePackages(reqVO), PACKAGES_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeletePackages_success() {
+ // mock 数据
+ PackagesDO dbPackages = randomPojo(PackagesDO.class);
+ packagesMapper.insert(dbPackages);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbPackages.getId();
+
+ // 调用
+ packagesService.deletePackages(id);
+ // 校验数据不存在了
+ assertNull(packagesMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeletePackages_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> packagesService.deletePackages(id), PACKAGES_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetPackagesPage() {
+ // mock 数据
+ PackagesDO dbPackages = randomPojo(PackagesDO.class, o -> { // 等会查询到
+ o.setDepotId(null);
+ o.setDepotName(null);
+ o.setCabinetId(null);
+ o.setCabinetName(null);
+ o.setRecordId(null);
+ o.setCompanyId(null);
+ o.setCompany(null);
+ o.setYear(null);
+ o.setPeriod(null);
+ o.setManegeName(null);
+ o.setCaseNum(null);
+ o.setCatalogNum(null);
+ o.setFilesNum(null);
+ o.setFlowId(null);
+ o.setCreateTime(null);
+ o.setStatus(null);
+ o.setRemark(null);
+ o.setPackageTime(null);
+ o.setDeptId(null);
+ o.setUserId(null);
+ o.setDeptName(null);
+ o.setCreateBy(null);
+ o.setCodeUrl(null);
+ o.setCodeValue(null);
+ o.setPackageNum(null);
+ });
+ packagesMapper.insert(dbPackages);
+ // 测试 depotId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDepotId(null)));
+ // 测试 depotName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDepotName(null)));
+ // 测试 cabinetId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCabinetId(null)));
+ // 测试 cabinetName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCabinetName(null)));
+ // 测试 recordId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setRecordId(null)));
+ // 测试 companyId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCompanyId(null)));
+ // 测试 company 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCompany(null)));
+ // 测试 year 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setYear(null)));
+ // 测试 period 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPeriod(null)));
+ // 测试 manegeName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setManegeName(null)));
+ // 测试 caseNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCaseNum(null)));
+ // 测试 catalogNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCatalogNum(null)));
+ // 测试 filesNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setFilesNum(null)));
+ // 测试 flowId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setFlowId(null)));
+ // 测试 createTime 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCreateTime(null)));
+ // 测试 status 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setStatus(null)));
+ // 测试 remark 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setRemark(null)));
+ // 测试 packageTime 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPackageTime(null)));
+ // 测试 deptId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDeptId(null)));
+ // 测试 userId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setUserId(null)));
+ // 测试 deptName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDeptName(null)));
+ // 测试 createBy 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCreateBy(null)));
+ // 测试 codeUrl 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCodeUrl(null)));
+ // 测试 codeValue 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCodeValue(null)));
+ // 测试 packageNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPackageNum(null)));
+ // 准备参数
+ PackagesPageReqVO reqVO = new PackagesPageReqVO();
+ reqVO.setDepotId(null);
+ reqVO.setDepotName(null);
+ reqVO.setCabinetId(null);
+ reqVO.setCabinetName(null);
+ reqVO.setRecordId(null);
+ reqVO.setCompanyId(null);
+ reqVO.setCompany(null);
+ reqVO.setYear(null);
+ reqVO.setPeriod(null);
+ reqVO.setManegeName(null);
+ reqVO.setCaseNum(null);
+ reqVO.setCatalogNum(null);
+ reqVO.setFilesNum(null);
+ reqVO.setFlowId(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+ reqVO.setStatus(null);
+ reqVO.setRemark(null);
+ reqVO.setPackageTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+ reqVO.setDeptId(null);
+ reqVO.setUserId(null);
+ reqVO.setDeptName(null);
+ reqVO.setCreateBy(null);
+ reqVO.setCodeUrl(null);
+ reqVO.setCodeValue(null);
+ reqVO.setPackageNum(null);
+
+ // 调用
+ PageResult pageResult = packagesService.getPackagesPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbPackages, pageResult.getList().get(0));
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetPackagesList() {
+ // mock 数据
+ PackagesDO dbPackages = randomPojo(PackagesDO.class, o -> { // 等会查询到
+ o.setDepotId(null);
+ o.setDepotName(null);
+ o.setCabinetId(null);
+ o.setCabinetName(null);
+ o.setRecordId(null);
+ o.setCompanyId(null);
+ o.setCompany(null);
+ o.setYear(null);
+ o.setPeriod(null);
+ o.setManegeName(null);
+ o.setCaseNum(null);
+ o.setCatalogNum(null);
+ o.setFilesNum(null);
+ o.setFlowId(null);
+ o.setCreateTime(null);
+ o.setStatus(null);
+ o.setRemark(null);
+ o.setPackageTime(null);
+ o.setDeptId(null);
+ o.setUserId(null);
+ o.setDeptName(null);
+ o.setCreateBy(null);
+ o.setCodeUrl(null);
+ o.setCodeValue(null);
+ o.setPackageNum(null);
+ });
+ packagesMapper.insert(dbPackages);
+ // 测试 depotId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDepotId(null)));
+ // 测试 depotName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDepotName(null)));
+ // 测试 cabinetId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCabinetId(null)));
+ // 测试 cabinetName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCabinetName(null)));
+ // 测试 recordId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setRecordId(null)));
+ // 测试 companyId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCompanyId(null)));
+ // 测试 company 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCompany(null)));
+ // 测试 year 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setYear(null)));
+ // 测试 period 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPeriod(null)));
+ // 测试 manegeName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setManegeName(null)));
+ // 测试 caseNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCaseNum(null)));
+ // 测试 catalogNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCatalogNum(null)));
+ // 测试 filesNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setFilesNum(null)));
+ // 测试 flowId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setFlowId(null)));
+ // 测试 createTime 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCreateTime(null)));
+ // 测试 status 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setStatus(null)));
+ // 测试 remark 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setRemark(null)));
+ // 测试 packageTime 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPackageTime(null)));
+ // 测试 deptId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDeptId(null)));
+ // 测试 userId 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setUserId(null)));
+ // 测试 deptName 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setDeptName(null)));
+ // 测试 createBy 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCreateBy(null)));
+ // 测试 codeUrl 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCodeUrl(null)));
+ // 测试 codeValue 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setCodeValue(null)));
+ // 测试 packageNum 不匹配
+ packagesMapper.insert(cloneIgnoreId(dbPackages, o -> o.setPackageNum(null)));
+ // 准备参数
+ PackagesExportReqVO reqVO = new PackagesExportReqVO();
+ reqVO.setDepotId(null);
+ reqVO.setDepotName(null);
+ reqVO.setCabinetId(null);
+ reqVO.setCabinetName(null);
+ reqVO.setRecordId(null);
+ reqVO.setCompanyId(null);
+ reqVO.setCompany(null);
+ reqVO.setYear(null);
+ reqVO.setPeriod(null);
+ reqVO.setManegeName(null);
+ reqVO.setCaseNum(null);
+ reqVO.setCatalogNum(null);
+ reqVO.setFilesNum(null);
+ reqVO.setFlowId(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+ reqVO.setStatus(null);
+ reqVO.setRemark(null);
+ reqVO.setPackageTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+ reqVO.setDeptId(null);
+ reqVO.setUserId(null);
+ reqVO.setDeptName(null);
+ reqVO.setCreateBy(null);
+ reqVO.setCodeUrl(null);
+ reqVO.setCodeValue(null);
+ reqVO.setPackageNum(null);
+
+ // 调用
+ List list = packagesService.getPackagesList(reqVO);
+ // 断言
+ assertEquals(1, list.size());
+ assertPojoEquals(dbPackages, list.get(0));
+ }
+
+}
diff --git a/yudao-module-setting/yudao-module-setting-biz/src/main/java/cn/iocoder/yudao/module/setting/service/organization/OrganizationServiceImpl.java b/yudao-module-setting/yudao-module-setting-biz/src/main/java/cn/iocoder/yudao/module/setting/service/organization/OrganizationServiceImpl.java
index 2b3d6868..7ca7ffa2 100644
--- a/yudao-module-setting/yudao-module-setting-biz/src/main/java/cn/iocoder/yudao/module/setting/service/organization/OrganizationServiceImpl.java
+++ b/yudao-module-setting/yudao-module-setting-biz/src/main/java/cn/iocoder/yudao/module/setting/service/organization/OrganizationServiceImpl.java
@@ -103,55 +103,50 @@ public class OrganizationServiceImpl implements OrganizationService {
}
public List merge(List list) {
- List result = new ArrayList<>();
- Map map = new HashMap<>();
+ Map organizationMap = new HashMap<>();
for (OrganizationDepotVO vo : list) {
Long id = vo.getOrganizationId();
- if (!map.containsKey(id)) {
- map.put(id, vo);
- result.add(vo);
+ if (!organizationMap.containsKey(id)) {
+ OrganizationDepotVO newOrganization = new OrganizationDepotVO();
+ newOrganization.setOrganizationId(id);
+ newOrganization.setOrganizationName(vo.getOrganizationName());
+ newOrganization.setOrganizationCode(vo.getOrganizationCode());
+ if (vo.getChildren()!=null) {
+ newOrganization.setChildren(vo.getChildren());
+ }
+ organizationMap.put(id, newOrganization);
} else {
- OrganizationDepotVO existing = map.get(id);
- existing.getChildren().addAll(vo.getChildren());
+ OrganizationDepotVO existing = organizationMap.get(id);
+ if (vo.getChildren()!=null) {
+ existing.setChildren(vo.getChildren());
+ }
}
}
- // 合并 children 中相同 depotId 的数据
+ List result = new ArrayList<>(organizationMap.values());
+
for (OrganizationDepotVO vo : result) {
- mergeChildren(vo.getChildren());
+ if (vo.getChildren()!=null) {
+ mergeChildren(vo.getChildren());
+ }
}
+
return result;
}
private void mergeChildren(List children) {
Map map = new HashMap<>();
- List newChildren = new ArrayList<>();
for (DepotVo vo : children) {
Long id = vo.getDepotId();
if (!map.containsKey(id)) {
map.put(id, vo);
- newChildren.add(vo);
- }else {
+ } else {
DepotVo existing = map.get(id);
- existing.getChildren().addAll(vo.getChildren());
+ if (vo.getChildren()!=null) {
+ existing.setChildren(vo.getChildren());
+ }
}
}
children.clear();
- children.addAll(newChildren);
- for (DepotVo depotVo : children){
- removeDuplicate(depotVo.getChildren());
- }
-
- }
-
- public List removeDuplicate(List list) {
- Set set = new HashSet<>();
- Iterator it = list.iterator();
- while (it.hasNext()) {
- DepotCabinetVo vo = it.next();
- if (!set.add(vo.getCabinetId())) {
- it.remove();
- }
- }
- return list;
+ children.addAll(map.values());
}